From 90d03cb45e2e82456ec8c2758759fe5ec09f00b2 Mon Sep 17 00:00:00 2001 From: zimoch Date: Mon, 29 Nov 2010 10:38:06 +0000 Subject: [PATCH] Initial revision --- LICENSE | 84 + Makefile | 29 + README | 24 + config/CONFIG | 91 + config/CONFIG.Host.Borland | 270 + config/CONFIG.Host.Darwin | 58 + config/CONFIG.Host.Linux | 55 + config/CONFIG.Host.UnixCommon | 89 + config/CONFIG.Host.WIN32 | 283 + config/CONFIG.Host.cygwin-x86 | 13 + config/CONFIG.Host.cygwin32 | 63 + config/CONFIG.Host.darwin-ppc | 12 + config/CONFIG.Host.darwin-ppcx86 | 14 + config/CONFIG.Host.darwin-x86 | 12 + config/CONFIG.Host.freebsd | 35 + config/CONFIG.Host.freebsd-x86 | 15 + config/CONFIG.Host.freebsd-x86_64 | 15 + config/CONFIG.Host.hp700 | 74 + config/CONFIG.Host.hpux-parisc | 13 + config/CONFIG.Host.hpux-parisc-gnu | 49 + config/CONFIG.Host.linux-ppc | 24 + config/CONFIG.Host.linux-x86 | 13 + config/CONFIG.Host.linux-x86-debug | 12 + config/CONFIG.Host.linux-x86_64 | 17 + config/CONFIG.Host.linux-x86_64-debug | 13 + config/CONFIG.Host.sgi | 59 + config/CONFIG.Host.solaris | 95 + config/CONFIG.Host.solaris-sparc | 13 + config/CONFIG.Host.solaris-sparc-debug | 14 + config/CONFIG.Host.solaris-sparc-gnu | 14 + config/CONFIG.Host.solaris-sparc-staticlib | 14 + config/CONFIG.Host.solaris-sparc64 | 18 + config/CONFIG.Host.solaris-sparc64-gnu | 18 + config/CONFIG.Host.solaris-x86 | 19 + config/CONFIG.Host.solaris-x86-gnu | 19 + config/CONFIG.Host.solarisGnu | 22 + config/CONFIG.Host.sun4 | 98 + config/CONFIG.Host.sun4-sparc | 13 + config/CONFIG.Host.win32-x86 | 14 + config/CONFIG.Host.win32-x86-borland | 13 + config/CONFIG.Host.win32-x86-cygwin | 62 + config/CONFIG.Host.win32-x86-debug | 12 + config/CONFIG.Host.win32-x86-mingw | 58 + config/CONFIG.Vx | 149 + config/CONFIG.Vx.frc40 | 24 + config/CONFIG.Vx.frc5ce | 30 + config/CONFIG.Vx.hkbaja47 | 29 + config/CONFIG.Vx.hkbaja60 | 25 + config/CONFIG.Vx.hkv2f | 25 + config/CONFIG.Vx.mv147 | 25 + config/CONFIG.Vx.mv1604 | 24 + config/CONFIG.Vx.mv162 | 25 + config/CONFIG.Vx.mv162lc | 24 + config/CONFIG.Vx.mv167 | 25 + config/CONFIG.Vx.mv177 | 25 + config/CONFIG.Vx.mv2700 | 25 + config/CONFIG.Vx.niCpu030 | 25 + config/CONFIG.Vx.pc486 | 26 + config/CONFIG.Vx.pcPentium | 26 + config/CONFIG.Vx.ppc603 | 25 + config/CONFIG.Vx.ppc603_long | 16 + config/CONFIG.Vx.ppc604 | 26 + config/CONFIG.Vx.ppc604_long | 17 + config/CONFIG.Vx.sbs_pc6 | 16 + config/CONFIG.Vx.simpc | 30 + config/CONFIG.Vx.vxWorks-486 | 13 + config/CONFIG.Vx.vxWorks-68040 | 13 + config/CONFIG.Vx.vxWorks-68040lc | 13 + config/CONFIG.Vx.vxWorks-68060 | 13 + config/CONFIG.Vx.vxWorks-ppc603 | 13 + config/CONFIG.Vx.vxWorks-ppc603_long | 13 + config/CONFIG.Vx.vxWorks-ppc604 | 13 + config/CONFIG.Vx.vxWorks-ppc604_long | 13 + config/CONFIG.Vx.vxipc | 26 + config/CONFIG_BASE | 85 + config/CONFIG_BASE_VERSION | 15 + config/CONFIG_COMMON | 258 + config/CONFIG_COMPAT | 49 + config/CONFIG_ENV | 137 + config/CONFIG_HOST_ARCH.Borland | 32 + config/CONFIG_HOST_ARCH.Darwin | 14 + config/CONFIG_HOST_ARCH.Linux | 17 + config/CONFIG_HOST_ARCH.UnixCommon | 27 + config/CONFIG_HOST_ARCH.WIN32 | 31 + config/CONFIG_HOST_ARCH.cygwin-x86 | 23 + config/CONFIG_HOST_ARCH.cygwin32 | 23 + config/CONFIG_HOST_ARCH.darwin-ppc | 12 + config/CONFIG_HOST_ARCH.darwin-ppcx86 | 12 + config/CONFIG_HOST_ARCH.darwin-x86 | 12 + config/CONFIG_HOST_ARCH.freebsd-x86_64 | 11 + config/CONFIG_HOST_ARCH.hp700 | 16 + config/CONFIG_HOST_ARCH.hpux-parisc | 13 + config/CONFIG_HOST_ARCH.hpux-parisc-gnu | 13 + config/CONFIG_HOST_ARCH.linux-ppc | 14 + config/CONFIG_HOST_ARCH.linux-x86 | 13 + config/CONFIG_HOST_ARCH.linux-x86-debug | 15 + config/CONFIG_HOST_ARCH.linux-x86_64 | 15 + config/CONFIG_HOST_ARCH.linux-x86_64-debug | 15 + config/CONFIG_HOST_ARCH.sgi | 17 + config/CONFIG_HOST_ARCH.solaris | 16 + config/CONFIG_HOST_ARCH.solaris-sparc | 13 + config/CONFIG_HOST_ARCH.solaris-sparc-debug | 14 + config/CONFIG_HOST_ARCH.solaris-sparc-gnu | 13 + .../CONFIG_HOST_ARCH.solaris-sparc-staticlib | 13 + config/CONFIG_HOST_ARCH.solaris-sparc64 | 13 + config/CONFIG_HOST_ARCH.solaris-sparc64-gnu | 13 + config/CONFIG_HOST_ARCH.solaris-x86 | 16 + config/CONFIG_HOST_ARCH.solaris-x86-gnu | 4 + config/CONFIG_HOST_ARCH.solarisGnu | 8 + config/CONFIG_HOST_ARCH.sun4 | 22 + config/CONFIG_HOST_ARCH.sun4-sparc | 13 + config/CONFIG_HOST_ARCH.win32-x86 | 13 + config/CONFIG_HOST_ARCH.win32-x86-borland | 13 + config/CONFIG_HOST_ARCH.win32-x86-cygwin | 20 + config/CONFIG_HOST_ARCH.win32-x86-debug | 14 + config/CONFIG_HOST_ARCH.win32-x86-mingw | 29 + config/CONFIG_SITE | 161 + config/CONFIG_SITE.Host.Borland | 10 + config/CONFIG_SITE.Host.WIN32 | 8 + config/CONFIG_SITE.Host.darwin-ppc | 5 + config/CONFIG_SITE.Host.darwin-x86 | 5 + config/CONFIG_SITE.Host.hp700 | 11 + config/CONFIG_SITE.Host.hpux-parisc | 4 + config/CONFIG_SITE.Host.hpux-parisc-gnu | 11 + config/CONFIG_SITE.Host.linux-x86 | 7 + config/CONFIG_SITE.Host.solaris | 11 + config/CONFIG_SITE.Host.solaris-sparc | 4 + config/CONFIG_SITE.Host.solaris-sparc-debug | 4 + config/CONFIG_SITE.Host.solaris-sparc-gnu | 4 + .../CONFIG_SITE.Host.solaris-sparc-staticlib | 4 + config/CONFIG_SITE.Host.solaris-sparc64 | 4 + config/CONFIG_SITE.Host.win32-x86 | 5 + config/CONFIG_SITE.Host.win32-x86-borland | 4 + config/CONFIG_SITE.Vx.Linux | 10 + config/CONFIG_SITE.Vx.Linux.mv167 | 11 + config/CONFIG_SITE.Vx.Linux.ppc603 | 10 + config/CONFIG_SITE.Vx.linux-x86 | 6 + config/CONFIG_SITE.Vx.ppc604 | 4 + config/CONFIG_SITE_ENV | 45 + config/Makefile | 29 + config/RULES.Db | 215 + config/RULES.Host | 855 +++ config/RULES.Unix | 320 ++ config/RULES.Vx | 332 ++ config/RULES_ARCHS | 99 + config/RULES_DIRS | 57 + config/RULES_TOP | 73 + config/Sample.Makefile.Host | 192 + config/tools/cp.pl | 50 + config/tools/findBase.pl | 22 + config/tools/installEpics | 106 + config/tools/installEpics.pl | 118 + config/tools/makeMakefile.pl | 40 + config/tools/mkdir.pl | 32 + config/tools/munch.pl | 66 + config/tools/mv.pl | 78 + config/tools/rm.pl | 45 + config/tools/useManifestTool.pl | 31 + configure/CONFIG | 140 + configure/CONFIG.CrossCommon | 32 + configure/CONFIG.gnuCommon | 57 + configure/CONFIG_ADDONS | 560 ++ configure/CONFIG_APP_INCLUDE | 24 + configure/CONFIG_BASE | 116 + configure/CONFIG_BASE_VERSION | 66 + configure/CONFIG_COMMON | 425 ++ configure/CONFIG_ENV | 57 + configure/CONFIG_FILE_TYPE | 64 + configure/CONFIG_SITE | 186 + configure/CONFIG_SITE_ENV | 79 + configure/Makefile | 30 + configure/RELEASE | 21 + configure/RULES | 21 + configure/RULES.Db | 382 ++ configure/RULES.ioc | 38 + configure/RULES_ARCHS | 94 + configure/RULES_BUILD | 469 ++ configure/RULES_DIRS | 80 + configure/RULES_EXPAND | 23 + configure/RULES_FILE_TYPE | 73 + configure/RULES_JAVA | 157 + configure/RULES_OCTAVE | 52 + configure/RULES_TARGET | 111 + configure/RULES_TOP | 85 + configure/Sample.Makefile | 216 + configure/os/CONFIG.Common.RTEMS | 117 + configure/os/CONFIG.Common.RTEMS-at91rm9200ek | 13 + configure/os/CONFIG.Common.RTEMS-beatnik | 38 + configure/os/CONFIG.Common.RTEMS-gen68360 | 10 + configure/os/CONFIG.Common.RTEMS-mcp750 | 10 + configure/os/CONFIG.Common.RTEMS-mvme167 | 10 + configure/os/CONFIG.Common.RTEMS-mvme2100 | 29 + configure/os/CONFIG.Common.RTEMS-mvme2700 | 25 + configure/os/CONFIG.Common.RTEMS-mvme3100 | 32 + configure/os/CONFIG.Common.RTEMS-mvme5500 | 20 + configure/os/CONFIG.Common.RTEMS-pc386 | 25 + configure/os/CONFIG.Common.RTEMS-psim | 10 + configure/os/CONFIG.Common.RTEMS-uC5282 | 18 + configure/os/CONFIG.Common.UnixCommon | 94 + configure/os/CONFIG.Common.aix-ppc | 24 + configure/os/CONFIG.Common.aix-ppc-gnu | 11 + configure/os/CONFIG.Common.cygwin-x86 | 77 + configure/os/CONFIG.Common.darwin-ppc | 15 + configure/os/CONFIG.Common.darwin-ppcx86 | 15 + configure/os/CONFIG.Common.darwin-x86 | 15 + configure/os/CONFIG.Common.freebsd-x86 | 34 + configure/os/CONFIG.Common.freebsd-x86_64 | 34 + configure/os/CONFIG.Common.freebsdCommon | 39 + configure/os/CONFIG.Common.ios-arm | 22 + configure/os/CONFIG.Common.ios-x86 | 16 + configure/os/CONFIG.Common.iosCommon | 86 + configure/os/CONFIG.Common.linux-386 | 22 + configure/os/CONFIG.Common.linux-486 | 22 + configure/os/CONFIG.Common.linux-586 | 24 + configure/os/CONFIG.Common.linux-686 | 23 + configure/os/CONFIG.Common.linux-arm | 39 + configure/os/CONFIG.Common.linux-arm_eb | 11 + configure/os/CONFIG.Common.linux-arm_el | 11 + configure/os/CONFIG.Common.linux-athlon | 22 + configure/os/CONFIG.Common.linux-cris | 58 + configure/os/CONFIG.Common.linux-ppc | 15 + configure/os/CONFIG.Common.linux-ppc64 | 18 + configure/os/CONFIG.Common.linux-x86 | 22 + configure/os/CONFIG.Common.linux-x86-borland | 25 + configure/os/CONFIG.Common.linux-x86-debug | 13 + configure/os/CONFIG.Common.linux-x86_64 | 22 + configure/os/CONFIG.Common.linux-x86_64-debug | 13 + configure/os/CONFIG.Common.linux-xscale_be | 25 + configure/os/CONFIG.Common.linuxCommon | 48 + configure/os/CONFIG.Common.solaris-sparc | 67 + .../os/CONFIG.Common.solaris-sparc-debug | 14 + configure/os/CONFIG.Common.solaris-sparc-gnu | 21 + configure/os/CONFIG.Common.solaris-sparc64 | 14 + .../os/CONFIG.Common.solaris-sparc64-gnu | 15 + configure/os/CONFIG.Common.solaris-x86 | 68 + configure/os/CONFIG.Common.solaris-x86-gnu | 20 + configure/os/CONFIG.Common.solaris-x86_64 | 14 + configure/os/CONFIG.Common.solaris-x86_64-gnu | 15 + configure/os/CONFIG.Common.vxWorks-486 | 25 + configure/os/CONFIG.Common.vxWorks-486-debug | 14 + configure/os/CONFIG.Common.vxWorks-68040 | 24 + .../os/CONFIG.Common.vxWorks-68040-debug | 14 + configure/os/CONFIG.Common.vxWorks-68040lc | 24 + .../os/CONFIG.Common.vxWorks-68040lc-debug | 14 + configure/os/CONFIG.Common.vxWorks-68060 | 24 + .../os/CONFIG.Common.vxWorks-68060-debug | 14 + configure/os/CONFIG.Common.vxWorks-mpc8540 | 24 + .../os/CONFIG.Common.vxWorks-mpc8540-debug | 14 + configure/os/CONFIG.Common.vxWorks-pentium | 22 + .../os/CONFIG.Common.vxWorks-pentium-debug | 14 + configure/os/CONFIG.Common.vxWorks-ppc603 | 21 + .../os/CONFIG.Common.vxWorks-ppc603-debug | 14 + .../os/CONFIG.Common.vxWorks-ppc603_long | 13 + .../CONFIG.Common.vxWorks-ppc603_long-debug | 14 + configure/os/CONFIG.Common.vxWorks-ppc604 | 24 + .../os/CONFIG.Common.vxWorks-ppc604-debug | 14 + .../os/CONFIG.Common.vxWorks-ppc604_altivec | 22 + ...CONFIG.Common.vxWorks-ppc604_altivec-debug | 14 + .../os/CONFIG.Common.vxWorks-ppc604_long | 13 + .../CONFIG.Common.vxWorks-ppc604_long-debug | 14 + configure/os/CONFIG.Common.vxWorksCommon | 217 + configure/os/CONFIG.Common.win32-x86-cygwin | 78 + configure/os/CONFIG.Common.win32-x86-mingw | 73 + configure/os/CONFIG.UnixCommon.Common | 18 + configure/os/CONFIG.aix-ppc-gnu.Common | 12 + configure/os/CONFIG.aix-ppc-gnu.aix-ppc-gnu | 16 + configure/os/CONFIG.aix-ppc.Common | 10 + configure/os/CONFIG.cygwin-x86.Common | 20 + configure/os/CONFIG.cygwin-x86.cygwin-x86 | 29 + .../os/CONFIG.cygwin-x86.cygwin-x86-debug | 17 + configure/os/CONFIG.darwin-ppc.Common | 11 + .../os/CONFIG.darwin-ppc.darwin-ppc-debug | 16 + configure/os/CONFIG.darwin-ppcx86.Common | 11 + configure/os/CONFIG.darwin-x86.Common | 11 + .../os/CONFIG.darwin-x86.darwin-x86-debug | 17 + configure/os/CONFIG.darwinCommon.darwinCommon | 121 + configure/os/CONFIG.freebsd-x86.Common | 10 + configure/os/CONFIG.freebsd-x86.freebsd-x86 | 14 + configure/os/CONFIG.freebsd-x86_64.Common | 11 + .../os/CONFIG.freebsd-x86_64.freebsd-x86_64 | 14 + configure/os/CONFIG.linux-ppc.Common | 11 + configure/os/CONFIG.linux-ppc.linux-ppc | 10 + configure/os/CONFIG.linux-ppc64.Common | 12 + configure/os/CONFIG.linux-ppc64.linux-ppc64 | 11 + configure/os/CONFIG.linux-x86-borland.Common | 13 + ...CONFIG.linux-x86-borland.linux-x86-borland | 198 + configure/os/CONFIG.linux-x86-debug.Common | 15 + .../os/CONFIG.linux-x86-debug.linux-x86-debug | 14 + configure/os/CONFIG.linux-x86.Common | 13 + configure/os/CONFIG.linux-x86.linux-arm | 12 + configure/os/CONFIG.linux-x86.linux-arm_eb | 9 + configure/os/CONFIG.linux-x86.linux-arm_el | 9 + configure/os/CONFIG.linux-x86.linux-cris | 38 + configure/os/CONFIG.linux-x86.linux-cris_v10 | 20 + configure/os/CONFIG.linux-x86.linux-cris_v32 | 20 + configure/os/CONFIG.linux-x86.linux-x86 | 18 + configure/os/CONFIG.linux-x86.linux-x86-debug | 15 + configure/os/CONFIG.linux-x86_64-debug.Common | 15 + ...NFIG.linux-x86_64-debug.linux-x86_64-debug | 14 + configure/os/CONFIG.linux-x86_64.Common | 13 + configure/os/CONFIG.linux-x86_64.linux-x86_64 | 11 + .../os/CONFIG.linux-x86_64.linux-x86_64-debug | 15 + .../os/CONFIG.solaris-sparc-debug.Common | 12 + ...IG.solaris-sparc-debug.solaris-sparc-debug | 14 + configure/os/CONFIG.solaris-sparc-gnu.Common | 12 + ...CONFIG.solaris-sparc-gnu.solaris-sparc-gnu | 26 + configure/os/CONFIG.solaris-sparc.Common | 14 + .../os/CONFIG.solaris-sparc.solaris-sparc | 12 + .../CONFIG.solaris-sparc.solaris-sparc-debug | 16 + .../os/CONFIG.solaris-sparc.solaris-sparc-gnu | 13 + .../os/CONFIG.solaris-sparc.solaris-sparc64 | 14 + .../CONFIG.solaris-sparc.solaris-sparc64-gnu | 13 + .../os/CONFIG.solaris-sparc64-gnu.Common | 12 + ...IG.solaris-sparc64-gnu.solaris-sparc64-gnu | 12 + configure/os/CONFIG.solaris-sparc64.Common | 12 + .../os/CONFIG.solaris-sparc64.solaris-sparc64 | 11 + ...NFIG.solaris-sparc64.solaris-sparc64-debug | 16 + configure/os/CONFIG.solaris-x86-gnu.Common | 12 + .../os/CONFIG.solaris-x86-gnu.solaris-x86-gnu | 20 + configure/os/CONFIG.solaris-x86.Common | 12 + configure/os/CONFIG.solaris-x86.solaris-x86 | 12 + .../os/CONFIG.solaris-x86.solaris-x86-debug | 18 + .../os/CONFIG.solaris-x86.solaris-x86_64 | 14 + configure/os/CONFIG.solaris-x86_64-gnu.Common | 12 + ...NFIG.solaris-x86_64-gnu.solaris-x86_64-gnu | 12 + configure/os/CONFIG.solaris-x86_64.Common | 12 + .../os/CONFIG.solaris-x86_64.solaris-x86_64 | 11 + ...CONFIG.solaris-x86_64.solaris-x86_64-debug | 16 + .../os/CONFIG.solarisCommon.solarisCommon | 68 + configure/os/CONFIG.win32-x86-borland.Common | 29 + ...CONFIG.win32-x86-borland.win32-x86-borland | 267 + configure/os/CONFIG.win32-x86-cygwin.Common | 22 + .../CONFIG.win32-x86-cygwin.win32-x86-cygwin | 28 + ...IG.win32-x86-cygwin.win32-x86-cygwin-debug | 14 + configure/os/CONFIG.win32-x86-debug.Common | 12 + .../os/CONFIG.win32-x86-debug.win32-x86-debug | 12 + configure/os/CONFIG.win32-x86-mingw.Common | 26 + .../os/CONFIG.win32-x86-mingw.win32-x86-mingw | 29 + ...NFIG.win32-x86-mingw.win32-x86-mingw-debug | 17 + configure/os/CONFIG.win32-x86.Common | 29 + configure/os/CONFIG.win32-x86.win32-x86 | 294 ++ configure/os/CONFIG.win32-x86.win32-x86-debug | 20 + configure/os/CONFIG.win32-x86.windows-x64 | 28 + configure/os/CONFIG.windows-x64-debug.Common | 12 + ...CONFIG.windows-x64-debug.windows-x64-debug | 12 + configure/os/CONFIG.windows-x64.Common | 13 + configure/os/CONFIG.windows-x64.windows-x64 | 25 + configure/os/CONFIG_COMPAT | 26 + configure/os/CONFIG_SITE.Common.RTEMS | 25 + configure/os/CONFIG_SITE.Common.RTEMS-pc386 | 3 + configure/os/CONFIG_SITE.Common.cygwin-x86 | 10 + configure/os/CONFIG_SITE.Common.darwin-ppc | 15 + configure/os/CONFIG_SITE.Common.darwin-ppcx86 | 23 + configure/os/CONFIG_SITE.Common.darwin-x86 | 15 + configure/os/CONFIG_SITE.Common.iosCommon | 24 + configure/os/CONFIG_SITE.Common.linux-cris | 37 + configure/os/CONFIG_SITE.Common.linux-x86 | 36 + configure/os/CONFIG_SITE.Common.linux-x86_64 | 36 + configure/os/CONFIG_SITE.Common.solaris-sparc | 18 + .../os/CONFIG_SITE.Common.solaris-sparc-gnu | 15 + .../os/CONFIG_SITE.Common.solaris-sparc64 | 11 + .../os/CONFIG_SITE.Common.solaris-sparc64-gnu | 11 + .../os/CONFIG_SITE.Common.solaris-x86-gnu | 12 + .../os/CONFIG_SITE.Common.solaris-x86_64 | 11 + .../os/CONFIG_SITE.Common.solaris-x86_64-gnu | 11 + .../os/CONFIG_SITE.Common.vxWorks-mpc8540 | 6 + .../os/CONFIG_SITE.Common.vxWorks-ppc603 | 7 + .../os/CONFIG_SITE.Common.vxWorks-ppc603_long | 10 + .../os/CONFIG_SITE.Common.vxWorks-ppc604 | 7 + .../CONFIG_SITE.Common.vxWorks-ppc604_altivec | 10 + .../os/CONFIG_SITE.Common.vxWorks-ppc604_long | 10 + configure/os/CONFIG_SITE.Common.vxWorksCommon | 47 + .../os/CONFIG_SITE.Common.win32-x86-cygwin | 7 + .../os/CONFIG_SITE.Common.win32-x86-mingw | 11 + configure/os/CONFIG_SITE.cygwin-x86.Common | 10 + .../os/CONFIG_SITE.cygwin-x86.cygwin-x86 | 11 + configure/os/CONFIG_SITE.darwin-ppc.Common | 7 + configure/os/CONFIG_SITE.darwin-ppcx86.Common | 7 + configure/os/CONFIG_SITE.darwin-x86.Common | 11 + .../os/CONFIG_SITE.linux-x86-borland.Common | 12 + ...ONFIG_SITE.linux-x86-debug.linux-x86-debug | 7 + configure/os/CONFIG_SITE.linux-x86.Common | 12 + configure/os/CONFIG_SITE.linux-x86.RTEMS | 8 + configure/os/CONFIG_SITE.linux-x86.UnixCommon | 6 + configure/os/CONFIG_SITE.linux-x86.linux-arm | 12 + .../os/CONFIG_SITE.linux-x86.linux-arm_eb | 15 + .../os/CONFIG_SITE.linux-x86.linux-arm_el | 15 + configure/os/CONFIG_SITE.linux-x86.linux-cris | 14 + configure/os/CONFIG_SITE.linux-x86.linux-x86 | 7 + .../os/CONFIG_SITE.linux-x86.solaris-sparc | 10 + .../os/CONFIG_SITE.linux-x86.vxWorks-68040 | 8 + .../os/CONFIG_SITE.linux-x86.vxWorks-ppc603 | 7 + .../CONFIG_SITE.linux-x86.vxWorks-ppc603_long | 9 + .../os/CONFIG_SITE.linux-x86.vxWorksCommon | 9 + ...SITE.linux-x86_64-debug.linux-x86_64-debug | 15 + configure/os/CONFIG_SITE.linux-x86_64.Common | 12 + .../os/CONFIG_SITE.linux-x86_64.UnixCommon | 7 + .../os/CONFIG_SITE.linux-x86_64.linux-x86_64 | 8 + .../os/CONFIG_SITE.linux-x86_64.vxWorks-68040 | 8 + .../CONFIG_SITE.linux-x86_64.vxWorks-ppc603 | 6 + ...NFIG_SITE.linux-x86_64.vxWorks-ppc603_long | 9 + ...TE.solaris-sparc-debug.solaris-sparc-debug | 8 + configure/os/CONFIG_SITE.solaris-sparc.Common | 10 + ...FIG_SITE.solaris-sparc.solaris-sparc-debug | 8 + .../os/CONFIG_SITE.win32-x86-borland.Common | 11 + ...FIG_SITE.win32-x86-cygwin.win32-x86-cygwin | 2 + ...ONFIG_SITE.win32-x86-mingw.win32-x86-mingw | 17 + configure/os/CONFIG_SITE.win32-x86.Common | 11 + configure/os/CONFIG_SITE.win32-x86.win32-x86 | 17 + documentation/BuildingR3.13AppsWithR3.14.html | 239 + .../BuildingR3.13ExtensionsWithR3.14.html | 227 + documentation/ConvertingR3.13AppsToR3.14.html | 476 ++ documentation/KnownProblems.html | 20 + documentation/README.1st | 441 ++ documentation/README.MS_Windows | 224 + documentation/README.cris | 67 + documentation/README.darwin.html | 186 + documentation/README.html | 464 ++ documentation/README.niCpu030 | 34 + documentation/RELEASE_NOTES.html | 3384 ++++++++++++ documentation/ReleaseChecklist.html | 382 ++ src/Makefile | 98 + src/RTEMS/Makefile | 21 + src/RTEMS/base/Makefile | 26 + src/RTEMS/base/epicsRtemsInitHookPost.c | 19 + src/RTEMS/base/epicsRtemsInitHookPre.c | 19 + src/RTEMS/base/epicsRtemsInitHooks.h | 24 + src/RTEMS/base/rtems_config.c | 72 + src/RTEMS/base/rtems_init.c | 571 ++ src/RTEMS/base/rtems_netconfig.c | 111 + src/RTEMS/base/rtems_util.c | 45 + src/RTEMS/base/setBootConfigFromNVRAM.c | 370 ++ src/as/Makefile | 51 + src/as/asCa.c | 333 ++ src/as/asCa.h | 30 + src/as/asDbLib.c | 330 ++ src/as/asDbLib.h | 53 + src/as/asHost.rc | 36 + src/as/asIoc.rc | 36 + src/as/asIocRegister.c | 128 + src/as/asIocRegister.h | 25 + src/as/asLib.h | 229 + src/as/asLib.y | 232 + src/as/asLibRoutines.c | 1352 +++++ src/as/asLib_lex.l | 82 + src/as/asTrapWrite.c | 167 + src/as/asTrapWrite.h | 54 + src/as/ascheck.c | 57 + src/bpt/Makefile | 30 + src/bpt/bptTypeJdegC.data | 142 + src/bpt/bptTypeJdegF.data | 213 + src/bpt/bptTypeKdegC.data | 201 + src/bpt/bptTypeKdegF.data | 360 ++ src/bpt/cvtTable.h | 37 + src/bpt/makeBpt.c | 426 ++ src/bpt/menuConvert.dbd | 26 + src/ca/CASG.cpp | 314 ++ src/ca/CAref.html | 4457 ++++++++++++++++ src/ca/Makefile | 106 + src/ca/SearchDest.h | 39 + src/ca/access.cpp | 1098 ++++ src/ca/acctst.c | 3193 +++++++++++ src/ca/acctstMain.c | 70 + src/ca/addrList.h | 40 + src/ca/autoPtrDestroy.h | 94 + src/ca/autoPtrFreeList.h | 104 + src/ca/autoPtrRecycle.h | 92 + src/ca/baseNMIU.cpp | 39 + src/ca/bhe.cpp | 365 ++ src/ca/bhe.h | 123 + src/ca/ca.rc | 36 + src/ca/caConnTest.cpp | 109 + src/ca/caConnTestMain.cpp | 45 + src/ca/caDiagnostics.h | 38 + src/ca/caEventRate.cpp | 139 + src/ca/caEventRateMain.cpp | 38 + src/ca/caProto.h | 215 + src/ca/caRepeater.cpp | 42 + src/ca/caServerID.h | 88 + src/ca/ca_client_context.cpp | 791 +++ src/ca/cac.cpp | 1309 +++++ src/ca/cac.h | 470 ++ src/ca/cacChannel.cpp | 145 + src/ca/cacChannelNotify.cpp | 34 + src/ca/cacContextNotify.cpp | 43 + src/ca/cacIO.h | 373 ++ src/ca/cacReadNotify.cpp | 30 + src/ca/cacStateNotify.cpp | 30 + src/ca/cacWriteNotify.cpp | 30 + src/ca/cadef.h | 903 ++++ src/ca/caerr.h | 160 + src/ca/caeventmask.h | 44 + src/ca/casw.cpp | 306 ++ src/ca/catime.c | 685 +++ src/ca/catimeMain.c | 53 + src/ca/comBuf.cpp | 66 + src/ca/comBuf.h | 336 ++ src/ca/comQueRecv.cpp | 269 + src/ca/comQueRecv.h | 111 + src/ca/comQueSend.cpp | 411 ++ src/ca/comQueSend.h | 238 + src/ca/convert.cpp | 1440 +++++ src/ca/db_access.h | 740 +++ src/ca/disconnectGovernorTimer.cpp | 110 + src/ca/disconnectGovernorTimer.h | 77 + src/ca/evtime.c | 95 + src/ca/future_work.txt | 38 + src/ca/getCallback.cpp | 111 + src/ca/getCopy.cpp | 125 + src/ca/hostNameCache.cpp | 92 + src/ca/hostNameCache.h | 61 + src/ca/inetAddrID.h | 72 + src/ca/iocinf.cpp | 264 + src/ca/iocinf.h | 77 + src/ca/localHostName.cpp | 66 + src/ca/localHostName.h | 65 + src/ca/msgForMultiplyDefinedPV.cpp | 98 + src/ca/msgForMultiplyDefinedPV.h | 77 + src/ca/nciu.cpp | 628 +++ src/ca/nciu.h | 376 ++ src/ca/netIO.h | 309 ++ src/ca/netReadNotifyIO.cpp | 141 + src/ca/netSubscription.cpp | 196 + src/ca/netWriteNotifyIO.cpp | 138 + src/ca/net_convert.h | 36 + src/ca/netiiu.cpp | 174 + src/ca/netiiu.h | 97 + src/ca/noopiiu.cpp | 170 + src/ca/noopiiu.h | 92 + src/ca/oldAccess.h | 564 ++ src/ca/oldChannelNotify.cpp | 709 +++ src/ca/oldSubscription.cpp | 107 + src/ca/putCallback.cpp | 110 + src/ca/repeater.cpp | 607 +++ src/ca/repeaterClient.h | 73 + src/ca/repeaterSubscribeTimer.cpp | 104 + src/ca/repeaterSubscribeTimer.h | 81 + src/ca/searchTimer.cpp | 400 ++ src/ca/searchTimer.h | 108 + src/ca/sgAutoPtr.h | 104 + src/ca/syncGroup.h | 280 + src/ca/syncGroupNotify.cpp | 34 + src/ca/syncGroupReadNotify.cpp | 155 + src/ca/syncGroupWriteNotify.cpp | 146 + src/ca/syncgrp.cpp | 292 ++ src/ca/tcpRecvThread.cpp | 11 + src/ca/tcpRecvWatchdog.cpp | 251 + src/ca/tcpRecvWatchdog.h | 85 + src/ca/tcpSendWatchdog.cpp | 76 + src/ca/tcpSendWatchdog.h | 63 + src/ca/tcpiiu.cpp | 2190 ++++++++ src/ca/templateInstances.cpp | 85 + src/ca/test_event.cpp | 588 +++ src/ca/ucx.h | 102 + src/ca/udpiiu.cpp | 1393 +++++ src/ca/udpiiu.h | 300 ++ src/ca/virtualCircuit.h | 424 ++ src/cap5/CA.pm | 650 +++ src/cap5/Cap5.xs | 1429 +++++ src/cap5/Makefile | 64 + src/cap5/caget.pl | 187 + src/cap5/cainfo.pl | 64 + src/cap5/camonitor.pl | 149 + src/cap5/capr.pl | 423 ++ src/cap5/caput.pl | 186 + src/cap5/perlConfig.pl | 9 + src/cas/Makefile | 21 + src/cas/README | 29 + src/cas/RELEASE_NOTES | 253 + src/cas/build/Makefile | 94 + src/cas/build/cas.rc | 36 + src/cas/example/Makefile | 19 + src/cas/example/README | 7 + src/cas/example/directoryService/Makefile | 41 + src/cas/example/directoryService/README | 13 + .../directoryService/directoryServer.cc | 169 + .../directoryService/directoryServer.h | 147 + src/cas/example/directoryService/main.cc | 212 + .../example/directoryService/pvDirectory.txt | 5 + src/cas/example/directoryService/test.adl | 844 +++ src/cas/example/directoryService/vxEntry.cc | 79 + src/cas/generic/README | 43 + src/cas/generic/beaconAnomalyGovernor.cc | 86 + src/cas/generic/beaconAnomalyGovernor.h | 55 + src/cas/generic/beaconTimer.cc | 91 + src/cas/generic/beaconTimer.h | 56 + src/cas/generic/caHdrLargeArray.h | 49 + src/cas/generic/caNetAddr.cc | 200 + src/cas/generic/caNetAddr.h | 71 + src/cas/generic/caServer.cc | 174 + src/cas/generic/caServerDefs.h | 34 + src/cas/generic/caServerI.cc | 300 ++ src/cas/generic/caServerI.h | 162 + src/cas/generic/casAddr.h | 17 + src/cas/generic/casAsyncIOI.cc | 127 + src/cas/generic/casAsyncIOI.h | 68 + src/cas/generic/casAsyncPVAttachIO.cc | 66 + src/cas/generic/casAsyncPVAttachIOI.cpp | 69 + src/cas/generic/casAsyncPVAttachIOI.h | 45 + src/cas/generic/casAsyncPVExistIO.cc | 56 + src/cas/generic/casAsyncPVExistIOI.cpp | 70 + src/cas/generic/casAsyncPVExistIOI.h | 48 + src/cas/generic/casAsyncReadIO.cc | 60 + src/cas/generic/casAsyncReadIOI.cc | 109 + src/cas/generic/casAsyncReadIOI.h | 47 + src/cas/generic/casAsyncWriteIO.cc | 57 + src/cas/generic/casAsyncWriteIOI.cpp | 71 + src/cas/generic/casAsyncWriteIOI.h | 46 + src/cas/generic/casBufferFactory.cpp | 106 + src/cas/generic/casChannel.cc | 121 + src/cas/generic/casChannelI.cc | 121 + src/cas/generic/casChannelI.h | 162 + src/cas/generic/casCoreClient.cc | 211 + src/cas/generic/casCoreClient.h | 251 + src/cas/generic/casCtx.cc | 46 + src/cas/generic/casCtx.h | 106 + src/cas/generic/casCtxIL.h | 116 + src/cas/generic/casDGClient.cc | 956 ++++ src/cas/generic/casDGClient.h | 109 + src/cas/generic/casEvent.h | 54 + src/cas/generic/casEventMask.cc | 155 + src/cas/generic/casEventMask.h | 109 + src/cas/generic/casEventRegistry.h | 69 + src/cas/generic/casEventSys.cc | 398 ++ src/cas/generic/casEventSys.h | 145 + src/cas/generic/casMonEvent.cc | 79 + src/cas/generic/casMonEvent.h | 81 + src/cas/generic/casMonitor.cc | 174 + src/cas/generic/casMonitor.h | 116 + src/cas/generic/casOpaqueAddr.cc | 38 + src/cas/generic/casOpaqueAddrIL.h | 55 + src/cas/generic/casPV.cc | 186 + src/cas/generic/casPVI.cc | 532 ++ src/cas/generic/casPVI.h | 123 + src/cas/generic/casStrmClient.cc | 2889 ++++++++++ src/cas/generic/casStrmClient.h | 196 + src/cas/generic/casdef.h | 980 ++++ src/cas/generic/chanIntfForPV.cc | 67 + src/cas/generic/chanIntfForPV.h | 94 + src/cas/generic/channelDestroyEvent.cpp | 35 + src/cas/generic/channelDestroyEvent.h | 58 + src/cas/generic/clientBufMemoryManager.cpp | 57 + src/cas/generic/clientBufMemoryManager.h | 50 + src/cas/generic/inBuf.cc | 180 + src/cas/generic/inBuf.h | 159 + src/cas/generic/ioBlocked.h | 62 + src/cas/generic/mt/README | 5 + src/cas/generic/mt/ioBlocked.cc | 81 + src/cas/generic/outBuf.cc | 333 ++ src/cas/generic/outBuf.h | 171 + src/cas/generic/pvAttachReturn.cc | 94 + src/cas/generic/pvExistReturn.cc | 59 + src/cas/generic/st/README | 5 + src/cas/generic/st/caServerOS.cc | 0 src/cas/generic/st/casDGEvWakeup.h | 33 + src/cas/generic/st/casDGIOWakeup.h | 45 + src/cas/generic/st/casDGIntfOS.cc | 485 ++ src/cas/generic/st/casDGIntfOS.h | 61 + src/cas/generic/st/casIntfOS.cc | 76 + src/cas/generic/st/casIntfOS.h | 46 + src/cas/generic/st/casOSD.h | 52 + src/cas/generic/st/casStreamOS.cc | 633 +++ src/cas/generic/st/casStreamOS.h | 94 + src/cas/generic/st/ioBlocked.cc | 113 + src/cas/generic/st/osiMutexCAS.h | 15 + src/cas/generic/templateInstances.cpp | 29 + src/cas/io/bsdSocket/README | 6 + src/cas/io/bsdSocket/caServerIO.cc | 198 + src/cas/io/bsdSocket/caServerIO.h | 52 + src/cas/io/bsdSocket/casDGIntfIO.cc | 593 +++ src/cas/io/bsdSocket/casDGIntfIO.h | 72 + src/cas/io/bsdSocket/casIOD.h | 40 + src/cas/io/bsdSocket/casIntfIO.cc | 236 + src/cas/io/bsdSocket/casIntfIO.h | 62 + src/cas/io/bsdSocket/casStreamIO.cc | 314 ++ src/cas/io/bsdSocket/casStreamIO.h | 50 + src/cas/io/bsdSocket/ipIgnoreEntry.cpp | 90 + src/cas/io/bsdSocket/ipIgnoreEntry.h | 52 + src/cas/os/vms/BUILD_VMS.COM | 203 + src/cas/os/vms/README | 7 + src/cas/os/vms/casSpecificOS.h | 57 + src/cas/os/vms/login.com | 3 + src/cas/os/vms/mitfp.c | 271 + src/cas/os/vms/mitfp.cc | 284 + src/cas/os/vms/mitfp.h | 17 + src/cas/os/vms/vms_depen.h | 45 + src/cas/test/gddAppFuncTableTest.cc | 74 + src/catools/Makefile | 32 + src/catools/caget.c | 558 ++ src/catools/cainfo.c | 224 + src/catools/camonitor.c | 398 ++ src/catools/caput.c | 560 ++ src/catools/tool_lib.c | 620 +++ src/catools/tool_lib.h | 99 + src/db/Makefile | 97 + src/db/callback.c | 233 + src/db/callback.h | 75 + src/db/cvtBpt.c | 203 + src/db/dbAccess.c | 1610 ++++++ src/db/dbAccess.h | 29 + src/db/dbAccessDefs.h | 289 + src/db/dbAddr.h | 33 + src/db/dbBkpt.c | 970 ++++ src/db/dbBkpt.h | 104 + src/db/dbCAC.h | 243 + src/db/dbCa.c | 1004 ++++ src/db/dbCa.h | 75 + src/db/dbCaPvt.h | 88 + src/db/dbCaTest.c | 204 + src/db/dbCaTest.h | 26 + src/db/dbChannelIO.cpp | 243 + src/db/dbChannelIO.h | 134 + src/db/dbCommon.dbd | 254 + src/db/dbCommonRecord.dbd | 12 + src/db/dbContext.cpp | 422 ++ src/db/dbContextReadNotifyCache.cpp | 164 + src/db/dbConvert.c | 4651 +++++++++++++++++ src/db/dbConvert.h | 33 + src/db/dbConvertFast.h | 29 + src/db/dbEvent.c | 1051 ++++ src/db/dbEvent.h | 83 + src/db/dbFastLinkConv.c | 1175 +++++ src/db/dbIoc.rc | 36 + src/db/dbIocRegister.c | 334 ++ src/db/dbIocRegister.h | 25 + src/db/dbLock.c | 627 +++ src/db/dbLock.h | 53 + src/db/dbNotify.c | 573 ++ src/db/dbNotify.h | 122 + src/db/dbPutNotifyBlocker.cpp | 234 + src/db/dbPutNotifyBlocker.h | 90 + src/db/dbScan.c | 765 +++ src/db/dbScan.h | 69 + src/db/dbSubscriptionIO.cpp | 169 + src/db/dbTest.c | 1232 +++++ src/db/dbTest.h | 52 + src/db/db_access.c | 1032 ++++ src/db/db_access_routines.h | 51 + src/db/db_convert.h | 69 + src/db/db_field_log.h | 58 + src/db/db_test.c | 728 +++ src/db/db_test.h | 26 + src/db/initHooks.c | 130 + src/db/initHooks.h | 68 + src/db/menuAlarmSevr.dbd | 15 + src/db/menuAlarmStat.dbd | 33 + src/db/menuCompress.dbd | 15 + src/db/menuFtype.dbd | 21 + src/db/menuGlobal.dbd | 19 + src/db/menuIvoa.dbd | 14 + src/db/menuOmsl.dbd | 13 + src/db/menuPini.dbd | 16 + src/db/menuPriority.dbd | 14 + src/db/menuScan.dbd | 21 + src/db/menuSimm.dbd | 14 + src/db/menuYesNo.dbd | 13 + src/db/recGbl.c | 358 ++ src/db/recGbl.h | 75 + src/db/templateInstances.cpp | 46 + src/db/test/Makefile | 30 + src/db/test/callbackTest.c | 123 + src/dbStatic/Makefile | 66 + src/dbStatic/alarm.h | 112 + src/dbStatic/alarmString.h | 69 + src/dbStatic/dbBase.h | 173 + src/dbStatic/dbExpand.c | 123 + src/dbStatic/dbFldTypes.h | 93 + src/dbStatic/dbLex.l | 90 + src/dbStatic/dbLexRoutines.c | 1041 ++++ src/dbStatic/dbPvdLib.c | 227 + src/dbStatic/dbReadTest.c | 90 + src/dbStatic/dbStaticHost.rc | 36 + src/dbStatic/dbStaticIoc.rc | 36 + src/dbStatic/dbStaticIocRegister.c | 170 + src/dbStatic/dbStaticIocRegister.h | 25 + src/dbStatic/dbStaticLib.c | 4120 +++++++++++++++ src/dbStatic/dbStaticLib.h | 271 + src/dbStatic/dbStaticNoRun.c | 338 ++ src/dbStatic/dbStaticPvt.h | 63 + src/dbStatic/dbStaticRun.c | 695 +++ src/dbStatic/dbToMenuH.c | 124 + src/dbStatic/dbToRecordtypeH.c | 267 + src/dbStatic/dbYacc.y | 285 + src/dbStatic/devSup.h | 71 + src/dbStatic/drvSup.h | 35 + src/dbStatic/guigroup.h | 83 + src/dbStatic/link.h | 194 + src/dbStatic/recSup.h | 87 + src/dbStatic/special.h | 69 + src/dbtools/Makefile | 44 + src/dbtools/dbLoadTemplate.h | 22 + src/dbtools/dbLoadTemplate.html | 137 + src/dbtools/dbLoadTemplate.y | 315 ++ src/dbtools/dbLoadTemplate_lex.l | 56 + src/dbtools/dbtoolsIoc.rc | 36 + src/dbtools/dbtoolsIocRegister.c | 29 + src/dbtools/dbtoolsIocRegister.h | 23 + src/dev/Makefile | 19 + src/dev/softDev/Makefile | 66 + src/dev/softDev/devAaiSoft.c | 87 + src/dev/softDev/devAaoSoft.c | 79 + src/dev/softDev/devAiSoft.c | 96 + src/dev/softDev/devAiSoftRaw.c | 78 + src/dev/softDev/devAoSoft.c | 75 + src/dev/softDev/devAoSoftCallback.c | 70 + src/dev/softDev/devAoSoftRaw.c | 63 + src/dev/softDev/devBiSoft.c | 79 + src/dev/softDev/devBiSoftRaw.c | 76 + src/dev/softDev/devBoSoft.c | 74 + src/dev/softDev/devBoSoftCallback.c | 70 + src/dev/softDev/devBoSoftRaw.c | 73 + src/dev/softDev/devCalcoutSoft.c | 49 + src/dev/softDev/devCalcoutSoftCallback.c | 64 + src/dev/softDev/devEventSoft.c | 81 + src/dev/softDev/devGeneralTime.c | 298 ++ src/dev/softDev/devHistogramSoft.c | 81 + src/dev/softDev/devLiSoft.c | 79 + src/dev/softDev/devLoSoft.c | 60 + src/dev/softDev/devLoSoftCallback.c | 67 + src/dev/softDev/devMbbiDirectSoft.c | 79 + src/dev/softDev/devMbbiDirectSoftRaw.c | 80 + src/dev/softDev/devMbbiSoft.c | 79 + src/dev/softDev/devMbbiSoftRaw.c | 80 + src/dev/softDev/devMbboDirectSoft.c | 66 + src/dev/softDev/devMbboDirectSoftCallback.c | 66 + src/dev/softDev/devMbboDirectSoftRaw.c | 71 + src/dev/softDev/devMbboSoft.c | 67 + src/dev/softDev/devMbboSoftCallback.c | 66 + src/dev/softDev/devMbboSoftRaw.c | 71 + src/dev/softDev/devSASoft.c | 100 + src/dev/softDev/devSiSoft.c | 84 + src/dev/softDev/devSoSoft.c | 54 + src/dev/softDev/devSoSoftCallback.c | 68 + src/dev/softDev/devSoStdio.c | 109 + src/dev/softDev/devSoft.dbd | 45 + src/dev/softDev/devTimestamp.c | 78 + src/dev/softDev/devWfSoft.c | 81 + src/dev/softDev/softDevIoc.rc | 36 + src/dev/testDev/Makefile | 27 + src/dev/testDev/devHistogramTestAsyn.c | 107 + src/dev/testDev/devTestAsyn.c | 75 + src/dev/testDev/devTestAsyn.dbd | 20 + src/dev/testDev/testDevIoc.rc | 36 + src/excas/Makefile | 37 + src/gdd/Makefile | 97 + src/gdd/README | 160 + src/gdd/aitConvert.cc | 451 ++ src/gdd/aitConvert.h | 172 + src/gdd/aitGen.c | 443 ++ src/gdd/aitHelpers.cc | 246 + src/gdd/aitHelpers.h | 465 ++ src/gdd/aitTypes.c | 92 + src/gdd/aitTypes.h | 140 + src/gdd/dbMapper.cc | 1805 +++++++ src/gdd/dbMapper.h | 66 + src/gdd/gdd.cc | 1829 +++++++ src/gdd/gdd.gif | Bin 0 -> 4417 bytes src/gdd/gdd.h | 519 ++ src/gdd/gdd.html | 497 ++ src/gdd/gdd.rc | 36 + src/gdd/gddAppDefs.cc | 268 + src/gdd/gddAppFuncTable.h | 271 + src/gdd/gddAppTable.cc | 638 +++ src/gdd/gddAppTable.h | 206 + src/gdd/gddArray.cc | 103 + src/gdd/gddArray.h | 61 + src/gdd/gddArrayI.h | 31 + src/gdd/gddContainer.cc | 158 + src/gdd/gddContainer.h | 114 + src/gdd/gddContainerI.h | 112 + src/gdd/gddEnumStringTable.cc | 152 + src/gdd/gddEnumStringTable.h | 50 + src/gdd/gddErrorCodes.cc | 33 + src/gdd/gddErrorCodes.h | 57 + src/gdd/gddI.h | 692 +++ src/gdd/gddNewDel.cc | 81 + src/gdd/gddNewDel.h | 121 + src/gdd/gddScalar.h | 52 + src/gdd/gddScalarI.h | 33 + src/gdd/gddTest.cc | 541 ++ src/gdd/gddUtils.cc | 62 + src/gdd/gddUtils.h | 150 + src/gdd/gddUtilsI.h | 36 + src/gdd/gddref.html | 518 ++ src/gdd/gddref2.html | 665 +++ src/gdd/genApps.cc | 54 + src/gdd/smartGDDPointer.h | 186 + src/libCom/Com.rc | 36 + src/libCom/Makefile | 332 ++ src/libCom/bucketLib/bucketLib.c | 522 ++ src/libCom/bucketLib/bucketLib.h | 93 + src/libCom/calc/calcPerform.c | 485 ++ src/libCom/calc/postfix.c | 609 +++ src/libCom/calc/postfix.h | 88 + src/libCom/calc/postfixPvt.h | 104 + src/libCom/cppStd/epicsAlgorithm.h | 79 + src/libCom/cppStd/epicsExcept.h | 72 + src/libCom/cppStd/epicsMemory.h | 115 + src/libCom/cvtFast/cvtFast.c | 596 +++ src/libCom/cvtFast/cvtFast.h | 82 + src/libCom/cxxTemplates/README | 57 + src/libCom/cxxTemplates/epicsGuard.h | 115 + src/libCom/cxxTemplates/epicsOnce.cpp | 113 + src/libCom/cxxTemplates/epicsOnce.h | 46 + src/libCom/cxxTemplates/epicsSingleton.h | 218 + .../cxxTemplates/epicsSingletonBase.cpp | 62 + .../cxxTemplates/epicsSingletonMutex.cpp | 63 + src/libCom/cxxTemplates/resourceLib.cpp | 33 + src/libCom/cxxTemplates/resourceLib.h | 1172 +++++ src/libCom/cxxTemplates/test/Makefile | 50 + src/libCom/cxxTemplates/test/minmaxTest.cc | 39 + .../cxxTemplates/test/resourceLibTest.cc | 309 ++ src/libCom/cxxTemplates/test/tsBTreeBench.cc | 95 + src/libCom/cxxTemplates/test/tsBTreeTest.cc | 93 + src/libCom/cxxTemplates/test/tsDLListBench.cc | 71 + src/libCom/cxxTemplates/test/tsDLListTest.cc | 131 + src/libCom/cxxTemplates/test/tsSLListBench.cc | 85 + src/libCom/cxxTemplates/test/tsSLListTest.cc | 106 + src/libCom/cxxTemplates/tsBTree.h | 279 + src/libCom/cxxTemplates/tsDLList.h | 686 +++ src/libCom/cxxTemplates/tsFreeList.h | 198 + src/libCom/cxxTemplates/tsMinMax.h | 31 + src/libCom/cxxTemplates/tsSLList.h | 445 ++ src/libCom/dbmf/dbmf.c | 238 + src/libCom/dbmf/dbmf.h | 45 + src/libCom/ellLib/ellLib.c | 317 ++ src/libCom/ellLib/ellLib.h | 66 + src/libCom/env/bldEnvData.pl | 125 + src/libCom/env/envDefs.h | 100 + src/libCom/env/envSubr.c | 413 ++ src/libCom/error/epicsPrint.h | 18 + src/libCom/error/errMdef.h | 64 + src/libCom/error/errSymLib.c | 320 ++ src/libCom/error/errSymTbl.h | 33 + src/libCom/error/errlog.c | 617 +++ src/libCom/error/errlog.h | 85 + src/libCom/error/error.h | 57 + src/libCom/error/makeStatTbl.pl | 127 + src/libCom/fdmgr/fdManager.cpp | 364 ++ src/libCom/fdmgr/fdManager.h | 205 + src/libCom/fdmgr/fdmgr.cpp | 337 ++ src/libCom/fdmgr/fdmgr.h | 171 + src/libCom/freeList/freeList.h | 35 + src/libCom/freeList/freeList.html | 85 + src/libCom/freeList/freeListLib.c | 153 + src/libCom/gpHash/gpHash.h | 52 + src/libCom/gpHash/gpHash.html | 139 + src/libCom/gpHash/gpHashLib.c | 231 + src/libCom/iocsh/iocsh.cpp | 926 ++++ src/libCom/iocsh/iocsh.h | 81 + src/libCom/iocsh/libComRegister.c | 368 ++ src/libCom/iocsh/libComRegister.h | 23 + src/libCom/iocsh/registry.c | 91 + src/libCom/iocsh/registry.h | 35 + src/libCom/logClient/iocLog.c | 151 + src/libCom/logClient/iocLog.h | 38 + src/libCom/logClient/logClient.c | 557 ++ src/libCom/logClient/logClient.h | 46 + src/libCom/macLib/macCore.c | 941 ++++ src/libCom/macLib/macEnv.c | 57 + src/libCom/macLib/macLib.h | 158 + src/libCom/macLib/macLibNOTES | 160 + src/libCom/macLib/macLibREADME | 170 + src/libCom/macLib/macUtil.c | 286 + src/libCom/misc/aToIPAddr.c | 136 + src/libCom/misc/adjustment.c | 55 + src/libCom/misc/adjustment.h | 28 + src/libCom/misc/cantProceed.c | 70 + src/libCom/misc/cantProceed.h | 29 + src/libCom/misc/compilerDependencies.h | 104 + src/libCom/misc/dbDefs.h | 65 + src/libCom/misc/epicsConvert.c | 45 + src/libCom/misc/epicsConvert.h | 23 + src/libCom/misc/epicsExit.c | 163 + src/libCom/misc/epicsExit.h | 33 + src/libCom/misc/epicsExport.h | 43 + src/libCom/misc/epicsStdlib.c | 91 + src/libCom/misc/epicsStdlib.h | 28 + src/libCom/misc/epicsString.c | 338 ++ src/libCom/misc/epicsString.h | 50 + src/libCom/misc/epicsTypes.h | 240 + src/libCom/misc/epicsUnitTest.c | 263 + src/libCom/misc/epicsUnitTest.h | 49 + src/libCom/misc/ipAddrToAsciiAsynchronous.cpp | 450 ++ src/libCom/misc/ipAddrToAsciiAsynchronous.h | 59 + src/libCom/misc/locationException.h | 77 + src/libCom/misc/makeEpicsVersion.pl | 73 + src/libCom/misc/shareLib.h | 196 + src/libCom/misc/testMain.h | 46 + src/libCom/misc/truncateFile.c | 136 + src/libCom/misc/unixFileName.h | 22 + src/libCom/osi/TODOfuture | 14 + src/libCom/osi/devLib.h | 107 + src/libCom/osi/devLibVME.c | 1159 ++++ src/libCom/osi/devLibVME.h | 312 ++ src/libCom/osi/devLibVMEImpl.h | 104 + src/libCom/osi/epicsAssert.h | 58 + src/libCom/osi/epicsEndian.h | 34 + src/libCom/osi/epicsEvent.cpp | 100 + src/libCom/osi/epicsEvent.h | 74 + src/libCom/osi/epicsFindSymbol.h | 26 + src/libCom/osi/epicsGeneralTime.c | 497 ++ src/libCom/osi/epicsGeneralTime.h | 43 + src/libCom/osi/epicsInterrupt.h | 29 + src/libCom/osi/epicsMath.cpp | 43 + src/libCom/osi/epicsMessageQueue.cpp | 83 + src/libCom/osi/epicsMessageQueue.h | 102 + src/libCom/osi/epicsMutex.cpp | 326 ++ src/libCom/osi/epicsMutex.h | 109 + src/libCom/osi/epicsSignal.h | 46 + src/libCom/osi/epicsStdio.c | 115 + src/libCom/osi/epicsStdio.h | 68 + src/libCom/osi/epicsStdioRedirect.h | 49 + src/libCom/osi/epicsThread.cpp | 358 ++ src/libCom/osi/epicsThread.h | 233 + src/libCom/osi/epicsTime.cpp | 1034 ++++ src/libCom/osi/epicsTime.h | 341 ++ src/libCom/osi/generalTimeSup.h | 47 + src/libCom/osi/os/Darwin/epicsMath.h | 29 + src/libCom/osi/os/Darwin/osdEnv.c | 84 + src/libCom/osi/os/Darwin/osdFindSymbol.c | 27 + src/libCom/osi/os/Darwin/osdSock.h | 82 + src/libCom/osi/os/Darwin/osdSockAddrReuse.cpp | 50 + src/libCom/osi/os/Darwin/osdTime.h | 29 + src/libCom/osi/os/Darwin/osiFileName.h | 18 + src/libCom/osi/os/Linux/osdFindSymbol.c | 27 + src/libCom/osi/os/Linux/osdSock.h | 96 + src/libCom/osi/os/Linux/osdTime.h | 34 + src/libCom/osi/os/Linux/osiFileName.h | 21 + src/libCom/osi/os/Linux/osiUnistd.h | 33 + src/libCom/osi/os/RTEMS/devLibVMEOSD.c | 366 ++ src/libCom/osi/os/RTEMS/epicsMath.h | 28 + src/libCom/osi/os/RTEMS/osdEvent.c | 206 + src/libCom/osi/os/RTEMS/osdEvent.h | 15 + src/libCom/osi/os/RTEMS/osdInterrupt.c | 95 + src/libCom/osi/os/RTEMS/osdInterrupt.h | 13 + src/libCom/osi/os/RTEMS/osdMessageQueue.c | 249 + src/libCom/osi/os/RTEMS/osdMessageQueue.h | 31 + src/libCom/osi/os/RTEMS/osdMutex.c | 196 + src/libCom/osi/os/RTEMS/osdMutex.h | 15 + src/libCom/osi/os/RTEMS/osdPoolStatus.c | 26 + src/libCom/osi/os/RTEMS/osdProcess.c | 52 + src/libCom/osi/os/RTEMS/osdSignal.cpp | 21 + src/libCom/osi/os/RTEMS/osdSock.h | 101 + src/libCom/osi/os/RTEMS/osdStrtod.h | 11 + src/libCom/osi/os/RTEMS/osdThread.c | 700 +++ src/libCom/osi/os/RTEMS/osdThread.h | 12 + src/libCom/osi/os/RTEMS/osdTime.cpp | 133 + src/libCom/osi/os/RTEMS/osdTime.h | 35 + src/libCom/osi/os/RTEMS/osdVME.h | 21 + src/libCom/osi/os/RTEMS/osiFileName.h | 19 + src/libCom/osi/os/RTEMS/osiUnistd.h | 51 + src/libCom/osi/os/WIN32/epicsGetopt.c | 123 + src/libCom/osi/os/WIN32/epicsGetopt.h | 31 + src/libCom/osi/os/WIN32/epicsMath.h | 40 + .../WIN32/epicsSocketConvertErrnoToString.cpp | 47 + src/libCom/osi/os/WIN32/epicsTempFile.cpp | 94 + .../osi/os/WIN32/forceBadAllocException.cpp | 56 + src/libCom/osi/os/WIN32/osdEvent.c | 159 + src/libCom/osi/os/WIN32/osdEvent.h | 20 + src/libCom/osi/os/WIN32/osdMutex.c | 178 + src/libCom/osi/os/WIN32/osdMutex.h | 24 + src/libCom/osi/os/WIN32/osdNetIntf.c | 242 + src/libCom/osi/os/WIN32/osdPoolStatus.c | 23 + src/libCom/osi/os/WIN32/osdPoolStatus.h | 14 + src/libCom/osi/os/WIN32/osdProcess.c | 153 + src/libCom/osi/os/WIN32/osdSignal.cpp | 21 + src/libCom/osi/os/WIN32/osdSock.c | 192 + src/libCom/osi/os/WIN32/osdSock.h | 81 + src/libCom/osi/os/WIN32/osdSockAddrReuse.cpp | 45 + src/libCom/osi/os/WIN32/osdStdio.c | 54 + src/libCom/osi/os/WIN32/osdStrtod.h | 15 + src/libCom/osi/os/WIN32/osdThread.c | 1090 ++++ src/libCom/osi/os/WIN32/osdThread.h | 15 + src/libCom/osi/os/WIN32/osdTime.cpp | 589 +++ src/libCom/osi/os/WIN32/osdTime.h | 28 + src/libCom/osi/os/WIN32/osdWireConfig.h | 16 + src/libCom/osi/os/WIN32/osiFileName.h | 22 + src/libCom/osi/os/WIN32/osiUnistd.h | 26 + src/libCom/osi/os/WIN32/setThreadName.cpp | 51 + src/libCom/osi/os/WIN32/systemCallIntMech.cpp | 23 + src/libCom/osi/os/cygwin32/devLibVMEOSD.c | 15 + src/libCom/osi/os/cygwin32/osdSock.h | 76 + .../osi/os/cygwin32/osdSockAddrReuse.cpp | 45 + src/libCom/osi/os/cygwin32/osdStrtod.h | 15 + src/libCom/osi/os/cygwin32/osiFileName.h | 22 + .../osi/os/cygwin32/systemCallIntMech.cpp | 30 + src/libCom/osi/os/default/devLibVMEOSD.c | 17 + src/libCom/osi/os/default/epicsGetopt.h | 17 + src/libCom/osi/os/default/epicsReadline.c | 372 ++ src/libCom/osi/os/default/epicsReadline.h | 28 + .../epicsSocketConvertErrnoToString.cpp | 35 + src/libCom/osi/os/default/osdAssert.c | 52 + src/libCom/osi/os/default/osdEnv.c | 77 + src/libCom/osi/os/default/osdFindSymbol.c | 21 + src/libCom/osi/os/default/osdInterrupt.c | 58 + src/libCom/osi/os/default/osdInterrupt.h | 15 + src/libCom/osi/os/default/osdMessageQueue.cpp | 345 ++ src/libCom/osi/os/default/osdMessageQueue.h | 3 + src/libCom/osi/os/default/osdNetIntf.c | 325 ++ src/libCom/osi/os/default/osdPoolStatus.c | 23 + src/libCom/osi/os/default/osdPoolStatus.h | 14 + src/libCom/osi/os/default/osdSignal.cpp | 21 + src/libCom/osi/os/default/osdVME.h | 14 + src/libCom/osi/os/default/osdWireConfig.h | 64 + src/libCom/osi/os/default/osdWireFormat.h | 247 + src/libCom/osi/os/freebsd/osdSock.h | 83 + src/libCom/osi/os/freebsd/osdTime.h | 33 + src/libCom/osi/os/freebsd/osiFileName.h | 20 + src/libCom/osi/os/freebsd/osiUnistd.h | 32 + src/libCom/osi/os/iOS/epicsMath.h | 27 + src/libCom/osi/os/iOS/osdEnv.c | 78 + src/libCom/osi/os/iOS/osdSock.h | 82 + src/libCom/osi/os/iOS/osdSockAddrReuse.cpp | 48 + src/libCom/osi/os/iOS/osdTime.h | 30 + src/libCom/osi/os/iOS/osiFileName.h | 18 + src/libCom/osi/os/posix/README | 13 + src/libCom/osi/os/posix/epicsMath.h | 27 + src/libCom/osi/os/posix/epicsTempFile.cpp | 37 + src/libCom/osi/os/posix/osdEvent.c | 175 + src/libCom/osi/os/posix/osdEvent.h | 10 + src/libCom/osi/os/posix/osdMutex.c | 284 + src/libCom/osi/os/posix/osdMutex.h | 10 + src/libCom/osi/os/posix/osdProcess.c | 109 + src/libCom/osi/os/posix/osdSignal.cpp | 57 + src/libCom/osi/os/posix/osdSock.c | 184 + src/libCom/osi/os/posix/osdSockAddrReuse.cpp | 50 + src/libCom/osi/os/posix/osdStdio.c | 31 + src/libCom/osi/os/posix/osdStrtod.h | 15 + src/libCom/osi/os/posix/osdThread.c | 753 +++ src/libCom/osi/os/posix/osdThread.h | 27 + src/libCom/osi/os/posix/osdTime.cpp | 118 + src/libCom/osi/os/posix/osdTime.h | 41 + src/libCom/osi/os/posix/osiUnistd.h | 26 + src/libCom/osi/os/posix/systemCallIntMech.cpp | 23 + src/libCom/osi/os/solaris/epicsMath.h | 31 + src/libCom/osi/os/solaris/osdFindSymbol.c | 27 + src/libCom/osi/os/solaris/osdSock.h | 95 + src/libCom/osi/os/solaris/osdStrtod.h | 15 + src/libCom/osi/os/solaris/osdWireConfig.h | 26 + src/libCom/osi/os/solaris/osiFileName.h | 21 + src/libCom/osi/os/vxWorks/README | 3 + src/libCom/osi/os/vxWorks/atReboot.cpp | 49 + src/libCom/osi/os/vxWorks/camacLib.h | 59 + src/libCom/osi/os/vxWorks/devLibVMEOSD.c | 502 ++ src/libCom/osi/os/vxWorks/epicsDynLink.c | 92 + src/libCom/osi/os/vxWorks/epicsDynLink.h | 51 + src/libCom/osi/os/vxWorks/epicsMath.h | 39 + src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp | 106 + src/libCom/osi/os/vxWorks/module_types.h | 495 ++ src/libCom/osi/os/vxWorks/osdEnv.c | 74 + src/libCom/osi/os/vxWorks/osdEvent.c | 82 + src/libCom/osi/os/vxWorks/osdEvent.h | 25 + src/libCom/osi/os/vxWorks/osdFindSymbol.c | 86 + src/libCom/osi/os/vxWorks/osdInterrupt.c | 29 + src/libCom/osi/os/vxWorks/osdInterrupt.h | 14 + src/libCom/osi/os/vxWorks/osdMessageQueue.cpp | 56 + src/libCom/osi/os/vxWorks/osdMessageQueue.h | 34 + src/libCom/osi/os/vxWorks/osdMutex.c | 49 + src/libCom/osi/os/vxWorks/osdMutex.h | 25 + src/libCom/osi/os/vxWorks/osdPoolStatus.c | 70 + src/libCom/osi/os/vxWorks/osdProcess.c | 58 + src/libCom/osi/os/vxWorks/osdSignal.cpp | 20 + src/libCom/osi/os/vxWorks/osdSock.c | 138 + src/libCom/osi/os/vxWorks/osdSock.h | 105 + src/libCom/osi/os/vxWorks/osdStdio.c | 57 + src/libCom/osi/os/vxWorks/osdStrtod.h | 15 + src/libCom/osi/os/vxWorks/osdThread.c | 404 ++ src/libCom/osi/os/vxWorks/osdThread.h | 13 + src/libCom/osi/os/vxWorks/osdTime.cpp | 80 + src/libCom/osi/os/vxWorks/osdTime.h | 33 + src/libCom/osi/os/vxWorks/osdVME.h | 15 + src/libCom/osi/os/vxWorks/osdWireConfig.h | 25 + src/libCom/osi/os/vxWorks/osiFileName.h | 21 + src/libCom/osi/os/vxWorks/task_params.h | 185 + src/libCom/osi/os/vxWorks/veclist.c | 227 + src/libCom/osi/os/vxWorks/vxComLibrary.c | 29 + src/libCom/osi/osiClockTime.c | 207 + src/libCom/osi/osiClockTime.h | 26 + src/libCom/osi/osiNTPTime.c | 284 + src/libCom/osi/osiNTPTime.h | 23 + src/libCom/osi/osiPoolStatus.h | 41 + src/libCom/osi/osiProcess.h | 46 + src/libCom/osi/osiSock.c | 191 + src/libCom/osi/osiSock.h | 208 + src/libCom/osi/osiWireFormat.h | 264 + src/libCom/ring/epicsRingBytes.c | 176 + src/libCom/ring/epicsRingBytes.h | 47 + src/libCom/ring/epicsRingPointer.cpp | 82 + src/libCom/ring/epicsRingPointer.h | 157 + src/libCom/taskwd/taskwd.c | 417 ++ src/libCom/taskwd/taskwd.h | 68 + src/libCom/test/Makefile | 185 + src/libCom/test/blockingSockTest.cpp | 290 + src/libCom/test/buckTest.c | 82 + src/libCom/test/cvtFastPerform.cpp | 148 + src/libCom/test/epicsAlgorithmTest.cpp | 63 + src/libCom/test/epicsCalcTest.cpp | 862 +++ src/libCom/test/epicsEllTest.c | 197 + src/libCom/test/epicsErrlogTest.c | 291 ++ src/libCom/test/epicsEventTest.cpp | 252 + src/libCom/test/epicsExceptionTest.cpp | 114 + src/libCom/test/epicsExitTest.c | 92 + src/libCom/test/epicsMathTest.c | 73 + src/libCom/test/epicsMaxThreads.c | 56 + src/libCom/test/epicsMessageQueueTest.cpp | 300 ++ src/libCom/test/epicsMutexTest.cpp | 283 + src/libCom/test/epicsRunLibComTests.c | 104 + src/libCom/test/epicsStdioTest.c | 128 + src/libCom/test/epicsStringTest.c | 89 + src/libCom/test/epicsThreadOnceTest.c | 115 + src/libCom/test/epicsThreadPerform.cpp | 228 + src/libCom/test/epicsThreadPriorityTest.cpp | 121 + src/libCom/test/epicsThreadPrivateTest.cpp | 49 + src/libCom/test/epicsThreadTest.cpp | 111 + src/libCom/test/epicsTimeTest.cpp | 190 + src/libCom/test/epicsTimerTest.cpp | 428 ++ src/libCom/test/epicsUnitTestTest.c | 36 + src/libCom/test/epicsUnitTestTest.plt | 29 + src/libCom/test/fdmgrTest.c | 138 + src/libCom/test/macEnvExpandTest.c | 163 + src/libCom/test/macLibTest.c | 221 + src/libCom/test/ringBytesTest.c | 119 + src/libCom/test/ringPointerTest.c | 110 + src/libCom/test/rtemsTestHarness.c | 19 + src/libCom/test/taskwdTest.c | 92 + src/libCom/timer/epicsTimer.cpp | 279 + src/libCom/timer/epicsTimer.h | 185 + src/libCom/timer/timer.cpp | 259 + src/libCom/timer/timerPrivate.h | 281 + src/libCom/timer/timerQueue.cpp | 226 + src/libCom/timer/timerQueueActive.cpp | 153 + src/libCom/timer/timerQueueActiveMgr.cpp | 85 + src/libCom/timer/timerQueuePassive.cpp | 70 + src/libCom/tsDefs/README | 6 + src/libCom/tsDefs/tsDefs.c | 36 + src/libCom/tsDefs/tsDefs.h | 50 + src/makeBaseApp/Makefile | 93 + src/makeBaseApp/makeBaseApp.pl | 508 ++ src/makeBaseApp/top/Makefile | 17 + src/makeBaseApp/top/caClientApp/Makefile | 19 + src/makeBaseApp/top/caClientApp/caExample.c | 25 + src/makeBaseApp/top/caClientApp/caMonitor.c | 127 + src/makeBaseApp/top/caServerApp/Makefile | 34 + src/makeBaseApp/top/caServerApp/README | 63 + src/makeBaseApp/top/caServerApp/exAsyncPV.cc | 229 + src/makeBaseApp/top/caServerApp/exChannel.cc | 41 + src/makeBaseApp/top/caServerApp/exPV.cc | 345 ++ src/makeBaseApp/top/caServerApp/exScalarPV.cc | 120 + src/makeBaseApp/top/caServerApp/exServer.cc | 436 ++ src/makeBaseApp/top/caServerApp/exServer.h | 590 +++ src/makeBaseApp/top/caServerApp/exVectorPV.cc | 266 + src/makeBaseApp/top/caServerApp/main.cc | 151 + src/makeBaseApp/top/caServerApp/test.adl | 844 +++ src/makeBaseApp/top/caServerApp/vxEntry.cc | 80 + src/makeBaseApp/top/configure/CONFIG | 29 + src/makeBaseApp/top/configure/CONFIG_SITE | 33 + src/makeBaseApp/top/configure/Makefile | 8 + src/makeBaseApp/top/configure/RELEASE | 32 + src/makeBaseApp/top/configure/RULES | 6 + src/makeBaseApp/top/configure/RULES.ioc | 2 + src/makeBaseApp/top/configure/RULES_DIRS | 2 + src/makeBaseApp/top/configure/RULES_TOP | 3 + src/makeBaseApp/top/exampleApp/Db/Makefile | 26 + .../top/exampleApp/Db/dbExample1.db | 62 + .../top/exampleApp/Db/dbExample2.db | 40 + .../top/exampleApp/Db/dbSubExample.db | 13 + .../top/exampleApp/Db/user.substitutions | 12 + .../top/exampleApp/Db/userHost.substitutions | 12 + src/makeBaseApp/top/exampleApp/Makefile | 8 + src/makeBaseApp/top/exampleApp/src/Makefile | 83 + .../top/exampleApp/src/_APPNAME_Hello.c | 31 + .../top/exampleApp/src/_APPNAME_Hello.dbd | 1 + .../top/exampleApp/src/_APPNAME_Main.cpp | 23 + .../top/exampleApp/src/dbSubExample.c | 49 + .../top/exampleApp/src/dbSubExample.dbd | 5 + .../top/exampleApp/src/devXxxSoft.c | 58 + .../top/exampleApp/src/initTrace.c | 39 + .../top/exampleApp/src/initTrace.dbd | 1 + .../top/exampleApp/src/sncExample.dbd | 1 + .../top/exampleApp/src/sncExample.stt | 22 + .../top/exampleApp/src/sncProgram.st | 1 + .../top/exampleApp/src/xxxRecord.c | 273 + .../top/exampleApp/src/xxxRecord.dbd | 117 + .../top/exampleApp/src/xxxSupport.dbd | 2 + src/makeBaseApp/top/exampleBoot/Makefile | 6 + .../top/exampleBoot/ioc/Makefile@Common | 5 + .../top/exampleBoot/ioc/Makefile@vxWorks | 5 + .../top/exampleBoot/ioc/Makefile@win32 | 5 + .../top/exampleBoot/ioc/README@Common | 9 + .../top/exampleBoot/ioc/README@RTEMS | 6 + .../top/exampleBoot/ioc/README@vxWorks | 0 .../top/exampleBoot/ioc/st.cmd@Common | 28 + .../top/exampleBoot/ioc/st.cmd@RTEMS | 25 + .../top/exampleBoot/ioc/st.cmd@vxWorks | 35 + .../top/exampleBoot/nfsCommands@RTEMS | 26 + .../top/exampleBoot/nfsCommands@vxWorks | 29 + src/makeBaseApp/top/iocApp/Db/Makefile | 22 + src/makeBaseApp/top/iocApp/Makefile | 8 + src/makeBaseApp/top/iocApp/src/Makefile | 42 + .../top/iocApp/src/_APPNAME_Main.cpp | 23 + src/makeBaseApp/top/iocBoot/Makefile | 6 + .../top/iocBoot/ioc/Makefile@Common | 5 + .../top/iocBoot/ioc/Makefile@vxWorks | 5 + .../top/iocBoot/ioc/Makefile@win32 | 5 + src/makeBaseApp/top/iocBoot/ioc/st.cmd@Common | 21 + src/makeBaseApp/top/iocBoot/ioc/st.cmd@Cross | 18 + src/makeBaseApp/top/iocBoot/ioc/st.cmd@RTEMS | 19 + .../top/iocBoot/ioc/st.cmd@vxWorks | 29 + src/makeBaseApp/top/iocBoot/nfsCommands@RTEMS | 29 + .../top/iocBoot/nfsCommands@vxWorks | 29 + src/makeBaseApp/top/supportApp/Db/Makefile | 22 + src/makeBaseApp/top/supportApp/Makefile | 7 + src/makeBaseApp/top/supportApp/src/Makefile | 28 + .../top/supportApp/src/_APPNAME_.dbd | 6 + src/makeBaseExt/Makefile | 32 + src/makeBaseExt/makeBaseExt.pl | 309 ++ src/makeBaseExt/top/Makefile | 9 + src/makeBaseExt/top/README | 5 + src/makeBaseExt/top/configure/CONFIG | 48 + src/makeBaseExt/top/configure/CONFIG_SITE | 20 + src/makeBaseExt/top/configure/Makefile | 10 + src/makeBaseExt/top/configure/RELEASE | 22 + src/makeBaseExt/top/configure/RULES | 5 + src/makeBaseExt/top/configure/RULES_DIRS | 3 + src/makeBaseExt/top/configure/RULES_IDL | 30 + src/makeBaseExt/top/configure/RULES_PYTHON | 50 + src/makeBaseExt/top/configure/RULES_TOP | 4 + .../configure/os/CONFIG.linux-x86.linux-386 | 2 + ...CONFIG.win32-x86-borland.win32-x86-borland | 13 + .../configure/os/CONFIG_SITE.Common.Common | 12 + .../os/CONFIG_SITE.aix-ppc-gnu.aix-ppc-gnu | 13 + .../configure/os/CONFIG_SITE.aix-ppc.aix-ppc | 79 + .../os/CONFIG_SITE.cygwin-x86.cygwin-x86 | 56 + .../os/CONFIG_SITE.darwin-ppc.darwin-ppc | 83 + .../CONFIG_SITE.darwin-ppcx86.darwin-ppcx86 | 13 + .../os/CONFIG_SITE.darwin-x86.darwin-x86 | 83 + .../CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 | 12 + ...ONFIG_SITE.hpux-parisc-gnu.hpux-parisc-gnu | 13 + .../os/CONFIG_SITE.hpux-parisc.hpux-parisc | 30 + .../os/CONFIG_SITE.interix-x86.interix-x86 | 11 + .../os/CONFIG_SITE.linux-ppc.linux-ppc | 13 + ...G_SITE.linux-x86-borland.linux-x86-borland | 41 + ...ONFIG_SITE.linux-x86-debug.linux-x86-debug | 13 + .../os/CONFIG_SITE.linux-x86.linux-athlon | 14 + .../os/CONFIG_SITE.linux-x86.linux-x86 | 59 + ...SITE.linux-x86_64-debug.linux-x86_64-debug | 13 + .../os/CONFIG_SITE.linux-x86_64.linux-x86_64 | 36 + ...TE.solaris-sparc-debug.solaris-sparc-debug | 14 + ...G_SITE.solaris-sparc-gnu.solaris-sparc-gnu | 13 + .../CONFIG_SITE.solaris-sparc.solaris-sparc | 92 + ...TE.solaris-sparc64-gnu.solaris-sparc64-gnu | 14 + ...ONFIG_SITE.solaris-sparc64.solaris-sparc64 | 26 + ...G_SITE.solaris-x86-debug.solaris-x86-debug | 8 + ...ONFIG_SITE.solaris-x86-gnu.solaris-x86-gnu | 13 + .../os/CONFIG_SITE.solaris-x86.solaris-x86 | 22 + .../CONFIG_SITE.solaris-x86_64.solaris-x86_64 | 16 + ...G_SITE.win32-x86-borland.win32-x86-borland | 109 + ...FIG_SITE.win32-x86-cygwin.win32-x86-cygwin | 14 + ...ONFIG_SITE.win32-x86-debug.win32-x86-debug | 14 + ...ONFIG_SITE.win32-x86-mingw.win32-x86-mingw | 14 + .../os/CONFIG_SITE.win32-x86.win32-x86 | 216 + .../os/CONFIG_SITE.windows-x64.windows-x64 | 8 + src/makeBaseExt/top/exampleExt/Makefile | 13 + .../top/exampleExt/RELEASE_NOTES.HTM | 16 + src/makeBaseExt/top/exampleExt/caExample.c | 25 + src/makeBaseExt/top/simpleExt/Makefile | 21 + src/makeBaseExt/top/src/Makefile | 9 + src/misc/Makefile | 42 + src/misc/asSubRecordFunctions.c | 91 + src/misc/base.dbd | 50 + src/misc/dlload.c | 25 + src/misc/dlload.dbd | 3 + src/misc/epicsRelease.c | 30 + src/misc/epicsRelease.h | 27 + src/misc/iocInit.c | 532 ++ src/misc/iocInit.h | 30 + src/misc/iocshRegisterCommon.c | 36 + src/misc/iocshRegisterCommon.h | 28 + src/misc/misc.rc | 36 + src/misc/miscIoc.rc | 36 + src/misc/miscIocRegister.c | 88 + src/misc/miscIocRegister.h | 25 + src/misc/system.dbd | 3 + src/rec/Makefile | 84 + src/rec/aSubRecord.c | 494 ++ src/rec/aSubRecord.dbd | 1636 ++++++ src/rec/aaiRecord.c | 322 ++ src/rec/aaiRecord.dbd | 110 + src/rec/aaoRecord.c | 320 ++ src/rec/aaoRecord.dbd | 110 + src/rec/aiRecord.c | 460 ++ src/rec/aiRecord.dbd | 233 + src/rec/aoRecord.c | 554 ++ src/rec/aoRecord.dbd | 294 ++ src/rec/biRecord.c | 292 ++ src/rec/biRecord.dbd | 106 + src/rec/boRecord.c | 407 ++ src/rec/boRecord.dbd | 151 + src/rec/calcRecord.c | 362 ++ src/rec/calcRecord.dbd | 301 ++ src/rec/calcoutRecord.c | 713 +++ src/rec/calcoutRecord.dbd | 506 ++ src/rec/compressRecord.c | 443 ++ src/rec/compressRecord.dbd | 131 + src/rec/dfanoutRecord.c | 333 ++ src/rec/dfanoutRecord.dbd | 192 + src/rec/eventRecord.c | 194 + src/rec/eventRecord.dbd | 46 + src/rec/fanoutRecord.c | 142 + src/rec/fanoutRecord.dbd | 68 + src/rec/histogramRecord.c | 427 ++ src/rec/histogramRecord.dbd | 137 + src/rec/longinRecord.c | 333 ++ src/rec/longinRecord.dbd | 145 + src/rec/longoutRecord.c | 377 ++ src/rec/longoutRecord.dbd | 176 + src/rec/mbbiDirectRecord.c | 278 + src/rec/mbbiDirectRecord.dbd | 167 + src/rec/mbbiRecord.c | 368 ++ src/rec/mbbiRecord.dbd | 469 ++ src/rec/mbboDirectRecord.c | 359 ++ src/rec/mbboDirectRecord.dbd | 225 + src/rec/mbboRecord.c | 459 ++ src/rec/mbboRecord.dbd | 498 ++ src/rec/permissiveRecord.c | 115 + src/rec/permissiveRecord.dbd | 38 + src/rec/recIoc.rc | 36 + src/rec/selRecord.c | 441 ++ src/rec/selRecord.dbd | 312 ++ src/rec/seqRecord.c | 420 ++ src/rec/seqRecord.dbd | 233 + src/rec/stateRecord.c | 105 + src/rec/stateRecord.dbd | 25 + src/rec/stringinRecord.c | 208 + src/rec/stringinRecord.dbd | 72 + src/rec/stringoutRecord.c | 233 + src/rec/stringoutRecord.dbd | 90 + src/rec/subArrayRecord.c | 309 ++ src/rec/subArrayRecord.dbd | 84 + src/rec/subRecord.c | 416 ++ src/rec/subRecord.dbd | 316 ++ src/rec/waveformRecord.c | 314 ++ src/rec/waveformRecord.dbd | 119 + src/registry/Makefile | 40 + src/registry/registerRecordDeviceDriver.pl | 229 + src/registry/registryCommon.c | 80 + src/registry/registryCommon.h | 32 + src/registry/registryDeviceSupport.c | 38 + src/registry/registryDeviceSupport.h | 30 + src/registry/registryDriverSupport.c | 38 + src/registry/registryDriverSupport.h | 31 + src/registry/registryFunction.c | 58 + src/registry/registryFunction.h | 39 + src/registry/registryIoc.rc | 36 + src/registry/registryIocRegister.c | 56 + src/registry/registryIocRegister.h | 25 + src/registry/registryRecordType.c | 37 + src/registry/registryRecordType.h | 44 + src/rsrv/Makefile | 39 + src/rsrv/camessage.c | 2605 +++++++++ src/rsrv/camsgtask.c | 199 + src/rsrv/caserverio.c | 418 ++ src/rsrv/caservertask.c | 967 ++++ src/rsrv/cast_server.c | 316 ++ src/rsrv/online_notify.c | 302 ++ src/rsrv/rsrv.h | 39 + src/rsrv/rsrvIoc.rc | 36 + src/rsrv/rsrvIocRegister.c | 28 + src/rsrv/rsrvIocRegister.h | 25 + src/rsrv/server.h | 232 + src/softIoc/Makefile | 39 + src/softIoc/makeInstallDir.pl | 25 + src/softIoc/softIocExit.db | 7 + src/softIoc/softMain.cpp | 235 + src/tools/EPICS/Copy.pm | 75 + src/tools/EPICS/Getopts.pm | 76 + src/tools/EPICS/Path.pm | 145 + src/tools/EPICS/Release.pm | 113 + src/tools/Makefile | 36 + src/tools/convertRelease.pl | 229 + src/tools/cvsclean.pl | 21 + src/tools/dos2unix.pl | 35 + src/tools/expandVars.pl | 71 + src/tools/filterWarnings.pl | 54 + src/tools/fullPathName.pl | 37 + src/tools/installEpics.pl | 118 + src/tools/makeDbDepends.pl | 33 + src/tools/makeIncludeDbd.pl | 43 + src/tools/makeMakefile.pl | 53 + src/tools/makeTestfile.pl | 31 + src/tools/mkmf.pl | 149 + src/tools/munch.pl | 137 + src/tools/replaceVAR.pl | 21 + src/tools/useManifestTool.pl | 35 + src/toolsComm/Makefile | 20 + src/toolsComm/antelope/ACKNOWLEDGEMENTS | 25 + src/toolsComm/antelope/EPICS_READ_THIS | 54 + src/toolsComm/antelope/Makefile | 29 + src/toolsComm/antelope/NEW_FEATURES | 46 + src/toolsComm/antelope/NOTES | 9 + src/toolsComm/antelope/NO_WARRANTY | 3 + src/toolsComm/antelope/README | 23 + src/toolsComm/antelope/closure.c | 274 + src/toolsComm/antelope/defs.h | 367 ++ src/toolsComm/antelope/error.c | 314 ++ src/toolsComm/antelope/lalr.c | 668 +++ src/toolsComm/antelope/lr0.c | 613 +++ src/toolsComm/antelope/main.c | 341 ++ src/toolsComm/antelope/mkpar.c | 372 ++ src/toolsComm/antelope/output.c | 1247 +++++ src/toolsComm/antelope/reader.c | 1805 +++++++ src/toolsComm/antelope/skeleton.c | 302 ++ src/toolsComm/antelope/symtab.c | 126 + src/toolsComm/antelope/verbose.c | 349 ++ src/toolsComm/antelope/warshall.c | 88 + src/toolsComm/antelope/yacc.html | 135 + src/toolsComm/flex/COPYING | 38 + src/toolsComm/flex/Changes | 345 ++ src/toolsComm/flex/EPICS_READ_THIS | 32 + src/toolsComm/flex/Flex.doc | 1792 +++++++ src/toolsComm/flex/Makefile | 44 + src/toolsComm/flex/README | 78 + src/toolsComm/flex/ccl.c | 174 + src/toolsComm/flex/dfa.c | 1061 ++++ src/toolsComm/flex/ecs.c | 347 ++ src/toolsComm/flex/flex.html | 620 +++ src/toolsComm/flex/flex.skel | 750 +++ src/toolsComm/flex/flex.skel.static | 752 +++ src/toolsComm/flex/flexdef.h | 839 +++ src/toolsComm/flex/flexdoc.html | 1890 +++++++ src/toolsComm/flex/gen.c | 1324 +++++ src/toolsComm/flex/libmain.c | 18 + src/toolsComm/flex/main.c | 754 +++ src/toolsComm/flex/misc.c | 776 +++ src/toolsComm/flex/nfa.c | 694 +++ src/toolsComm/flex/parse.y | 707 +++ src/toolsComm/flex/scan.c | 2330 +++++++++ src/toolsComm/flex/scan.l.DISTRIB | 525 ++ src/toolsComm/flex/sym.c | 295 ++ src/toolsComm/flex/tblcmp.c | 909 ++++ src/toolsComm/flex/yylex.c | 222 + src/util/Makefile | 38 + src/util/ca_test.c | 326 ++ src/util/ca_test.h | 23 + src/util/ca_test_main.c | 55 + src/util/iocLogServer.c | 1004 ++++ src/util/rc2.caRepeater | 28 + src/util/rc2.logServer | 29 + src/vxWorks/Makefile | 33 + src/vxWorks/registerRecordDeviceDriver.c | 118 + startup/EpicsHostArch | 82 + startup/EpicsHostArch.pl | 42 + startup/Site.cshrc | 118 + startup/Site.profile | 118 + startup/cygwin.bat | 89 + startup/win32.bat | 100 + 1557 files changed, 241151 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 config/CONFIG create mode 100644 config/CONFIG.Host.Borland create mode 100644 config/CONFIG.Host.Darwin create mode 100644 config/CONFIG.Host.Linux create mode 100644 config/CONFIG.Host.UnixCommon create mode 100644 config/CONFIG.Host.WIN32 create mode 100644 config/CONFIG.Host.cygwin-x86 create mode 100644 config/CONFIG.Host.cygwin32 create mode 100644 config/CONFIG.Host.darwin-ppc create mode 100644 config/CONFIG.Host.darwin-ppcx86 create mode 100644 config/CONFIG.Host.darwin-x86 create mode 100644 config/CONFIG.Host.freebsd create mode 100644 config/CONFIG.Host.freebsd-x86 create mode 100644 config/CONFIG.Host.freebsd-x86_64 create mode 100644 config/CONFIG.Host.hp700 create mode 100644 config/CONFIG.Host.hpux-parisc create mode 100644 config/CONFIG.Host.hpux-parisc-gnu create mode 100644 config/CONFIG.Host.linux-ppc create mode 100644 config/CONFIG.Host.linux-x86 create mode 100644 config/CONFIG.Host.linux-x86-debug create mode 100644 config/CONFIG.Host.linux-x86_64 create mode 100644 config/CONFIG.Host.linux-x86_64-debug create mode 100644 config/CONFIG.Host.sgi create mode 100644 config/CONFIG.Host.solaris create mode 100644 config/CONFIG.Host.solaris-sparc create mode 100644 config/CONFIG.Host.solaris-sparc-debug create mode 100644 config/CONFIG.Host.solaris-sparc-gnu create mode 100644 config/CONFIG.Host.solaris-sparc-staticlib create mode 100644 config/CONFIG.Host.solaris-sparc64 create mode 100644 config/CONFIG.Host.solaris-sparc64-gnu create mode 100644 config/CONFIG.Host.solaris-x86 create mode 100644 config/CONFIG.Host.solaris-x86-gnu create mode 100644 config/CONFIG.Host.solarisGnu create mode 100644 config/CONFIG.Host.sun4 create mode 100644 config/CONFIG.Host.sun4-sparc create mode 100644 config/CONFIG.Host.win32-x86 create mode 100644 config/CONFIG.Host.win32-x86-borland create mode 100644 config/CONFIG.Host.win32-x86-cygwin create mode 100644 config/CONFIG.Host.win32-x86-debug create mode 100644 config/CONFIG.Host.win32-x86-mingw create mode 100644 config/CONFIG.Vx create mode 100644 config/CONFIG.Vx.frc40 create mode 100644 config/CONFIG.Vx.frc5ce create mode 100644 config/CONFIG.Vx.hkbaja47 create mode 100644 config/CONFIG.Vx.hkbaja60 create mode 100644 config/CONFIG.Vx.hkv2f create mode 100644 config/CONFIG.Vx.mv147 create mode 100644 config/CONFIG.Vx.mv1604 create mode 100644 config/CONFIG.Vx.mv162 create mode 100644 config/CONFIG.Vx.mv162lc create mode 100644 config/CONFIG.Vx.mv167 create mode 100644 config/CONFIG.Vx.mv177 create mode 100644 config/CONFIG.Vx.mv2700 create mode 100644 config/CONFIG.Vx.niCpu030 create mode 100644 config/CONFIG.Vx.pc486 create mode 100644 config/CONFIG.Vx.pcPentium create mode 100644 config/CONFIG.Vx.ppc603 create mode 100644 config/CONFIG.Vx.ppc603_long create mode 100644 config/CONFIG.Vx.ppc604 create mode 100644 config/CONFIG.Vx.ppc604_long create mode 100644 config/CONFIG.Vx.sbs_pc6 create mode 100644 config/CONFIG.Vx.simpc create mode 100644 config/CONFIG.Vx.vxWorks-486 create mode 100644 config/CONFIG.Vx.vxWorks-68040 create mode 100644 config/CONFIG.Vx.vxWorks-68040lc create mode 100644 config/CONFIG.Vx.vxWorks-68060 create mode 100644 config/CONFIG.Vx.vxWorks-ppc603 create mode 100644 config/CONFIG.Vx.vxWorks-ppc603_long create mode 100644 config/CONFIG.Vx.vxWorks-ppc604 create mode 100644 config/CONFIG.Vx.vxWorks-ppc604_long create mode 100644 config/CONFIG.Vx.vxipc create mode 100644 config/CONFIG_BASE create mode 100644 config/CONFIG_BASE_VERSION create mode 100644 config/CONFIG_COMMON create mode 100644 config/CONFIG_COMPAT create mode 100644 config/CONFIG_ENV create mode 100644 config/CONFIG_HOST_ARCH.Borland create mode 100644 config/CONFIG_HOST_ARCH.Darwin create mode 100644 config/CONFIG_HOST_ARCH.Linux create mode 100644 config/CONFIG_HOST_ARCH.UnixCommon create mode 100644 config/CONFIG_HOST_ARCH.WIN32 create mode 100644 config/CONFIG_HOST_ARCH.cygwin-x86 create mode 100644 config/CONFIG_HOST_ARCH.cygwin32 create mode 100644 config/CONFIG_HOST_ARCH.darwin-ppc create mode 100644 config/CONFIG_HOST_ARCH.darwin-ppcx86 create mode 100644 config/CONFIG_HOST_ARCH.darwin-x86 create mode 100644 config/CONFIG_HOST_ARCH.freebsd-x86_64 create mode 100644 config/CONFIG_HOST_ARCH.hp700 create mode 100644 config/CONFIG_HOST_ARCH.hpux-parisc create mode 100644 config/CONFIG_HOST_ARCH.hpux-parisc-gnu create mode 100644 config/CONFIG_HOST_ARCH.linux-ppc create mode 100644 config/CONFIG_HOST_ARCH.linux-x86 create mode 100644 config/CONFIG_HOST_ARCH.linux-x86-debug create mode 100644 config/CONFIG_HOST_ARCH.linux-x86_64 create mode 100644 config/CONFIG_HOST_ARCH.linux-x86_64-debug create mode 100644 config/CONFIG_HOST_ARCH.sgi create mode 100644 config/CONFIG_HOST_ARCH.solaris create mode 100644 config/CONFIG_HOST_ARCH.solaris-sparc create mode 100644 config/CONFIG_HOST_ARCH.solaris-sparc-debug create mode 100644 config/CONFIG_HOST_ARCH.solaris-sparc-gnu create mode 100644 config/CONFIG_HOST_ARCH.solaris-sparc-staticlib create mode 100644 config/CONFIG_HOST_ARCH.solaris-sparc64 create mode 100644 config/CONFIG_HOST_ARCH.solaris-sparc64-gnu create mode 100644 config/CONFIG_HOST_ARCH.solaris-x86 create mode 100644 config/CONFIG_HOST_ARCH.solaris-x86-gnu create mode 100644 config/CONFIG_HOST_ARCH.solarisGnu create mode 100644 config/CONFIG_HOST_ARCH.sun4 create mode 100644 config/CONFIG_HOST_ARCH.sun4-sparc create mode 100644 config/CONFIG_HOST_ARCH.win32-x86 create mode 100644 config/CONFIG_HOST_ARCH.win32-x86-borland create mode 100644 config/CONFIG_HOST_ARCH.win32-x86-cygwin create mode 100644 config/CONFIG_HOST_ARCH.win32-x86-debug create mode 100644 config/CONFIG_HOST_ARCH.win32-x86-mingw create mode 100644 config/CONFIG_SITE create mode 100644 config/CONFIG_SITE.Host.Borland create mode 100644 config/CONFIG_SITE.Host.WIN32 create mode 100644 config/CONFIG_SITE.Host.darwin-ppc create mode 100644 config/CONFIG_SITE.Host.darwin-x86 create mode 100644 config/CONFIG_SITE.Host.hp700 create mode 100644 config/CONFIG_SITE.Host.hpux-parisc create mode 100644 config/CONFIG_SITE.Host.hpux-parisc-gnu create mode 100644 config/CONFIG_SITE.Host.linux-x86 create mode 100644 config/CONFIG_SITE.Host.solaris create mode 100644 config/CONFIG_SITE.Host.solaris-sparc create mode 100644 config/CONFIG_SITE.Host.solaris-sparc-debug create mode 100644 config/CONFIG_SITE.Host.solaris-sparc-gnu create mode 100644 config/CONFIG_SITE.Host.solaris-sparc-staticlib create mode 100644 config/CONFIG_SITE.Host.solaris-sparc64 create mode 100644 config/CONFIG_SITE.Host.win32-x86 create mode 100644 config/CONFIG_SITE.Host.win32-x86-borland create mode 100644 config/CONFIG_SITE.Vx.Linux create mode 100644 config/CONFIG_SITE.Vx.Linux.mv167 create mode 100644 config/CONFIG_SITE.Vx.Linux.ppc603 create mode 100644 config/CONFIG_SITE.Vx.linux-x86 create mode 100644 config/CONFIG_SITE.Vx.ppc604 create mode 100644 config/CONFIG_SITE_ENV create mode 100644 config/Makefile create mode 100644 config/RULES.Db create mode 100644 config/RULES.Host create mode 100644 config/RULES.Unix create mode 100644 config/RULES.Vx create mode 100644 config/RULES_ARCHS create mode 100644 config/RULES_DIRS create mode 100644 config/RULES_TOP create mode 100644 config/Sample.Makefile.Host create mode 100755 config/tools/cp.pl create mode 100755 config/tools/findBase.pl create mode 100755 config/tools/installEpics create mode 100755 config/tools/installEpics.pl create mode 100755 config/tools/makeMakefile.pl create mode 100755 config/tools/mkdir.pl create mode 100755 config/tools/munch.pl create mode 100755 config/tools/mv.pl create mode 100755 config/tools/rm.pl create mode 100755 config/tools/useManifestTool.pl create mode 100644 configure/CONFIG create mode 100644 configure/CONFIG.CrossCommon create mode 100644 configure/CONFIG.gnuCommon create mode 100644 configure/CONFIG_ADDONS create mode 100644 configure/CONFIG_APP_INCLUDE create mode 100644 configure/CONFIG_BASE create mode 100644 configure/CONFIG_BASE_VERSION create mode 100644 configure/CONFIG_COMMON create mode 100644 configure/CONFIG_ENV create mode 100644 configure/CONFIG_FILE_TYPE create mode 100644 configure/CONFIG_SITE create mode 100644 configure/CONFIG_SITE_ENV create mode 100644 configure/Makefile create mode 100644 configure/RELEASE create mode 100644 configure/RULES create mode 100644 configure/RULES.Db create mode 100644 configure/RULES.ioc create mode 100644 configure/RULES_ARCHS create mode 100644 configure/RULES_BUILD create mode 100644 configure/RULES_DIRS create mode 100644 configure/RULES_EXPAND create mode 100644 configure/RULES_FILE_TYPE create mode 100644 configure/RULES_JAVA create mode 100644 configure/RULES_OCTAVE create mode 100755 configure/RULES_TARGET create mode 100644 configure/RULES_TOP create mode 100755 configure/Sample.Makefile create mode 100644 configure/os/CONFIG.Common.RTEMS create mode 100644 configure/os/CONFIG.Common.RTEMS-at91rm9200ek create mode 100644 configure/os/CONFIG.Common.RTEMS-beatnik create mode 100644 configure/os/CONFIG.Common.RTEMS-gen68360 create mode 100644 configure/os/CONFIG.Common.RTEMS-mcp750 create mode 100644 configure/os/CONFIG.Common.RTEMS-mvme167 create mode 100644 configure/os/CONFIG.Common.RTEMS-mvme2100 create mode 100644 configure/os/CONFIG.Common.RTEMS-mvme2700 create mode 100644 configure/os/CONFIG.Common.RTEMS-mvme3100 create mode 100644 configure/os/CONFIG.Common.RTEMS-mvme5500 create mode 100644 configure/os/CONFIG.Common.RTEMS-pc386 create mode 100644 configure/os/CONFIG.Common.RTEMS-psim create mode 100644 configure/os/CONFIG.Common.RTEMS-uC5282 create mode 100644 configure/os/CONFIG.Common.UnixCommon create mode 100644 configure/os/CONFIG.Common.aix-ppc create mode 100644 configure/os/CONFIG.Common.aix-ppc-gnu create mode 100644 configure/os/CONFIG.Common.cygwin-x86 create mode 100644 configure/os/CONFIG.Common.darwin-ppc create mode 100644 configure/os/CONFIG.Common.darwin-ppcx86 create mode 100644 configure/os/CONFIG.Common.darwin-x86 create mode 100644 configure/os/CONFIG.Common.freebsd-x86 create mode 100644 configure/os/CONFIG.Common.freebsd-x86_64 create mode 100644 configure/os/CONFIG.Common.freebsdCommon create mode 100644 configure/os/CONFIG.Common.ios-arm create mode 100644 configure/os/CONFIG.Common.ios-x86 create mode 100644 configure/os/CONFIG.Common.iosCommon create mode 100644 configure/os/CONFIG.Common.linux-386 create mode 100644 configure/os/CONFIG.Common.linux-486 create mode 100644 configure/os/CONFIG.Common.linux-586 create mode 100644 configure/os/CONFIG.Common.linux-686 create mode 100644 configure/os/CONFIG.Common.linux-arm create mode 100644 configure/os/CONFIG.Common.linux-arm_eb create mode 100644 configure/os/CONFIG.Common.linux-arm_el create mode 100644 configure/os/CONFIG.Common.linux-athlon create mode 100644 configure/os/CONFIG.Common.linux-cris create mode 100644 configure/os/CONFIG.Common.linux-ppc create mode 100644 configure/os/CONFIG.Common.linux-ppc64 create mode 100644 configure/os/CONFIG.Common.linux-x86 create mode 100644 configure/os/CONFIG.Common.linux-x86-borland create mode 100644 configure/os/CONFIG.Common.linux-x86-debug create mode 100644 configure/os/CONFIG.Common.linux-x86_64 create mode 100644 configure/os/CONFIG.Common.linux-x86_64-debug create mode 100644 configure/os/CONFIG.Common.linux-xscale_be create mode 100644 configure/os/CONFIG.Common.linuxCommon create mode 100644 configure/os/CONFIG.Common.solaris-sparc create mode 100644 configure/os/CONFIG.Common.solaris-sparc-debug create mode 100644 configure/os/CONFIG.Common.solaris-sparc-gnu create mode 100644 configure/os/CONFIG.Common.solaris-sparc64 create mode 100644 configure/os/CONFIG.Common.solaris-sparc64-gnu create mode 100644 configure/os/CONFIG.Common.solaris-x86 create mode 100644 configure/os/CONFIG.Common.solaris-x86-gnu create mode 100644 configure/os/CONFIG.Common.solaris-x86_64 create mode 100644 configure/os/CONFIG.Common.solaris-x86_64-gnu create mode 100644 configure/os/CONFIG.Common.vxWorks-486 create mode 100644 configure/os/CONFIG.Common.vxWorks-486-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-68040 create mode 100644 configure/os/CONFIG.Common.vxWorks-68040-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-68040lc create mode 100644 configure/os/CONFIG.Common.vxWorks-68040lc-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-68060 create mode 100644 configure/os/CONFIG.Common.vxWorks-68060-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-mpc8540 create mode 100644 configure/os/CONFIG.Common.vxWorks-mpc8540-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-pentium create mode 100644 configure/os/CONFIG.Common.vxWorks-pentium-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc603 create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc603-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc603_long create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc603_long-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc604 create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc604-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc604_altivec create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc604_altivec-debug create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc604_long create mode 100644 configure/os/CONFIG.Common.vxWorks-ppc604_long-debug create mode 100644 configure/os/CONFIG.Common.vxWorksCommon create mode 100644 configure/os/CONFIG.Common.win32-x86-cygwin create mode 100644 configure/os/CONFIG.Common.win32-x86-mingw create mode 100644 configure/os/CONFIG.UnixCommon.Common create mode 100644 configure/os/CONFIG.aix-ppc-gnu.Common create mode 100644 configure/os/CONFIG.aix-ppc-gnu.aix-ppc-gnu create mode 100644 configure/os/CONFIG.aix-ppc.Common create mode 100644 configure/os/CONFIG.cygwin-x86.Common create mode 100644 configure/os/CONFIG.cygwin-x86.cygwin-x86 create mode 100644 configure/os/CONFIG.cygwin-x86.cygwin-x86-debug create mode 100644 configure/os/CONFIG.darwin-ppc.Common create mode 100644 configure/os/CONFIG.darwin-ppc.darwin-ppc-debug create mode 100644 configure/os/CONFIG.darwin-ppcx86.Common create mode 100644 configure/os/CONFIG.darwin-x86.Common create mode 100644 configure/os/CONFIG.darwin-x86.darwin-x86-debug create mode 100644 configure/os/CONFIG.darwinCommon.darwinCommon create mode 100644 configure/os/CONFIG.freebsd-x86.Common create mode 100644 configure/os/CONFIG.freebsd-x86.freebsd-x86 create mode 100644 configure/os/CONFIG.freebsd-x86_64.Common create mode 100644 configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 create mode 100644 configure/os/CONFIG.linux-ppc.Common create mode 100644 configure/os/CONFIG.linux-ppc.linux-ppc create mode 100644 configure/os/CONFIG.linux-ppc64.Common create mode 100644 configure/os/CONFIG.linux-ppc64.linux-ppc64 create mode 100644 configure/os/CONFIG.linux-x86-borland.Common create mode 100644 configure/os/CONFIG.linux-x86-borland.linux-x86-borland create mode 100644 configure/os/CONFIG.linux-x86-debug.Common create mode 100644 configure/os/CONFIG.linux-x86-debug.linux-x86-debug create mode 100644 configure/os/CONFIG.linux-x86.Common create mode 100644 configure/os/CONFIG.linux-x86.linux-arm create mode 100644 configure/os/CONFIG.linux-x86.linux-arm_eb create mode 100644 configure/os/CONFIG.linux-x86.linux-arm_el create mode 100644 configure/os/CONFIG.linux-x86.linux-cris create mode 100644 configure/os/CONFIG.linux-x86.linux-cris_v10 create mode 100644 configure/os/CONFIG.linux-x86.linux-cris_v32 create mode 100644 configure/os/CONFIG.linux-x86.linux-x86 create mode 100644 configure/os/CONFIG.linux-x86.linux-x86-debug create mode 100644 configure/os/CONFIG.linux-x86_64-debug.Common create mode 100644 configure/os/CONFIG.linux-x86_64-debug.linux-x86_64-debug create mode 100644 configure/os/CONFIG.linux-x86_64.Common create mode 100644 configure/os/CONFIG.linux-x86_64.linux-x86_64 create mode 100644 configure/os/CONFIG.linux-x86_64.linux-x86_64-debug create mode 100644 configure/os/CONFIG.solaris-sparc-debug.Common create mode 100644 configure/os/CONFIG.solaris-sparc-debug.solaris-sparc-debug create mode 100644 configure/os/CONFIG.solaris-sparc-gnu.Common create mode 100644 configure/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu create mode 100644 configure/os/CONFIG.solaris-sparc.Common create mode 100644 configure/os/CONFIG.solaris-sparc.solaris-sparc create mode 100644 configure/os/CONFIG.solaris-sparc.solaris-sparc-debug create mode 100644 configure/os/CONFIG.solaris-sparc.solaris-sparc-gnu create mode 100644 configure/os/CONFIG.solaris-sparc.solaris-sparc64 create mode 100644 configure/os/CONFIG.solaris-sparc.solaris-sparc64-gnu create mode 100644 configure/os/CONFIG.solaris-sparc64-gnu.Common create mode 100644 configure/os/CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu create mode 100644 configure/os/CONFIG.solaris-sparc64.Common create mode 100644 configure/os/CONFIG.solaris-sparc64.solaris-sparc64 create mode 100644 configure/os/CONFIG.solaris-sparc64.solaris-sparc64-debug create mode 100644 configure/os/CONFIG.solaris-x86-gnu.Common create mode 100644 configure/os/CONFIG.solaris-x86-gnu.solaris-x86-gnu create mode 100644 configure/os/CONFIG.solaris-x86.Common create mode 100644 configure/os/CONFIG.solaris-x86.solaris-x86 create mode 100644 configure/os/CONFIG.solaris-x86.solaris-x86-debug create mode 100644 configure/os/CONFIG.solaris-x86.solaris-x86_64 create mode 100644 configure/os/CONFIG.solaris-x86_64-gnu.Common create mode 100644 configure/os/CONFIG.solaris-x86_64-gnu.solaris-x86_64-gnu create mode 100644 configure/os/CONFIG.solaris-x86_64.Common create mode 100644 configure/os/CONFIG.solaris-x86_64.solaris-x86_64 create mode 100644 configure/os/CONFIG.solaris-x86_64.solaris-x86_64-debug create mode 100644 configure/os/CONFIG.solarisCommon.solarisCommon create mode 100644 configure/os/CONFIG.win32-x86-borland.Common create mode 100644 configure/os/CONFIG.win32-x86-borland.win32-x86-borland create mode 100644 configure/os/CONFIG.win32-x86-cygwin.Common create mode 100644 configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin create mode 100644 configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin-debug create mode 100644 configure/os/CONFIG.win32-x86-debug.Common create mode 100644 configure/os/CONFIG.win32-x86-debug.win32-x86-debug create mode 100644 configure/os/CONFIG.win32-x86-mingw.Common create mode 100644 configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw create mode 100644 configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw-debug create mode 100644 configure/os/CONFIG.win32-x86.Common create mode 100644 configure/os/CONFIG.win32-x86.win32-x86 create mode 100644 configure/os/CONFIG.win32-x86.win32-x86-debug create mode 100644 configure/os/CONFIG.win32-x86.windows-x64 create mode 100644 configure/os/CONFIG.windows-x64-debug.Common create mode 100644 configure/os/CONFIG.windows-x64-debug.windows-x64-debug create mode 100644 configure/os/CONFIG.windows-x64.Common create mode 100644 configure/os/CONFIG.windows-x64.windows-x64 create mode 100644 configure/os/CONFIG_COMPAT create mode 100644 configure/os/CONFIG_SITE.Common.RTEMS create mode 100644 configure/os/CONFIG_SITE.Common.RTEMS-pc386 create mode 100644 configure/os/CONFIG_SITE.Common.cygwin-x86 create mode 100644 configure/os/CONFIG_SITE.Common.darwin-ppc create mode 100644 configure/os/CONFIG_SITE.Common.darwin-ppcx86 create mode 100644 configure/os/CONFIG_SITE.Common.darwin-x86 create mode 100644 configure/os/CONFIG_SITE.Common.iosCommon create mode 100644 configure/os/CONFIG_SITE.Common.linux-cris create mode 100644 configure/os/CONFIG_SITE.Common.linux-x86 create mode 100644 configure/os/CONFIG_SITE.Common.linux-x86_64 create mode 100644 configure/os/CONFIG_SITE.Common.solaris-sparc create mode 100644 configure/os/CONFIG_SITE.Common.solaris-sparc-gnu create mode 100644 configure/os/CONFIG_SITE.Common.solaris-sparc64 create mode 100644 configure/os/CONFIG_SITE.Common.solaris-sparc64-gnu create mode 100644 configure/os/CONFIG_SITE.Common.solaris-x86-gnu create mode 100644 configure/os/CONFIG_SITE.Common.solaris-x86_64 create mode 100644 configure/os/CONFIG_SITE.Common.solaris-x86_64-gnu create mode 100644 configure/os/CONFIG_SITE.Common.vxWorks-mpc8540 create mode 100644 configure/os/CONFIG_SITE.Common.vxWorks-ppc603 create mode 100644 configure/os/CONFIG_SITE.Common.vxWorks-ppc603_long create mode 100644 configure/os/CONFIG_SITE.Common.vxWorks-ppc604 create mode 100644 configure/os/CONFIG_SITE.Common.vxWorks-ppc604_altivec create mode 100644 configure/os/CONFIG_SITE.Common.vxWorks-ppc604_long create mode 100644 configure/os/CONFIG_SITE.Common.vxWorksCommon create mode 100644 configure/os/CONFIG_SITE.Common.win32-x86-cygwin create mode 100644 configure/os/CONFIG_SITE.Common.win32-x86-mingw create mode 100644 configure/os/CONFIG_SITE.cygwin-x86.Common create mode 100644 configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 create mode 100644 configure/os/CONFIG_SITE.darwin-ppc.Common create mode 100644 configure/os/CONFIG_SITE.darwin-ppcx86.Common create mode 100644 configure/os/CONFIG_SITE.darwin-x86.Common create mode 100644 configure/os/CONFIG_SITE.linux-x86-borland.Common create mode 100644 configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug create mode 100644 configure/os/CONFIG_SITE.linux-x86.Common create mode 100644 configure/os/CONFIG_SITE.linux-x86.RTEMS create mode 100644 configure/os/CONFIG_SITE.linux-x86.UnixCommon create mode 100644 configure/os/CONFIG_SITE.linux-x86.linux-arm create mode 100644 configure/os/CONFIG_SITE.linux-x86.linux-arm_eb create mode 100644 configure/os/CONFIG_SITE.linux-x86.linux-arm_el create mode 100644 configure/os/CONFIG_SITE.linux-x86.linux-cris create mode 100644 configure/os/CONFIG_SITE.linux-x86.linux-x86 create mode 100644 configure/os/CONFIG_SITE.linux-x86.solaris-sparc create mode 100644 configure/os/CONFIG_SITE.linux-x86.vxWorks-68040 create mode 100644 configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603 create mode 100644 configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603_long create mode 100644 configure/os/CONFIG_SITE.linux-x86.vxWorksCommon create mode 100644 configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug create mode 100644 configure/os/CONFIG_SITE.linux-x86_64.Common create mode 100644 configure/os/CONFIG_SITE.linux-x86_64.UnixCommon create mode 100644 configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 create mode 100644 configure/os/CONFIG_SITE.linux-x86_64.vxWorks-68040 create mode 100644 configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603 create mode 100644 configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603_long create mode 100644 configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug create mode 100644 configure/os/CONFIG_SITE.solaris-sparc.Common create mode 100644 configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc-debug create mode 100644 configure/os/CONFIG_SITE.win32-x86-borland.Common create mode 100644 configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin create mode 100644 configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw create mode 100644 configure/os/CONFIG_SITE.win32-x86.Common create mode 100644 configure/os/CONFIG_SITE.win32-x86.win32-x86 create mode 100644 documentation/BuildingR3.13AppsWithR3.14.html create mode 100644 documentation/BuildingR3.13ExtensionsWithR3.14.html create mode 100644 documentation/ConvertingR3.13AppsToR3.14.html create mode 100644 documentation/KnownProblems.html create mode 100644 documentation/README.1st create mode 100644 documentation/README.MS_Windows create mode 100644 documentation/README.cris create mode 100644 documentation/README.darwin.html create mode 100644 documentation/README.html create mode 100644 documentation/README.niCpu030 create mode 100644 documentation/RELEASE_NOTES.html create mode 100644 documentation/ReleaseChecklist.html create mode 100644 src/Makefile create mode 100644 src/RTEMS/Makefile create mode 100644 src/RTEMS/base/Makefile create mode 100644 src/RTEMS/base/epicsRtemsInitHookPost.c create mode 100644 src/RTEMS/base/epicsRtemsInitHookPre.c create mode 100644 src/RTEMS/base/epicsRtemsInitHooks.h create mode 100644 src/RTEMS/base/rtems_config.c create mode 100644 src/RTEMS/base/rtems_init.c create mode 100644 src/RTEMS/base/rtems_netconfig.c create mode 100644 src/RTEMS/base/rtems_util.c create mode 100644 src/RTEMS/base/setBootConfigFromNVRAM.c create mode 100644 src/as/Makefile create mode 100644 src/as/asCa.c create mode 100644 src/as/asCa.h create mode 100644 src/as/asDbLib.c create mode 100644 src/as/asDbLib.h create mode 100755 src/as/asHost.rc create mode 100755 src/as/asIoc.rc create mode 100644 src/as/asIocRegister.c create mode 100644 src/as/asIocRegister.h create mode 100644 src/as/asLib.h create mode 100644 src/as/asLib.y create mode 100644 src/as/asLibRoutines.c create mode 100644 src/as/asLib_lex.l create mode 100644 src/as/asTrapWrite.c create mode 100644 src/as/asTrapWrite.h create mode 100644 src/as/ascheck.c create mode 100644 src/bpt/Makefile create mode 100644 src/bpt/bptTypeJdegC.data create mode 100644 src/bpt/bptTypeJdegF.data create mode 100644 src/bpt/bptTypeKdegC.data create mode 100644 src/bpt/bptTypeKdegF.data create mode 100644 src/bpt/cvtTable.h create mode 100644 src/bpt/makeBpt.c create mode 100644 src/bpt/menuConvert.dbd create mode 100644 src/ca/CASG.cpp create mode 100644 src/ca/CAref.html create mode 100644 src/ca/Makefile create mode 100755 src/ca/SearchDest.h create mode 100644 src/ca/access.cpp create mode 100644 src/ca/acctst.c create mode 100644 src/ca/acctstMain.c create mode 100644 src/ca/addrList.h create mode 100644 src/ca/autoPtrDestroy.h create mode 100644 src/ca/autoPtrFreeList.h create mode 100644 src/ca/autoPtrRecycle.h create mode 100644 src/ca/baseNMIU.cpp create mode 100644 src/ca/bhe.cpp create mode 100644 src/ca/bhe.h create mode 100755 src/ca/ca.rc create mode 100644 src/ca/caConnTest.cpp create mode 100644 src/ca/caConnTestMain.cpp create mode 100644 src/ca/caDiagnostics.h create mode 100644 src/ca/caEventRate.cpp create mode 100644 src/ca/caEventRateMain.cpp create mode 100644 src/ca/caProto.h create mode 100644 src/ca/caRepeater.cpp create mode 100644 src/ca/caServerID.h create mode 100644 src/ca/ca_client_context.cpp create mode 100644 src/ca/cac.cpp create mode 100644 src/ca/cac.h create mode 100644 src/ca/cacChannel.cpp create mode 100644 src/ca/cacChannelNotify.cpp create mode 100644 src/ca/cacContextNotify.cpp create mode 100644 src/ca/cacIO.h create mode 100644 src/ca/cacReadNotify.cpp create mode 100644 src/ca/cacStateNotify.cpp create mode 100644 src/ca/cacWriteNotify.cpp create mode 100644 src/ca/cadef.h create mode 100644 src/ca/caerr.h create mode 100644 src/ca/caeventmask.h create mode 100644 src/ca/casw.cpp create mode 100644 src/ca/catime.c create mode 100644 src/ca/catimeMain.c create mode 100644 src/ca/comBuf.cpp create mode 100644 src/ca/comBuf.h create mode 100644 src/ca/comQueRecv.cpp create mode 100644 src/ca/comQueRecv.h create mode 100644 src/ca/comQueSend.cpp create mode 100644 src/ca/comQueSend.h create mode 100644 src/ca/convert.cpp create mode 100644 src/ca/db_access.h create mode 100644 src/ca/disconnectGovernorTimer.cpp create mode 100644 src/ca/disconnectGovernorTimer.h create mode 100644 src/ca/evtime.c create mode 100644 src/ca/future_work.txt create mode 100644 src/ca/getCallback.cpp create mode 100644 src/ca/getCopy.cpp create mode 100644 src/ca/hostNameCache.cpp create mode 100644 src/ca/hostNameCache.h create mode 100644 src/ca/inetAddrID.h create mode 100644 src/ca/iocinf.cpp create mode 100644 src/ca/iocinf.h create mode 100644 src/ca/localHostName.cpp create mode 100644 src/ca/localHostName.h create mode 100644 src/ca/msgForMultiplyDefinedPV.cpp create mode 100644 src/ca/msgForMultiplyDefinedPV.h create mode 100644 src/ca/nciu.cpp create mode 100644 src/ca/nciu.h create mode 100644 src/ca/netIO.h create mode 100644 src/ca/netReadNotifyIO.cpp create mode 100644 src/ca/netSubscription.cpp create mode 100644 src/ca/netWriteNotifyIO.cpp create mode 100644 src/ca/net_convert.h create mode 100644 src/ca/netiiu.cpp create mode 100644 src/ca/netiiu.h create mode 100644 src/ca/noopiiu.cpp create mode 100644 src/ca/noopiiu.h create mode 100644 src/ca/oldAccess.h create mode 100644 src/ca/oldChannelNotify.cpp create mode 100644 src/ca/oldSubscription.cpp create mode 100644 src/ca/putCallback.cpp create mode 100644 src/ca/repeater.cpp create mode 100644 src/ca/repeaterClient.h create mode 100644 src/ca/repeaterSubscribeTimer.cpp create mode 100644 src/ca/repeaterSubscribeTimer.h create mode 100644 src/ca/searchTimer.cpp create mode 100644 src/ca/searchTimer.h create mode 100644 src/ca/sgAutoPtr.h create mode 100644 src/ca/syncGroup.h create mode 100644 src/ca/syncGroupNotify.cpp create mode 100644 src/ca/syncGroupReadNotify.cpp create mode 100644 src/ca/syncGroupWriteNotify.cpp create mode 100644 src/ca/syncgrp.cpp create mode 100644 src/ca/tcpRecvThread.cpp create mode 100644 src/ca/tcpRecvWatchdog.cpp create mode 100644 src/ca/tcpRecvWatchdog.h create mode 100644 src/ca/tcpSendWatchdog.cpp create mode 100644 src/ca/tcpSendWatchdog.h create mode 100644 src/ca/tcpiiu.cpp create mode 100644 src/ca/templateInstances.cpp create mode 100644 src/ca/test_event.cpp create mode 100644 src/ca/ucx.h create mode 100644 src/ca/udpiiu.cpp create mode 100644 src/ca/udpiiu.h create mode 100644 src/ca/virtualCircuit.h create mode 100644 src/cap5/CA.pm create mode 100644 src/cap5/Cap5.xs create mode 100644 src/cap5/Makefile create mode 100644 src/cap5/caget.pl create mode 100644 src/cap5/cainfo.pl create mode 100644 src/cap5/camonitor.pl create mode 100644 src/cap5/capr.pl create mode 100644 src/cap5/caput.pl create mode 100644 src/cap5/perlConfig.pl create mode 100644 src/cas/Makefile create mode 100644 src/cas/README create mode 100644 src/cas/RELEASE_NOTES create mode 100644 src/cas/build/Makefile create mode 100755 src/cas/build/cas.rc create mode 100644 src/cas/example/Makefile create mode 100644 src/cas/example/README create mode 100644 src/cas/example/directoryService/Makefile create mode 100644 src/cas/example/directoryService/README create mode 100644 src/cas/example/directoryService/directoryServer.cc create mode 100644 src/cas/example/directoryService/directoryServer.h create mode 100644 src/cas/example/directoryService/main.cc create mode 100644 src/cas/example/directoryService/pvDirectory.txt create mode 100644 src/cas/example/directoryService/test.adl create mode 100644 src/cas/example/directoryService/vxEntry.cc create mode 100644 src/cas/generic/README create mode 100644 src/cas/generic/beaconAnomalyGovernor.cc create mode 100644 src/cas/generic/beaconAnomalyGovernor.h create mode 100644 src/cas/generic/beaconTimer.cc create mode 100644 src/cas/generic/beaconTimer.h create mode 100644 src/cas/generic/caHdrLargeArray.h create mode 100644 src/cas/generic/caNetAddr.cc create mode 100644 src/cas/generic/caNetAddr.h create mode 100644 src/cas/generic/caServer.cc create mode 100644 src/cas/generic/caServerDefs.h create mode 100644 src/cas/generic/caServerI.cc create mode 100644 src/cas/generic/caServerI.h create mode 100644 src/cas/generic/casAddr.h create mode 100644 src/cas/generic/casAsyncIOI.cc create mode 100644 src/cas/generic/casAsyncIOI.h create mode 100644 src/cas/generic/casAsyncPVAttachIO.cc create mode 100644 src/cas/generic/casAsyncPVAttachIOI.cpp create mode 100644 src/cas/generic/casAsyncPVAttachIOI.h create mode 100644 src/cas/generic/casAsyncPVExistIO.cc create mode 100644 src/cas/generic/casAsyncPVExistIOI.cpp create mode 100644 src/cas/generic/casAsyncPVExistIOI.h create mode 100644 src/cas/generic/casAsyncReadIO.cc create mode 100644 src/cas/generic/casAsyncReadIOI.cc create mode 100644 src/cas/generic/casAsyncReadIOI.h create mode 100644 src/cas/generic/casAsyncWriteIO.cc create mode 100644 src/cas/generic/casAsyncWriteIOI.cpp create mode 100644 src/cas/generic/casAsyncWriteIOI.h create mode 100644 src/cas/generic/casBufferFactory.cpp create mode 100644 src/cas/generic/casChannel.cc create mode 100644 src/cas/generic/casChannelI.cc create mode 100644 src/cas/generic/casChannelI.h create mode 100644 src/cas/generic/casCoreClient.cc create mode 100644 src/cas/generic/casCoreClient.h create mode 100644 src/cas/generic/casCtx.cc create mode 100644 src/cas/generic/casCtx.h create mode 100644 src/cas/generic/casCtxIL.h create mode 100644 src/cas/generic/casDGClient.cc create mode 100644 src/cas/generic/casDGClient.h create mode 100644 src/cas/generic/casEvent.h create mode 100644 src/cas/generic/casEventMask.cc create mode 100644 src/cas/generic/casEventMask.h create mode 100644 src/cas/generic/casEventRegistry.h create mode 100644 src/cas/generic/casEventSys.cc create mode 100644 src/cas/generic/casEventSys.h create mode 100644 src/cas/generic/casMonEvent.cc create mode 100644 src/cas/generic/casMonEvent.h create mode 100644 src/cas/generic/casMonitor.cc create mode 100644 src/cas/generic/casMonitor.h create mode 100644 src/cas/generic/casOpaqueAddr.cc create mode 100644 src/cas/generic/casOpaqueAddrIL.h create mode 100644 src/cas/generic/casPV.cc create mode 100644 src/cas/generic/casPVI.cc create mode 100644 src/cas/generic/casPVI.h create mode 100644 src/cas/generic/casStrmClient.cc create mode 100644 src/cas/generic/casStrmClient.h create mode 100644 src/cas/generic/casdef.h create mode 100644 src/cas/generic/chanIntfForPV.cc create mode 100644 src/cas/generic/chanIntfForPV.h create mode 100644 src/cas/generic/channelDestroyEvent.cpp create mode 100644 src/cas/generic/channelDestroyEvent.h create mode 100644 src/cas/generic/clientBufMemoryManager.cpp create mode 100644 src/cas/generic/clientBufMemoryManager.h create mode 100644 src/cas/generic/inBuf.cc create mode 100644 src/cas/generic/inBuf.h create mode 100644 src/cas/generic/ioBlocked.h create mode 100644 src/cas/generic/mt/README create mode 100644 src/cas/generic/mt/ioBlocked.cc create mode 100644 src/cas/generic/outBuf.cc create mode 100644 src/cas/generic/outBuf.h create mode 100644 src/cas/generic/pvAttachReturn.cc create mode 100644 src/cas/generic/pvExistReturn.cc create mode 100644 src/cas/generic/st/README create mode 100644 src/cas/generic/st/caServerOS.cc create mode 100644 src/cas/generic/st/casDGEvWakeup.h create mode 100644 src/cas/generic/st/casDGIOWakeup.h create mode 100644 src/cas/generic/st/casDGIntfOS.cc create mode 100644 src/cas/generic/st/casDGIntfOS.h create mode 100644 src/cas/generic/st/casIntfOS.cc create mode 100644 src/cas/generic/st/casIntfOS.h create mode 100644 src/cas/generic/st/casOSD.h create mode 100644 src/cas/generic/st/casStreamOS.cc create mode 100644 src/cas/generic/st/casStreamOS.h create mode 100644 src/cas/generic/st/ioBlocked.cc create mode 100644 src/cas/generic/st/osiMutexCAS.h create mode 100644 src/cas/generic/templateInstances.cpp create mode 100644 src/cas/io/bsdSocket/README create mode 100644 src/cas/io/bsdSocket/caServerIO.cc create mode 100644 src/cas/io/bsdSocket/caServerIO.h create mode 100644 src/cas/io/bsdSocket/casDGIntfIO.cc create mode 100644 src/cas/io/bsdSocket/casDGIntfIO.h create mode 100644 src/cas/io/bsdSocket/casIOD.h create mode 100644 src/cas/io/bsdSocket/casIntfIO.cc create mode 100644 src/cas/io/bsdSocket/casIntfIO.h create mode 100644 src/cas/io/bsdSocket/casStreamIO.cc create mode 100644 src/cas/io/bsdSocket/casStreamIO.h create mode 100644 src/cas/io/bsdSocket/ipIgnoreEntry.cpp create mode 100644 src/cas/io/bsdSocket/ipIgnoreEntry.h create mode 100644 src/cas/os/vms/BUILD_VMS.COM create mode 100644 src/cas/os/vms/README create mode 100644 src/cas/os/vms/casSpecificOS.h create mode 100644 src/cas/os/vms/login.com create mode 100644 src/cas/os/vms/mitfp.c create mode 100644 src/cas/os/vms/mitfp.cc create mode 100644 src/cas/os/vms/mitfp.h create mode 100644 src/cas/os/vms/vms_depen.h create mode 100644 src/cas/test/gddAppFuncTableTest.cc create mode 100644 src/catools/Makefile create mode 100644 src/catools/caget.c create mode 100644 src/catools/cainfo.c create mode 100644 src/catools/camonitor.c create mode 100644 src/catools/caput.c create mode 100644 src/catools/tool_lib.c create mode 100644 src/catools/tool_lib.h create mode 100644 src/db/Makefile create mode 100644 src/db/callback.c create mode 100644 src/db/callback.h create mode 100644 src/db/cvtBpt.c create mode 100644 src/db/dbAccess.c create mode 100644 src/db/dbAccess.h create mode 100644 src/db/dbAccessDefs.h create mode 100644 src/db/dbAddr.h create mode 100644 src/db/dbBkpt.c create mode 100644 src/db/dbBkpt.h create mode 100644 src/db/dbCAC.h create mode 100644 src/db/dbCa.c create mode 100644 src/db/dbCa.h create mode 100644 src/db/dbCaPvt.h create mode 100644 src/db/dbCaTest.c create mode 100644 src/db/dbCaTest.h create mode 100644 src/db/dbChannelIO.cpp create mode 100644 src/db/dbChannelIO.h create mode 100644 src/db/dbCommon.dbd create mode 100644 src/db/dbCommonRecord.dbd create mode 100644 src/db/dbContext.cpp create mode 100644 src/db/dbContextReadNotifyCache.cpp create mode 100644 src/db/dbConvert.c create mode 100644 src/db/dbConvert.h create mode 100644 src/db/dbConvertFast.h create mode 100644 src/db/dbEvent.c create mode 100644 src/db/dbEvent.h create mode 100644 src/db/dbFastLinkConv.c create mode 100755 src/db/dbIoc.rc create mode 100644 src/db/dbIocRegister.c create mode 100644 src/db/dbIocRegister.h create mode 100644 src/db/dbLock.c create mode 100644 src/db/dbLock.h create mode 100644 src/db/dbNotify.c create mode 100644 src/db/dbNotify.h create mode 100644 src/db/dbPutNotifyBlocker.cpp create mode 100644 src/db/dbPutNotifyBlocker.h create mode 100644 src/db/dbScan.c create mode 100644 src/db/dbScan.h create mode 100644 src/db/dbSubscriptionIO.cpp create mode 100644 src/db/dbTest.c create mode 100644 src/db/dbTest.h create mode 100644 src/db/db_access.c create mode 100644 src/db/db_access_routines.h create mode 100644 src/db/db_convert.h create mode 100644 src/db/db_field_log.h create mode 100644 src/db/db_test.c create mode 100644 src/db/db_test.h create mode 100644 src/db/initHooks.c create mode 100644 src/db/initHooks.h create mode 100644 src/db/menuAlarmSevr.dbd create mode 100644 src/db/menuAlarmStat.dbd create mode 100644 src/db/menuCompress.dbd create mode 100644 src/db/menuFtype.dbd create mode 100644 src/db/menuGlobal.dbd create mode 100644 src/db/menuIvoa.dbd create mode 100644 src/db/menuOmsl.dbd create mode 100644 src/db/menuPini.dbd create mode 100644 src/db/menuPriority.dbd create mode 100644 src/db/menuScan.dbd create mode 100644 src/db/menuSimm.dbd create mode 100644 src/db/menuYesNo.dbd create mode 100644 src/db/recGbl.c create mode 100644 src/db/recGbl.h create mode 100644 src/db/templateInstances.cpp create mode 100644 src/db/test/Makefile create mode 100644 src/db/test/callbackTest.c create mode 100644 src/dbStatic/Makefile create mode 100644 src/dbStatic/alarm.h create mode 100644 src/dbStatic/alarmString.h create mode 100644 src/dbStatic/dbBase.h create mode 100644 src/dbStatic/dbExpand.c create mode 100644 src/dbStatic/dbFldTypes.h create mode 100644 src/dbStatic/dbLex.l create mode 100644 src/dbStatic/dbLexRoutines.c create mode 100644 src/dbStatic/dbPvdLib.c create mode 100644 src/dbStatic/dbReadTest.c create mode 100755 src/dbStatic/dbStaticHost.rc create mode 100755 src/dbStatic/dbStaticIoc.rc create mode 100644 src/dbStatic/dbStaticIocRegister.c create mode 100644 src/dbStatic/dbStaticIocRegister.h create mode 100644 src/dbStatic/dbStaticLib.c create mode 100644 src/dbStatic/dbStaticLib.h create mode 100644 src/dbStatic/dbStaticNoRun.c create mode 100644 src/dbStatic/dbStaticPvt.h create mode 100644 src/dbStatic/dbStaticRun.c create mode 100644 src/dbStatic/dbToMenuH.c create mode 100644 src/dbStatic/dbToRecordtypeH.c create mode 100644 src/dbStatic/dbYacc.y create mode 100644 src/dbStatic/devSup.h create mode 100644 src/dbStatic/drvSup.h create mode 100644 src/dbStatic/guigroup.h create mode 100644 src/dbStatic/link.h create mode 100644 src/dbStatic/recSup.h create mode 100644 src/dbStatic/special.h create mode 100644 src/dbtools/Makefile create mode 100644 src/dbtools/dbLoadTemplate.h create mode 100644 src/dbtools/dbLoadTemplate.html create mode 100644 src/dbtools/dbLoadTemplate.y create mode 100644 src/dbtools/dbLoadTemplate_lex.l create mode 100755 src/dbtools/dbtoolsIoc.rc create mode 100644 src/dbtools/dbtoolsIocRegister.c create mode 100644 src/dbtools/dbtoolsIocRegister.h create mode 100644 src/dev/Makefile create mode 100644 src/dev/softDev/Makefile create mode 100644 src/dev/softDev/devAaiSoft.c create mode 100644 src/dev/softDev/devAaoSoft.c create mode 100644 src/dev/softDev/devAiSoft.c create mode 100644 src/dev/softDev/devAiSoftRaw.c create mode 100644 src/dev/softDev/devAoSoft.c create mode 100644 src/dev/softDev/devAoSoftCallback.c create mode 100644 src/dev/softDev/devAoSoftRaw.c create mode 100644 src/dev/softDev/devBiSoft.c create mode 100644 src/dev/softDev/devBiSoftRaw.c create mode 100644 src/dev/softDev/devBoSoft.c create mode 100644 src/dev/softDev/devBoSoftCallback.c create mode 100644 src/dev/softDev/devBoSoftRaw.c create mode 100644 src/dev/softDev/devCalcoutSoft.c create mode 100644 src/dev/softDev/devCalcoutSoftCallback.c create mode 100644 src/dev/softDev/devEventSoft.c create mode 100644 src/dev/softDev/devGeneralTime.c create mode 100644 src/dev/softDev/devHistogramSoft.c create mode 100644 src/dev/softDev/devLiSoft.c create mode 100644 src/dev/softDev/devLoSoft.c create mode 100644 src/dev/softDev/devLoSoftCallback.c create mode 100644 src/dev/softDev/devMbbiDirectSoft.c create mode 100644 src/dev/softDev/devMbbiDirectSoftRaw.c create mode 100644 src/dev/softDev/devMbbiSoft.c create mode 100644 src/dev/softDev/devMbbiSoftRaw.c create mode 100644 src/dev/softDev/devMbboDirectSoft.c create mode 100644 src/dev/softDev/devMbboDirectSoftCallback.c create mode 100644 src/dev/softDev/devMbboDirectSoftRaw.c create mode 100644 src/dev/softDev/devMbboSoft.c create mode 100644 src/dev/softDev/devMbboSoftCallback.c create mode 100644 src/dev/softDev/devMbboSoftRaw.c create mode 100644 src/dev/softDev/devSASoft.c create mode 100644 src/dev/softDev/devSiSoft.c create mode 100644 src/dev/softDev/devSoSoft.c create mode 100644 src/dev/softDev/devSoSoftCallback.c create mode 100644 src/dev/softDev/devSoStdio.c create mode 100644 src/dev/softDev/devSoft.dbd create mode 100644 src/dev/softDev/devTimestamp.c create mode 100644 src/dev/softDev/devWfSoft.c create mode 100755 src/dev/softDev/softDevIoc.rc create mode 100644 src/dev/testDev/Makefile create mode 100644 src/dev/testDev/devHistogramTestAsyn.c create mode 100644 src/dev/testDev/devTestAsyn.c create mode 100644 src/dev/testDev/devTestAsyn.dbd create mode 100755 src/dev/testDev/testDevIoc.rc create mode 100644 src/excas/Makefile create mode 100644 src/gdd/Makefile create mode 100644 src/gdd/README create mode 100644 src/gdd/aitConvert.cc create mode 100644 src/gdd/aitConvert.h create mode 100644 src/gdd/aitGen.c create mode 100644 src/gdd/aitHelpers.cc create mode 100644 src/gdd/aitHelpers.h create mode 100644 src/gdd/aitTypes.c create mode 100644 src/gdd/aitTypes.h create mode 100644 src/gdd/dbMapper.cc create mode 100644 src/gdd/dbMapper.h create mode 100644 src/gdd/gdd.cc create mode 100644 src/gdd/gdd.gif create mode 100644 src/gdd/gdd.h create mode 100644 src/gdd/gdd.html create mode 100755 src/gdd/gdd.rc create mode 100644 src/gdd/gddAppDefs.cc create mode 100644 src/gdd/gddAppFuncTable.h create mode 100644 src/gdd/gddAppTable.cc create mode 100644 src/gdd/gddAppTable.h create mode 100644 src/gdd/gddArray.cc create mode 100644 src/gdd/gddArray.h create mode 100644 src/gdd/gddArrayI.h create mode 100644 src/gdd/gddContainer.cc create mode 100644 src/gdd/gddContainer.h create mode 100644 src/gdd/gddContainerI.h create mode 100644 src/gdd/gddEnumStringTable.cc create mode 100644 src/gdd/gddEnumStringTable.h create mode 100644 src/gdd/gddErrorCodes.cc create mode 100644 src/gdd/gddErrorCodes.h create mode 100644 src/gdd/gddI.h create mode 100644 src/gdd/gddNewDel.cc create mode 100644 src/gdd/gddNewDel.h create mode 100644 src/gdd/gddScalar.h create mode 100644 src/gdd/gddScalarI.h create mode 100644 src/gdd/gddTest.cc create mode 100644 src/gdd/gddUtils.cc create mode 100644 src/gdd/gddUtils.h create mode 100644 src/gdd/gddUtilsI.h create mode 100644 src/gdd/gddref.html create mode 100644 src/gdd/gddref2.html create mode 100644 src/gdd/genApps.cc create mode 100644 src/gdd/smartGDDPointer.h create mode 100755 src/libCom/Com.rc create mode 100644 src/libCom/Makefile create mode 100644 src/libCom/bucketLib/bucketLib.c create mode 100644 src/libCom/bucketLib/bucketLib.h create mode 100644 src/libCom/calc/calcPerform.c create mode 100644 src/libCom/calc/postfix.c create mode 100644 src/libCom/calc/postfix.h create mode 100644 src/libCom/calc/postfixPvt.h create mode 100644 src/libCom/cppStd/epicsAlgorithm.h create mode 100644 src/libCom/cppStd/epicsExcept.h create mode 100644 src/libCom/cppStd/epicsMemory.h create mode 100644 src/libCom/cvtFast/cvtFast.c create mode 100644 src/libCom/cvtFast/cvtFast.h create mode 100644 src/libCom/cxxTemplates/README create mode 100644 src/libCom/cxxTemplates/epicsGuard.h create mode 100644 src/libCom/cxxTemplates/epicsOnce.cpp create mode 100644 src/libCom/cxxTemplates/epicsOnce.h create mode 100644 src/libCom/cxxTemplates/epicsSingleton.h create mode 100644 src/libCom/cxxTemplates/epicsSingletonBase.cpp create mode 100644 src/libCom/cxxTemplates/epicsSingletonMutex.cpp create mode 100644 src/libCom/cxxTemplates/resourceLib.cpp create mode 100644 src/libCom/cxxTemplates/resourceLib.h create mode 100644 src/libCom/cxxTemplates/test/Makefile create mode 100644 src/libCom/cxxTemplates/test/minmaxTest.cc create mode 100644 src/libCom/cxxTemplates/test/resourceLibTest.cc create mode 100644 src/libCom/cxxTemplates/test/tsBTreeBench.cc create mode 100644 src/libCom/cxxTemplates/test/tsBTreeTest.cc create mode 100644 src/libCom/cxxTemplates/test/tsDLListBench.cc create mode 100644 src/libCom/cxxTemplates/test/tsDLListTest.cc create mode 100644 src/libCom/cxxTemplates/test/tsSLListBench.cc create mode 100644 src/libCom/cxxTemplates/test/tsSLListTest.cc create mode 100644 src/libCom/cxxTemplates/tsBTree.h create mode 100644 src/libCom/cxxTemplates/tsDLList.h create mode 100644 src/libCom/cxxTemplates/tsFreeList.h create mode 100644 src/libCom/cxxTemplates/tsMinMax.h create mode 100644 src/libCom/cxxTemplates/tsSLList.h create mode 100644 src/libCom/dbmf/dbmf.c create mode 100644 src/libCom/dbmf/dbmf.h create mode 100644 src/libCom/ellLib/ellLib.c create mode 100644 src/libCom/ellLib/ellLib.h create mode 100644 src/libCom/env/bldEnvData.pl create mode 100644 src/libCom/env/envDefs.h create mode 100644 src/libCom/env/envSubr.c create mode 100644 src/libCom/error/epicsPrint.h create mode 100644 src/libCom/error/errMdef.h create mode 100644 src/libCom/error/errSymLib.c create mode 100644 src/libCom/error/errSymTbl.h create mode 100644 src/libCom/error/errlog.c create mode 100644 src/libCom/error/errlog.h create mode 100644 src/libCom/error/error.h create mode 100644 src/libCom/error/makeStatTbl.pl create mode 100644 src/libCom/fdmgr/fdManager.cpp create mode 100644 src/libCom/fdmgr/fdManager.h create mode 100644 src/libCom/fdmgr/fdmgr.cpp create mode 100644 src/libCom/fdmgr/fdmgr.h create mode 100644 src/libCom/freeList/freeList.h create mode 100644 src/libCom/freeList/freeList.html create mode 100644 src/libCom/freeList/freeListLib.c create mode 100644 src/libCom/gpHash/gpHash.h create mode 100644 src/libCom/gpHash/gpHash.html create mode 100644 src/libCom/gpHash/gpHashLib.c create mode 100644 src/libCom/iocsh/iocsh.cpp create mode 100644 src/libCom/iocsh/iocsh.h create mode 100644 src/libCom/iocsh/libComRegister.c create mode 100644 src/libCom/iocsh/libComRegister.h create mode 100644 src/libCom/iocsh/registry.c create mode 100644 src/libCom/iocsh/registry.h create mode 100644 src/libCom/logClient/iocLog.c create mode 100644 src/libCom/logClient/iocLog.h create mode 100644 src/libCom/logClient/logClient.c create mode 100644 src/libCom/logClient/logClient.h create mode 100644 src/libCom/macLib/macCore.c create mode 100644 src/libCom/macLib/macEnv.c create mode 100644 src/libCom/macLib/macLib.h create mode 100644 src/libCom/macLib/macLibNOTES create mode 100644 src/libCom/macLib/macLibREADME create mode 100644 src/libCom/macLib/macUtil.c create mode 100644 src/libCom/misc/aToIPAddr.c create mode 100644 src/libCom/misc/adjustment.c create mode 100644 src/libCom/misc/adjustment.h create mode 100644 src/libCom/misc/cantProceed.c create mode 100644 src/libCom/misc/cantProceed.h create mode 100644 src/libCom/misc/compilerDependencies.h create mode 100644 src/libCom/misc/dbDefs.h create mode 100644 src/libCom/misc/epicsConvert.c create mode 100644 src/libCom/misc/epicsConvert.h create mode 100644 src/libCom/misc/epicsExit.c create mode 100644 src/libCom/misc/epicsExit.h create mode 100644 src/libCom/misc/epicsExport.h create mode 100644 src/libCom/misc/epicsStdlib.c create mode 100644 src/libCom/misc/epicsStdlib.h create mode 100644 src/libCom/misc/epicsString.c create mode 100644 src/libCom/misc/epicsString.h create mode 100644 src/libCom/misc/epicsTypes.h create mode 100644 src/libCom/misc/epicsUnitTest.c create mode 100644 src/libCom/misc/epicsUnitTest.h create mode 100644 src/libCom/misc/ipAddrToAsciiAsynchronous.cpp create mode 100644 src/libCom/misc/ipAddrToAsciiAsynchronous.h create mode 100644 src/libCom/misc/locationException.h create mode 100644 src/libCom/misc/makeEpicsVersion.pl create mode 100644 src/libCom/misc/shareLib.h create mode 100644 src/libCom/misc/testMain.h create mode 100644 src/libCom/misc/truncateFile.c create mode 100644 src/libCom/misc/unixFileName.h create mode 100644 src/libCom/osi/TODOfuture create mode 100644 src/libCom/osi/devLib.h create mode 100644 src/libCom/osi/devLibVME.c create mode 100644 src/libCom/osi/devLibVME.h create mode 100644 src/libCom/osi/devLibVMEImpl.h create mode 100644 src/libCom/osi/epicsAssert.h create mode 100644 src/libCom/osi/epicsEndian.h create mode 100644 src/libCom/osi/epicsEvent.cpp create mode 100644 src/libCom/osi/epicsEvent.h create mode 100644 src/libCom/osi/epicsFindSymbol.h create mode 100644 src/libCom/osi/epicsGeneralTime.c create mode 100644 src/libCom/osi/epicsGeneralTime.h create mode 100644 src/libCom/osi/epicsInterrupt.h create mode 100644 src/libCom/osi/epicsMath.cpp create mode 100644 src/libCom/osi/epicsMessageQueue.cpp create mode 100644 src/libCom/osi/epicsMessageQueue.h create mode 100644 src/libCom/osi/epicsMutex.cpp create mode 100644 src/libCom/osi/epicsMutex.h create mode 100644 src/libCom/osi/epicsSignal.h create mode 100644 src/libCom/osi/epicsStdio.c create mode 100644 src/libCom/osi/epicsStdio.h create mode 100644 src/libCom/osi/epicsStdioRedirect.h create mode 100644 src/libCom/osi/epicsThread.cpp create mode 100644 src/libCom/osi/epicsThread.h create mode 100644 src/libCom/osi/epicsTime.cpp create mode 100644 src/libCom/osi/epicsTime.h create mode 100644 src/libCom/osi/generalTimeSup.h create mode 100644 src/libCom/osi/os/Darwin/epicsMath.h create mode 100644 src/libCom/osi/os/Darwin/osdEnv.c create mode 100644 src/libCom/osi/os/Darwin/osdFindSymbol.c create mode 100644 src/libCom/osi/os/Darwin/osdSock.h create mode 100644 src/libCom/osi/os/Darwin/osdSockAddrReuse.cpp create mode 100644 src/libCom/osi/os/Darwin/osdTime.h create mode 100644 src/libCom/osi/os/Darwin/osiFileName.h create mode 100644 src/libCom/osi/os/Linux/osdFindSymbol.c create mode 100644 src/libCom/osi/os/Linux/osdSock.h create mode 100644 src/libCom/osi/os/Linux/osdTime.h create mode 100644 src/libCom/osi/os/Linux/osiFileName.h create mode 100644 src/libCom/osi/os/Linux/osiUnistd.h create mode 100644 src/libCom/osi/os/RTEMS/devLibVMEOSD.c create mode 100644 src/libCom/osi/os/RTEMS/epicsMath.h create mode 100644 src/libCom/osi/os/RTEMS/osdEvent.c create mode 100644 src/libCom/osi/os/RTEMS/osdEvent.h create mode 100644 src/libCom/osi/os/RTEMS/osdInterrupt.c create mode 100644 src/libCom/osi/os/RTEMS/osdInterrupt.h create mode 100644 src/libCom/osi/os/RTEMS/osdMessageQueue.c create mode 100644 src/libCom/osi/os/RTEMS/osdMessageQueue.h create mode 100644 src/libCom/osi/os/RTEMS/osdMutex.c create mode 100644 src/libCom/osi/os/RTEMS/osdMutex.h create mode 100644 src/libCom/osi/os/RTEMS/osdPoolStatus.c create mode 100644 src/libCom/osi/os/RTEMS/osdProcess.c create mode 100644 src/libCom/osi/os/RTEMS/osdSignal.cpp create mode 100644 src/libCom/osi/os/RTEMS/osdSock.h create mode 100644 src/libCom/osi/os/RTEMS/osdStrtod.h create mode 100644 src/libCom/osi/os/RTEMS/osdThread.c create mode 100644 src/libCom/osi/os/RTEMS/osdThread.h create mode 100644 src/libCom/osi/os/RTEMS/osdTime.cpp create mode 100644 src/libCom/osi/os/RTEMS/osdTime.h create mode 100644 src/libCom/osi/os/RTEMS/osdVME.h create mode 100644 src/libCom/osi/os/RTEMS/osiFileName.h create mode 100644 src/libCom/osi/os/RTEMS/osiUnistd.h create mode 100644 src/libCom/osi/os/WIN32/epicsGetopt.c create mode 100644 src/libCom/osi/os/WIN32/epicsGetopt.h create mode 100644 src/libCom/osi/os/WIN32/epicsMath.h create mode 100644 src/libCom/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp create mode 100644 src/libCom/osi/os/WIN32/epicsTempFile.cpp create mode 100644 src/libCom/osi/os/WIN32/forceBadAllocException.cpp create mode 100644 src/libCom/osi/os/WIN32/osdEvent.c create mode 100644 src/libCom/osi/os/WIN32/osdEvent.h create mode 100644 src/libCom/osi/os/WIN32/osdMutex.c create mode 100644 src/libCom/osi/os/WIN32/osdMutex.h create mode 100644 src/libCom/osi/os/WIN32/osdNetIntf.c create mode 100644 src/libCom/osi/os/WIN32/osdPoolStatus.c create mode 100644 src/libCom/osi/os/WIN32/osdPoolStatus.h create mode 100644 src/libCom/osi/os/WIN32/osdProcess.c create mode 100644 src/libCom/osi/os/WIN32/osdSignal.cpp create mode 100644 src/libCom/osi/os/WIN32/osdSock.c create mode 100644 src/libCom/osi/os/WIN32/osdSock.h create mode 100644 src/libCom/osi/os/WIN32/osdSockAddrReuse.cpp create mode 100644 src/libCom/osi/os/WIN32/osdStdio.c create mode 100644 src/libCom/osi/os/WIN32/osdStrtod.h create mode 100644 src/libCom/osi/os/WIN32/osdThread.c create mode 100644 src/libCom/osi/os/WIN32/osdThread.h create mode 100644 src/libCom/osi/os/WIN32/osdTime.cpp create mode 100644 src/libCom/osi/os/WIN32/osdTime.h create mode 100644 src/libCom/osi/os/WIN32/osdWireConfig.h create mode 100644 src/libCom/osi/os/WIN32/osiFileName.h create mode 100644 src/libCom/osi/os/WIN32/osiUnistd.h create mode 100644 src/libCom/osi/os/WIN32/setThreadName.cpp create mode 100644 src/libCom/osi/os/WIN32/systemCallIntMech.cpp create mode 100644 src/libCom/osi/os/cygwin32/devLibVMEOSD.c create mode 100644 src/libCom/osi/os/cygwin32/osdSock.h create mode 100644 src/libCom/osi/os/cygwin32/osdSockAddrReuse.cpp create mode 100644 src/libCom/osi/os/cygwin32/osdStrtod.h create mode 100644 src/libCom/osi/os/cygwin32/osiFileName.h create mode 100644 src/libCom/osi/os/cygwin32/systemCallIntMech.cpp create mode 100644 src/libCom/osi/os/default/devLibVMEOSD.c create mode 100644 src/libCom/osi/os/default/epicsGetopt.h create mode 100644 src/libCom/osi/os/default/epicsReadline.c create mode 100644 src/libCom/osi/os/default/epicsReadline.h create mode 100644 src/libCom/osi/os/default/epicsSocketConvertErrnoToString.cpp create mode 100644 src/libCom/osi/os/default/osdAssert.c create mode 100644 src/libCom/osi/os/default/osdEnv.c create mode 100644 src/libCom/osi/os/default/osdFindSymbol.c create mode 100644 src/libCom/osi/os/default/osdInterrupt.c create mode 100644 src/libCom/osi/os/default/osdInterrupt.h create mode 100644 src/libCom/osi/os/default/osdMessageQueue.cpp create mode 100644 src/libCom/osi/os/default/osdMessageQueue.h create mode 100644 src/libCom/osi/os/default/osdNetIntf.c create mode 100644 src/libCom/osi/os/default/osdPoolStatus.c create mode 100644 src/libCom/osi/os/default/osdPoolStatus.h create mode 100644 src/libCom/osi/os/default/osdSignal.cpp create mode 100644 src/libCom/osi/os/default/osdVME.h create mode 100644 src/libCom/osi/os/default/osdWireConfig.h create mode 100644 src/libCom/osi/os/default/osdWireFormat.h create mode 100644 src/libCom/osi/os/freebsd/osdSock.h create mode 100644 src/libCom/osi/os/freebsd/osdTime.h create mode 100644 src/libCom/osi/os/freebsd/osiFileName.h create mode 100644 src/libCom/osi/os/freebsd/osiUnistd.h create mode 100644 src/libCom/osi/os/iOS/epicsMath.h create mode 100644 src/libCom/osi/os/iOS/osdEnv.c create mode 100644 src/libCom/osi/os/iOS/osdSock.h create mode 100644 src/libCom/osi/os/iOS/osdSockAddrReuse.cpp create mode 100644 src/libCom/osi/os/iOS/osdTime.h create mode 100644 src/libCom/osi/os/iOS/osiFileName.h create mode 100644 src/libCom/osi/os/posix/README create mode 100644 src/libCom/osi/os/posix/epicsMath.h create mode 100644 src/libCom/osi/os/posix/epicsTempFile.cpp create mode 100644 src/libCom/osi/os/posix/osdEvent.c create mode 100644 src/libCom/osi/os/posix/osdEvent.h create mode 100644 src/libCom/osi/os/posix/osdMutex.c create mode 100644 src/libCom/osi/os/posix/osdMutex.h create mode 100644 src/libCom/osi/os/posix/osdProcess.c create mode 100644 src/libCom/osi/os/posix/osdSignal.cpp create mode 100644 src/libCom/osi/os/posix/osdSock.c create mode 100644 src/libCom/osi/os/posix/osdSockAddrReuse.cpp create mode 100644 src/libCom/osi/os/posix/osdStdio.c create mode 100644 src/libCom/osi/os/posix/osdStrtod.h create mode 100644 src/libCom/osi/os/posix/osdThread.c create mode 100644 src/libCom/osi/os/posix/osdThread.h create mode 100644 src/libCom/osi/os/posix/osdTime.cpp create mode 100644 src/libCom/osi/os/posix/osdTime.h create mode 100644 src/libCom/osi/os/posix/osiUnistd.h create mode 100644 src/libCom/osi/os/posix/systemCallIntMech.cpp create mode 100644 src/libCom/osi/os/solaris/epicsMath.h create mode 100644 src/libCom/osi/os/solaris/osdFindSymbol.c create mode 100644 src/libCom/osi/os/solaris/osdSock.h create mode 100644 src/libCom/osi/os/solaris/osdStrtod.h create mode 100644 src/libCom/osi/os/solaris/osdWireConfig.h create mode 100644 src/libCom/osi/os/solaris/osiFileName.h create mode 100644 src/libCom/osi/os/vxWorks/README create mode 100644 src/libCom/osi/os/vxWorks/atReboot.cpp create mode 100644 src/libCom/osi/os/vxWorks/camacLib.h create mode 100644 src/libCom/osi/os/vxWorks/devLibVMEOSD.c create mode 100644 src/libCom/osi/os/vxWorks/epicsDynLink.c create mode 100644 src/libCom/osi/os/vxWorks/epicsDynLink.h create mode 100644 src/libCom/osi/os/vxWorks/epicsMath.h create mode 100644 src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp create mode 100644 src/libCom/osi/os/vxWorks/module_types.h create mode 100644 src/libCom/osi/os/vxWorks/osdEnv.c create mode 100644 src/libCom/osi/os/vxWorks/osdEvent.c create mode 100644 src/libCom/osi/os/vxWorks/osdEvent.h create mode 100644 src/libCom/osi/os/vxWorks/osdFindSymbol.c create mode 100644 src/libCom/osi/os/vxWorks/osdInterrupt.c create mode 100644 src/libCom/osi/os/vxWorks/osdInterrupt.h create mode 100644 src/libCom/osi/os/vxWorks/osdMessageQueue.cpp create mode 100644 src/libCom/osi/os/vxWorks/osdMessageQueue.h create mode 100644 src/libCom/osi/os/vxWorks/osdMutex.c create mode 100644 src/libCom/osi/os/vxWorks/osdMutex.h create mode 100644 src/libCom/osi/os/vxWorks/osdPoolStatus.c create mode 100644 src/libCom/osi/os/vxWorks/osdProcess.c create mode 100644 src/libCom/osi/os/vxWorks/osdSignal.cpp create mode 100644 src/libCom/osi/os/vxWorks/osdSock.c create mode 100644 src/libCom/osi/os/vxWorks/osdSock.h create mode 100644 src/libCom/osi/os/vxWorks/osdStdio.c create mode 100644 src/libCom/osi/os/vxWorks/osdStrtod.h create mode 100644 src/libCom/osi/os/vxWorks/osdThread.c create mode 100644 src/libCom/osi/os/vxWorks/osdThread.h create mode 100644 src/libCom/osi/os/vxWorks/osdTime.cpp create mode 100644 src/libCom/osi/os/vxWorks/osdTime.h create mode 100644 src/libCom/osi/os/vxWorks/osdVME.h create mode 100644 src/libCom/osi/os/vxWorks/osdWireConfig.h create mode 100644 src/libCom/osi/os/vxWorks/osiFileName.h create mode 100644 src/libCom/osi/os/vxWorks/task_params.h create mode 100644 src/libCom/osi/os/vxWorks/veclist.c create mode 100644 src/libCom/osi/os/vxWorks/vxComLibrary.c create mode 100644 src/libCom/osi/osiClockTime.c create mode 100644 src/libCom/osi/osiClockTime.h create mode 100644 src/libCom/osi/osiNTPTime.c create mode 100644 src/libCom/osi/osiNTPTime.h create mode 100644 src/libCom/osi/osiPoolStatus.h create mode 100644 src/libCom/osi/osiProcess.h create mode 100644 src/libCom/osi/osiSock.c create mode 100644 src/libCom/osi/osiSock.h create mode 100644 src/libCom/osi/osiWireFormat.h create mode 100644 src/libCom/ring/epicsRingBytes.c create mode 100644 src/libCom/ring/epicsRingBytes.h create mode 100644 src/libCom/ring/epicsRingPointer.cpp create mode 100644 src/libCom/ring/epicsRingPointer.h create mode 100644 src/libCom/taskwd/taskwd.c create mode 100644 src/libCom/taskwd/taskwd.h create mode 100644 src/libCom/test/Makefile create mode 100644 src/libCom/test/blockingSockTest.cpp create mode 100644 src/libCom/test/buckTest.c create mode 100644 src/libCom/test/cvtFastPerform.cpp create mode 100644 src/libCom/test/epicsAlgorithmTest.cpp create mode 100644 src/libCom/test/epicsCalcTest.cpp create mode 100644 src/libCom/test/epicsEllTest.c create mode 100644 src/libCom/test/epicsErrlogTest.c create mode 100644 src/libCom/test/epicsEventTest.cpp create mode 100644 src/libCom/test/epicsExceptionTest.cpp create mode 100644 src/libCom/test/epicsExitTest.c create mode 100644 src/libCom/test/epicsMathTest.c create mode 100644 src/libCom/test/epicsMaxThreads.c create mode 100644 src/libCom/test/epicsMessageQueueTest.cpp create mode 100644 src/libCom/test/epicsMutexTest.cpp create mode 100644 src/libCom/test/epicsRunLibComTests.c create mode 100644 src/libCom/test/epicsStdioTest.c create mode 100644 src/libCom/test/epicsStringTest.c create mode 100644 src/libCom/test/epicsThreadOnceTest.c create mode 100644 src/libCom/test/epicsThreadPerform.cpp create mode 100644 src/libCom/test/epicsThreadPriorityTest.cpp create mode 100644 src/libCom/test/epicsThreadPrivateTest.cpp create mode 100644 src/libCom/test/epicsThreadTest.cpp create mode 100644 src/libCom/test/epicsTimeTest.cpp create mode 100644 src/libCom/test/epicsTimerTest.cpp create mode 100644 src/libCom/test/epicsUnitTestTest.c create mode 100644 src/libCom/test/epicsUnitTestTest.plt create mode 100644 src/libCom/test/fdmgrTest.c create mode 100644 src/libCom/test/macEnvExpandTest.c create mode 100644 src/libCom/test/macLibTest.c create mode 100644 src/libCom/test/ringBytesTest.c create mode 100644 src/libCom/test/ringPointerTest.c create mode 100644 src/libCom/test/rtemsTestHarness.c create mode 100644 src/libCom/test/taskwdTest.c create mode 100644 src/libCom/timer/epicsTimer.cpp create mode 100644 src/libCom/timer/epicsTimer.h create mode 100644 src/libCom/timer/timer.cpp create mode 100644 src/libCom/timer/timerPrivate.h create mode 100644 src/libCom/timer/timerQueue.cpp create mode 100644 src/libCom/timer/timerQueueActive.cpp create mode 100644 src/libCom/timer/timerQueueActiveMgr.cpp create mode 100644 src/libCom/timer/timerQueuePassive.cpp create mode 100644 src/libCom/tsDefs/README create mode 100644 src/libCom/tsDefs/tsDefs.c create mode 100644 src/libCom/tsDefs/tsDefs.h create mode 100644 src/makeBaseApp/Makefile create mode 100755 src/makeBaseApp/makeBaseApp.pl create mode 100644 src/makeBaseApp/top/Makefile create mode 100644 src/makeBaseApp/top/caClientApp/Makefile create mode 100644 src/makeBaseApp/top/caClientApp/caExample.c create mode 100644 src/makeBaseApp/top/caClientApp/caMonitor.c create mode 100644 src/makeBaseApp/top/caServerApp/Makefile create mode 100644 src/makeBaseApp/top/caServerApp/README create mode 100644 src/makeBaseApp/top/caServerApp/exAsyncPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/exChannel.cc create mode 100644 src/makeBaseApp/top/caServerApp/exPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/exScalarPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/exServer.cc create mode 100644 src/makeBaseApp/top/caServerApp/exServer.h create mode 100644 src/makeBaseApp/top/caServerApp/exVectorPV.cc create mode 100644 src/makeBaseApp/top/caServerApp/main.cc create mode 100644 src/makeBaseApp/top/caServerApp/test.adl create mode 100644 src/makeBaseApp/top/caServerApp/vxEntry.cc create mode 100644 src/makeBaseApp/top/configure/CONFIG create mode 100644 src/makeBaseApp/top/configure/CONFIG_SITE create mode 100644 src/makeBaseApp/top/configure/Makefile create mode 100644 src/makeBaseApp/top/configure/RELEASE create mode 100644 src/makeBaseApp/top/configure/RULES create mode 100644 src/makeBaseApp/top/configure/RULES.ioc create mode 100644 src/makeBaseApp/top/configure/RULES_DIRS create mode 100644 src/makeBaseApp/top/configure/RULES_TOP create mode 100644 src/makeBaseApp/top/exampleApp/Db/Makefile create mode 100644 src/makeBaseApp/top/exampleApp/Db/dbExample1.db create mode 100644 src/makeBaseApp/top/exampleApp/Db/dbExample2.db create mode 100644 src/makeBaseApp/top/exampleApp/Db/dbSubExample.db create mode 100644 src/makeBaseApp/top/exampleApp/Db/user.substitutions create mode 100644 src/makeBaseApp/top/exampleApp/Db/userHost.substitutions create mode 100644 src/makeBaseApp/top/exampleApp/Makefile create mode 100644 src/makeBaseApp/top/exampleApp/src/Makefile create mode 100644 src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.c create mode 100644 src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.dbd create mode 100644 src/makeBaseApp/top/exampleApp/src/_APPNAME_Main.cpp create mode 100644 src/makeBaseApp/top/exampleApp/src/dbSubExample.c create mode 100644 src/makeBaseApp/top/exampleApp/src/dbSubExample.dbd create mode 100644 src/makeBaseApp/top/exampleApp/src/devXxxSoft.c create mode 100644 src/makeBaseApp/top/exampleApp/src/initTrace.c create mode 100644 src/makeBaseApp/top/exampleApp/src/initTrace.dbd create mode 100644 src/makeBaseApp/top/exampleApp/src/sncExample.dbd create mode 100644 src/makeBaseApp/top/exampleApp/src/sncExample.stt create mode 100644 src/makeBaseApp/top/exampleApp/src/sncProgram.st create mode 100644 src/makeBaseApp/top/exampleApp/src/xxxRecord.c create mode 100644 src/makeBaseApp/top/exampleApp/src/xxxRecord.dbd create mode 100644 src/makeBaseApp/top/exampleApp/src/xxxSupport.dbd create mode 100644 src/makeBaseApp/top/exampleBoot/Makefile create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/Makefile@Common create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/Makefile@vxWorks create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/Makefile@win32 create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/README@Common create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/README@RTEMS create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/README@vxWorks create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/st.cmd@Common create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/st.cmd@RTEMS create mode 100644 src/makeBaseApp/top/exampleBoot/ioc/st.cmd@vxWorks create mode 100644 src/makeBaseApp/top/exampleBoot/nfsCommands@RTEMS create mode 100644 src/makeBaseApp/top/exampleBoot/nfsCommands@vxWorks create mode 100644 src/makeBaseApp/top/iocApp/Db/Makefile create mode 100644 src/makeBaseApp/top/iocApp/Makefile create mode 100644 src/makeBaseApp/top/iocApp/src/Makefile create mode 100644 src/makeBaseApp/top/iocApp/src/_APPNAME_Main.cpp create mode 100644 src/makeBaseApp/top/iocBoot/Makefile create mode 100644 src/makeBaseApp/top/iocBoot/ioc/Makefile@Common create mode 100644 src/makeBaseApp/top/iocBoot/ioc/Makefile@vxWorks create mode 100644 src/makeBaseApp/top/iocBoot/ioc/Makefile@win32 create mode 100644 src/makeBaseApp/top/iocBoot/ioc/st.cmd@Common create mode 100644 src/makeBaseApp/top/iocBoot/ioc/st.cmd@Cross create mode 100644 src/makeBaseApp/top/iocBoot/ioc/st.cmd@RTEMS create mode 100644 src/makeBaseApp/top/iocBoot/ioc/st.cmd@vxWorks create mode 100644 src/makeBaseApp/top/iocBoot/nfsCommands@RTEMS create mode 100644 src/makeBaseApp/top/iocBoot/nfsCommands@vxWorks create mode 100644 src/makeBaseApp/top/supportApp/Db/Makefile create mode 100644 src/makeBaseApp/top/supportApp/Makefile create mode 100644 src/makeBaseApp/top/supportApp/src/Makefile create mode 100644 src/makeBaseApp/top/supportApp/src/_APPNAME_.dbd create mode 100644 src/makeBaseExt/Makefile create mode 100755 src/makeBaseExt/makeBaseExt.pl create mode 100644 src/makeBaseExt/top/Makefile create mode 100644 src/makeBaseExt/top/README create mode 100644 src/makeBaseExt/top/configure/CONFIG create mode 100644 src/makeBaseExt/top/configure/CONFIG_SITE create mode 100644 src/makeBaseExt/top/configure/Makefile create mode 100644 src/makeBaseExt/top/configure/RELEASE create mode 100644 src/makeBaseExt/top/configure/RULES create mode 100644 src/makeBaseExt/top/configure/RULES_DIRS create mode 100644 src/makeBaseExt/top/configure/RULES_IDL create mode 100644 src/makeBaseExt/top/configure/RULES_PYTHON create mode 100644 src/makeBaseExt/top/configure/RULES_TOP create mode 100644 src/makeBaseExt/top/configure/os/CONFIG.linux-x86.linux-386 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG.win32-x86-borland.win32-x86-borland create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.Common.Common create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc-gnu.aix-ppc-gnu create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc.aix-ppc create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppc.darwin-ppc create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppcx86.darwin-ppcx86 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc-gnu.hpux-parisc-gnu create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc.hpux-parisc create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.interix-x86.interix-x86 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-borland.linux-x86-borland create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-athlon create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-gnu.solaris-sparc-gnu create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64-gnu.solaris-sparc64-gnu create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64.solaris-sparc64 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-debug.solaris-x86-debug create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-gnu.solaris-x86-gnu create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86.solaris-x86 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86_64.solaris-x86_64 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-borland.win32-x86-borland create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 create mode 100644 src/makeBaseExt/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 create mode 100644 src/makeBaseExt/top/exampleExt/Makefile create mode 100644 src/makeBaseExt/top/exampleExt/RELEASE_NOTES.HTM create mode 100644 src/makeBaseExt/top/exampleExt/caExample.c create mode 100644 src/makeBaseExt/top/simpleExt/Makefile create mode 100644 src/makeBaseExt/top/src/Makefile create mode 100644 src/misc/Makefile create mode 100644 src/misc/asSubRecordFunctions.c create mode 100644 src/misc/base.dbd create mode 100644 src/misc/dlload.c create mode 100644 src/misc/dlload.dbd create mode 100644 src/misc/epicsRelease.c create mode 100644 src/misc/epicsRelease.h create mode 100644 src/misc/iocInit.c create mode 100644 src/misc/iocInit.h create mode 100644 src/misc/iocshRegisterCommon.c create mode 100644 src/misc/iocshRegisterCommon.h create mode 100755 src/misc/misc.rc create mode 100755 src/misc/miscIoc.rc create mode 100644 src/misc/miscIocRegister.c create mode 100644 src/misc/miscIocRegister.h create mode 100644 src/misc/system.dbd create mode 100644 src/rec/Makefile create mode 100644 src/rec/aSubRecord.c create mode 100644 src/rec/aSubRecord.dbd create mode 100644 src/rec/aaiRecord.c create mode 100644 src/rec/aaiRecord.dbd create mode 100644 src/rec/aaoRecord.c create mode 100644 src/rec/aaoRecord.dbd create mode 100644 src/rec/aiRecord.c create mode 100644 src/rec/aiRecord.dbd create mode 100644 src/rec/aoRecord.c create mode 100644 src/rec/aoRecord.dbd create mode 100644 src/rec/biRecord.c create mode 100644 src/rec/biRecord.dbd create mode 100644 src/rec/boRecord.c create mode 100644 src/rec/boRecord.dbd create mode 100644 src/rec/calcRecord.c create mode 100644 src/rec/calcRecord.dbd create mode 100644 src/rec/calcoutRecord.c create mode 100644 src/rec/calcoutRecord.dbd create mode 100644 src/rec/compressRecord.c create mode 100644 src/rec/compressRecord.dbd create mode 100644 src/rec/dfanoutRecord.c create mode 100644 src/rec/dfanoutRecord.dbd create mode 100644 src/rec/eventRecord.c create mode 100644 src/rec/eventRecord.dbd create mode 100644 src/rec/fanoutRecord.c create mode 100644 src/rec/fanoutRecord.dbd create mode 100644 src/rec/histogramRecord.c create mode 100644 src/rec/histogramRecord.dbd create mode 100644 src/rec/longinRecord.c create mode 100644 src/rec/longinRecord.dbd create mode 100644 src/rec/longoutRecord.c create mode 100644 src/rec/longoutRecord.dbd create mode 100644 src/rec/mbbiDirectRecord.c create mode 100644 src/rec/mbbiDirectRecord.dbd create mode 100644 src/rec/mbbiRecord.c create mode 100644 src/rec/mbbiRecord.dbd create mode 100644 src/rec/mbboDirectRecord.c create mode 100644 src/rec/mbboDirectRecord.dbd create mode 100644 src/rec/mbboRecord.c create mode 100644 src/rec/mbboRecord.dbd create mode 100644 src/rec/permissiveRecord.c create mode 100644 src/rec/permissiveRecord.dbd create mode 100755 src/rec/recIoc.rc create mode 100644 src/rec/selRecord.c create mode 100644 src/rec/selRecord.dbd create mode 100644 src/rec/seqRecord.c create mode 100644 src/rec/seqRecord.dbd create mode 100644 src/rec/stateRecord.c create mode 100644 src/rec/stateRecord.dbd create mode 100644 src/rec/stringinRecord.c create mode 100644 src/rec/stringinRecord.dbd create mode 100644 src/rec/stringoutRecord.c create mode 100644 src/rec/stringoutRecord.dbd create mode 100644 src/rec/subArrayRecord.c create mode 100644 src/rec/subArrayRecord.dbd create mode 100644 src/rec/subRecord.c create mode 100644 src/rec/subRecord.dbd create mode 100644 src/rec/waveformRecord.c create mode 100644 src/rec/waveformRecord.dbd create mode 100644 src/registry/Makefile create mode 100755 src/registry/registerRecordDeviceDriver.pl create mode 100644 src/registry/registryCommon.c create mode 100644 src/registry/registryCommon.h create mode 100644 src/registry/registryDeviceSupport.c create mode 100644 src/registry/registryDeviceSupport.h create mode 100644 src/registry/registryDriverSupport.c create mode 100644 src/registry/registryDriverSupport.h create mode 100644 src/registry/registryFunction.c create mode 100644 src/registry/registryFunction.h create mode 100755 src/registry/registryIoc.rc create mode 100644 src/registry/registryIocRegister.c create mode 100644 src/registry/registryIocRegister.h create mode 100644 src/registry/registryRecordType.c create mode 100644 src/registry/registryRecordType.h create mode 100644 src/rsrv/Makefile create mode 100644 src/rsrv/camessage.c create mode 100644 src/rsrv/camsgtask.c create mode 100644 src/rsrv/caserverio.c create mode 100644 src/rsrv/caservertask.c create mode 100644 src/rsrv/cast_server.c create mode 100644 src/rsrv/online_notify.c create mode 100644 src/rsrv/rsrv.h create mode 100755 src/rsrv/rsrvIoc.rc create mode 100644 src/rsrv/rsrvIocRegister.c create mode 100644 src/rsrv/rsrvIocRegister.h create mode 100644 src/rsrv/server.h create mode 100644 src/softIoc/Makefile create mode 100644 src/softIoc/makeInstallDir.pl create mode 100644 src/softIoc/softIocExit.db create mode 100644 src/softIoc/softMain.cpp create mode 100644 src/tools/EPICS/Copy.pm create mode 100644 src/tools/EPICS/Getopts.pm create mode 100644 src/tools/EPICS/Path.pm create mode 100644 src/tools/EPICS/Release.pm create mode 100644 src/tools/Makefile create mode 100644 src/tools/convertRelease.pl create mode 100644 src/tools/cvsclean.pl create mode 100644 src/tools/dos2unix.pl create mode 100644 src/tools/expandVars.pl create mode 100644 src/tools/filterWarnings.pl create mode 100644 src/tools/fullPathName.pl create mode 100644 src/tools/installEpics.pl create mode 100644 src/tools/makeDbDepends.pl create mode 100644 src/tools/makeIncludeDbd.pl create mode 100644 src/tools/makeMakefile.pl create mode 100644 src/tools/makeTestfile.pl create mode 100644 src/tools/mkmf.pl create mode 100644 src/tools/munch.pl create mode 100644 src/tools/replaceVAR.pl create mode 100644 src/tools/useManifestTool.pl create mode 100644 src/toolsComm/Makefile create mode 100644 src/toolsComm/antelope/ACKNOWLEDGEMENTS create mode 100644 src/toolsComm/antelope/EPICS_READ_THIS create mode 100644 src/toolsComm/antelope/Makefile create mode 100644 src/toolsComm/antelope/NEW_FEATURES create mode 100644 src/toolsComm/antelope/NOTES create mode 100644 src/toolsComm/antelope/NO_WARRANTY create mode 100644 src/toolsComm/antelope/README create mode 100644 src/toolsComm/antelope/closure.c create mode 100644 src/toolsComm/antelope/defs.h create mode 100644 src/toolsComm/antelope/error.c create mode 100644 src/toolsComm/antelope/lalr.c create mode 100644 src/toolsComm/antelope/lr0.c create mode 100644 src/toolsComm/antelope/main.c create mode 100644 src/toolsComm/antelope/mkpar.c create mode 100644 src/toolsComm/antelope/output.c create mode 100644 src/toolsComm/antelope/reader.c create mode 100644 src/toolsComm/antelope/skeleton.c create mode 100644 src/toolsComm/antelope/symtab.c create mode 100644 src/toolsComm/antelope/verbose.c create mode 100644 src/toolsComm/antelope/warshall.c create mode 100644 src/toolsComm/antelope/yacc.html create mode 100644 src/toolsComm/flex/COPYING create mode 100644 src/toolsComm/flex/Changes create mode 100644 src/toolsComm/flex/EPICS_READ_THIS create mode 100644 src/toolsComm/flex/Flex.doc create mode 100644 src/toolsComm/flex/Makefile create mode 100644 src/toolsComm/flex/README create mode 100644 src/toolsComm/flex/ccl.c create mode 100644 src/toolsComm/flex/dfa.c create mode 100644 src/toolsComm/flex/ecs.c create mode 100644 src/toolsComm/flex/flex.html create mode 100644 src/toolsComm/flex/flex.skel create mode 100644 src/toolsComm/flex/flex.skel.static create mode 100644 src/toolsComm/flex/flexdef.h create mode 100644 src/toolsComm/flex/flexdoc.html create mode 100644 src/toolsComm/flex/gen.c create mode 100644 src/toolsComm/flex/libmain.c create mode 100644 src/toolsComm/flex/main.c create mode 100644 src/toolsComm/flex/misc.c create mode 100644 src/toolsComm/flex/nfa.c create mode 100644 src/toolsComm/flex/parse.y create mode 100644 src/toolsComm/flex/scan.c create mode 100644 src/toolsComm/flex/scan.l.DISTRIB create mode 100644 src/toolsComm/flex/sym.c create mode 100644 src/toolsComm/flex/tblcmp.c create mode 100644 src/toolsComm/flex/yylex.c create mode 100644 src/util/Makefile create mode 100644 src/util/ca_test.c create mode 100644 src/util/ca_test.h create mode 100644 src/util/ca_test_main.c create mode 100644 src/util/iocLogServer.c create mode 100644 src/util/rc2.caRepeater create mode 100644 src/util/rc2.logServer create mode 100644 src/vxWorks/Makefile create mode 100644 src/vxWorks/registerRecordDeviceDriver.c create mode 100755 startup/EpicsHostArch create mode 100755 startup/EpicsHostArch.pl create mode 100755 startup/Site.cshrc create mode 100755 startup/Site.profile create mode 100755 startup/cygwin.bat create mode 100755 startup/win32.bat diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..ff9597ccf --- /dev/null +++ b/LICENSE @@ -0,0 +1,84 @@ +Copyright (c) 1991-2007 UChicago Argonne LLC and The Regents of the +University of California. All rights reserved. + +EPICS BASE is distributed subject to the following license conditions: + + SOFTWARE LICENSE AGREEMENT + Software: EPICS BASE + Versions: 3.13.7 and higher + + 1. The "Software", below, refers to EPICS BASE (in either source code, or + binary form and accompanying documentation). Each licensee is + addressed as "you" or "Licensee." + + 2. The copyright holders shown above and their third-party licensors + hereby grant Licensee a royalty-free nonexclusive license, subject to + the limitations stated herein and U.S. Government license rights. + + 3. You may modify and make a copy or copies of the Software for use + within your organization, if you meet the following conditions: + a. Copies in source code must include the copyright notice and this + Software License Agreement. + b. Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other + materials provided with the copy. + + 4. You may modify a copy or copies of the Software or any portion of it, + thus forming a work based on the Software, and distribute copies of + such work outside your organization, if you meet all of the following + conditions: + a. Copies in source code must include the copyright notice and this + Software License Agreement; + b. Copies in binary form must include the copyright notice and this + Software License Agreement in the documentation and/or other + materials provided with the copy; + c. Modified copies and works based on the Software must carry + prominent notices stating that you changed specified portions of + the Software. + + 5. Portions of the Software resulted from work developed under a U.S. + Government contract and are subject to the following license: the + Government is granted for itself and others acting on its behalf a + paid-up, nonexclusive, irrevocable worldwide license in this computer + software to reproduce, prepare derivative works, and perform publicly + and display publicly. + + 6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY + OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE + UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR + EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME + ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, + OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE + SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT + THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE + OR THAT ANY ERRORS WILL BE CORRECTED. + + 7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR + THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT + OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, + CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, + INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY + REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF + CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR + OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE + POSSIBILITY OF SUCH LOSS OR DAMAGES. + +________________________________________________________________________ + +This software is in part copyrighted by the BERLINER SPEICHERRING +GESELLSCHAFT FUER SYNCHROTRONSTRAHLUNG M.B.H. (BESSY), BERLIN, GERMANY. + +In no event shall BESSY be liable to any party for direct, indirect, +special, incidental, or consequential damages arising out of the use of +this software, its documentation, or any derivatives thereof, even if +BESSY has been advised of the possibility of such damage. + +BESSY specifically disclaims any warranties, including, but not limited +to, the implied warranties of merchantability, fitness for a particular +purpose, and non-infringement. This software is provided on an "as is" +basis, and BESSY has no obligation to provide maintenance, support, +updates, enhancements, or modifications. +________________________________________________________________________ + diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..069a5cf81 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ +#************************************************************************* +# 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 +# + +TOP = . +include $(TOP)/configure/CONFIG + +# Bootstrap resolution: tools not installed yet +TOOLS = $(TOP)/src/tools + +DIRS += configure src +ifeq ($(findstring YES,$(COMPAT_313) $(COMPAT_TOOLS_313)),YES) +DIRS += config +endif + +src_DEPEND_DIRS = configure +config_DEPEND_DIRS = src + +include $(TOP)/configure/RULES_TOP + diff --git a/README b/README new file mode 100644 index 000000000..ce971d630 --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +--------------------------------------------------------- +EPICS Base - the central core of a control system toolkit +--------------------------------------------------------- + +Copyright (c) 1991-2003 The University of Chicago, as Operator +of Argonne National Laboratory. +Copyright (c) 1991-2003 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 the +file LICENSE that is included with this distribution. + +--------------------------------------------------------- + +Installation and release information can be found in the +various files in the documentation subdirectory. + +Additional information about EPICS including mailing list +archives and subscription instructions, documentation and +training materials, additional components, links to other +websites etc. is available on the EPICS home page at + http://www.aps.anl.gov/epics/ + diff --git a/config/CONFIG b/config/CONFIG new file mode 100644 index 000000000..9514c7f5d --- /dev/null +++ b/config/CONFIG @@ -0,0 +1,91 @@ +#************************************************************************* +# 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 +# +# The developer may edit this file. +# assume T_A is the host arch if not specified + +# +# EPICS base definitions +# +include $(EPICS_BASE)/config/CONFIG_COMMON + +# +# EPICS version definitions +# +include $(EPICS_BASE)/config/CONFIG_BASE_VERSION + +# Site-specific build options +# +include $(EPICS_BASE)/config/CONFIG_SITE + +# Host architecture specific definitions +# +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.$(HOST_ARCH) +# +-include $(EPICS_BASE)/config/CONFIG_SITE_HOST_ARCH.$(HOST_ARCH) + +ifdef T_A + +# Build type specific definitions +# +-include $(EPICS_BASE)/config/CONFIG.$(BUILD_TYPE) +# +-include $(EPICS_BASE)/config/CONFIG_SITE.$(BUILD_TYPE) + +# Architecture specific definitions +# +include $(EPICS_BASE)/config/CONFIG.$(BUILD_TYPE).$(T_A) +# +ifneq ($(HOST_ARCH),$(T_A)) +-include $(EPICS_BASE)/config/CONFIG_SITE.$(BUILD_TYPE).$(HOST_ARCH) +endif +# +-include $(EPICS_BASE)/config/CONFIG_SITE.$(BUILD_TYPE).$(T_A) + +endif + +# User specific definitions +# +-include $(HOME)/EPICS_CONFIG +-include $(HOME)/EPICS_CONFIG.$(HOST_ARCH) +-include $(HOME)/EPICS_CONFIG_HOST_ARCH.$(HOST_ARCH) +ifdef T_A +-include $(HOME)/EPICS_CONFIG.$(BUILD_TYPE) +ifneq ($(HOST_ARCH),$(T_A)) +-include $(HOME)/EPICS_CONFIG.$(BUILD_TYPE).$(HOST_ARCH) +endif +-include $(HOME)/EPICS_CONFIG.$(BUILD_TYPE).$(T_A) +endif + +# All EPICS options other than BUILD_TYPE +# may be overridden here. +# +# EXAMPLES +# -------- +# Build client objects statically ? must be either YES or NO +#STATIC_BUILD=NO +# Unix Optimization, must be either YES or NO +#HOST_OPT=YES +# VxWorks Optimization, must be either YES or NO +#VX_OPT=YES +# Generate Verbose Compiler Warnings for Unix, must be either YES or NO +#UNIX_WARN=YES +# Generate Verbose Compiler Warnings for VxWorks, must be either YES or NO +#VX_WARN=YES +#etc. + +#CROSS_COMPILER_TARGET_ARCHS=mv167 +#ANSI=GCC +#CPLUSPLUS=G++ +#CMPLR=STRICT +#CXXCMPLR=STRICT + diff --git a/config/CONFIG.Host.Borland b/config/CONFIG.Host.Borland new file mode 100644 index 000000000..0c60f03b6 --- /dev/null +++ b/config/CONFIG.Host.Borland @@ -0,0 +1,270 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.Borland +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Borland + +BORLAND_LIB = $(BORLAND)\\lib +BORLAND_INC = $(BORLAND)\\include +BORLAND_BIN = $(BORLAND)\\bin + +# +# +ANSI = ACC +CPLUSPLUS = CCC + +# +# -q supress command line banner +WINLINK = $(BORLAND_BIN)/ilink32 -q + +# -l specifies default language +# -fo Renames the output .RES file +RCCMD = $(BORLAND_BIN)/brcc32 $(subst -I,-i,$(INCLUDES)) -l0x409 -fo$@ $< + +ARCMD = $(BORLAND_BIN)/tlib $@ + +# +# Configure Borland C compiler +# -q suppress compiler identification banner +# -tWM generate a 32-bit multi-threaded target +# -tWD generate a .DLL executable +# -a8 quad word alignment +# -D_WIN32 macro defined to be consistant with Microsoft Visual C++ +# -D_RTLDLL macro defined to use Borland C++ RTL library +CCLINKOPT = -q -tWM -tWD -a8 -D_WIN32 -D_RTLDLL +ACC = $(BORLAND_BIN)/bcc32 $(CCLINKOPT) + +# +# __STDC__=0 works but not as cleanly as with +# Microsoft Visual C++. +# The Borland header files use ifdef __STDC__ +# to disable many nice things. This is overridden +# by defining NO_BORLAND_STDC in the Makefile.Host. +# +ifdef NO_BORLAND_STDC +ACC_ANSI = $(ACC) +ACC_STRICT = $(ACC) +else +ACC_ANSI = $(ACC) -D__STDC__=0 +ACC_STRICT = $(ACC) -D__STDC__=0 +endif +ACC_TRAD = $(ACC) + +# -w display warnings on +# -g0 no limit to warning messages +# some warning message here are always disabled because they are +# trivial and numerous +# -w-8012 Comparing signed and unsigned values +# -w-8060 Possibly incorrect assignment +# -w-8071 Conversion may lose significant digits +ACC_WARN_YES = -w -g0 -w-8012 -w-8060 -w-8071 +# -w- display warnings off +ACC_WARN_NO = -w- + +# +# -k- turn off standard stack frame +# -H- turn off precompiled headers +# -R- don't include browser info in .obj files +# -O1 optimization for size +# -v- turn off source debugging +# -vi control expansion of inline functions +ACC_OPT_YES = -k- -H- -R- -O1 -v- -vi + +# +ACC_OPT_NO = + + +# +# no special libs for static link +# +ACC_SLIBS_YES= +ACC_SLIBS_NO= + +# Configure OS vendor C++ compiler +# +# __STDC__=0 works but not as cleanly as with +# Microsoft Visual C++. +# The Borland header files use ifdef __STDC__ +# to disable many nice things. This is overridden +# by defining NO_BORLAND_STDC in the Makefile.Host. +# +CCC = $(BORLAND_BIN)/bcc32 $(CCLINKOPT) +ifdef NO_BORLAND_STDC +CCC_NORMAL = $(CCC) +CCC_STRICT = $(CCC) +else +CCC_NORMAL = $(CCC) -D__STDC__=0 +CCC_STRICT = $(CCC) -D__STDC__=0 +endif +CCC_TEMPL_INST_FLAG = + +# -w display warnings on +# -g0 no limit to warning messages +# -w-8012 Comparing signed and unsigned values +# -w-8060 Possibly incorrect assignment +# -w-8071 Conversion may lose significant digits +CCC_WARN_YES = -w -g0 -w-8012 -w-8060 -w-8071 -w-8008 -w-8027 -w-8066 -w-8080 -w-8004 +# -w- display warnings off +CCC_WARN_NO = -w- + +# +# -k- turn off standard stack frame +# -H- Turn off precompiled headers +# -R- Don't include browser info in .obj files +# -O1 optimization for size +# -v- turn off source debugging +# -vi control expansion of inline functions +CCC_OPT_YES = -k- -H- -R- -O1 -v- -vi + +# +CCC_OPT_NO = + +# +# no special libs for static link +# +CCC_SLIBS_YES= +CCC_SLIBS_NO= + +PROD_VERSION=3.13 +# -c case sensitive linking +# -C clear state before linking +# -Gi generate import library +# -Gn no state files +# -Tpd targets a Windows .DLL file +# -x no map +# -w display warnings on +LINK_OPT_FLAGS_YES = -c -C -Gi -Gn -Tpd -x -w +LINK_OPT_FLAGS_NO = -c -C -Gi -Gn -Tpd -x -w- +WIN32_DLLFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT)) +OPT_LDFLAGS = + +ARCH_DEP_CFLAGS= + +# to identify the general architecture class: +# should be BSD, SYSV, WIN32, ... +# is: WIN32, sun4, hpux, linux, ... +# +ARCH_CLASS=WIN32 + +# ifdef WIN32 looks better that ifeq ($(ARCH_CLASS),WIN32) ?? +WIN32=1 +BORLANDC=1 + +EXE=.exe +OBJ=.obj +RES=.res + +# Problem: BorlandC does not recognize *.cc as C++ source, +# we have to compile xx.cc using the flag -P xx.cc, +SOURCE_CXXFLAG = -P -D__cplusplus + +# Operating system flags +OP_SYS_CFLAGS = + +# +# Borland specific include files +# +OP_SYS_INCLUDES = -I$(BORLAND_INC) +# +OP_SYS_LDLIBS = + +# +# specify dll .def file only if it exists +# +#DLL_DEF_FLAG = $(addprefix /def:,$(wildcard ../$(LIBRARY).def)) +DLL_DEF_FLAG = $(subst /,\\,$(wildcard ../$(LIBRARY).def)) + +# HOST_OPT_FLAGS is part of CFLAGS/CXXFLAGS, +# which in turn are used in COMPILE.c[c] +# +# If we compile a .c, .cc into an $(OBJ), +# we test if this object is part of the +# library objects LIBOBJS. +# If so, we define _WINDLL so that +# e.g. include/shareLib.h works correctly. +# +HOST_OPT_FLAGS += $(subst $@, /_WINDLL, $(findstring $@,$(LIBOBJS))) + +# +# A WIN32 dll has three parts: +# x.dll: the real dll (SHRLIBNAME) +# x.lib: what you link to progs that use the dll (LIBNAME) +# x.exp: what you need to build the dll (in no variable) +# +LINK.shrlib = $(WINLINK) $(WIN32_DLLFLAGS) -L$(BORLAND_LIB) -L$(BORLAND_LIB)\\Psdk c0d32.obj + +# adjust names of libraries to build +# +# But: if there are no objects LIBOBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +SHRLIBNAME = $(LIBRARY).dll + +# +# Under WIN32 we have the unique situation where the DLL link creates the +# DLL link library xxx.lib and we need to be very careful to avoid replacing +# the xxx.lib created by the dll link with an xxx.lib created by $(AR). +# Therefore, the object library is named xxxObj.lib +# +# SHARED_LIBRARIES is YES if we are building a DLL and NO if we aren't +# +DLL_LINK_LIBNAME_YES = $(LIBRARY).lib +DLL_LINK_LIBNAME = $(DLL_LINK_LIBNAME_$(SHARED_LIBRARIES)) + +#ifeq ($(strip $(SHARED_LIBRARIES)),NO) +#LIBNAME = $(LIBRARY).lib +#else +LIBNAME = $(LIBRARY)Obj.lib +#endif + +# dll install location +INSTALL_SHRLIB = $(INSTALL_BIN) + +#-------------------------------------------------- +# Dependancy definitions +OBJECT_LIB_EXT_YES = Obj.lib # object library extension for static build +OBJECT_LIB_EXT_NO = .lib # object library extension for dynamic build +OBJECT_LIB_EXT = $(OBJECT_LIB_EXT_$(STATIC_BUILD)) + +COND_PROD_DEPLIBS = $(foreach prod,$(PROD), $(foreach lib, $($(basename $(prod))_LIBS),\ + $(firstword $($(lib)_DIR) $(EPICS_BASE_LIB))/$(lib)$(OBJECT_LIB_EXT))) +PRODNAME_DEPLIBS = $(foreach lib,$(PRODNAME_LIBS),$(firstword $(wildcard $($(lib)_DIR)/$(lib).lib $($(lib)_DIR)/$(lib)Obj.lib $(EPICS_BASE_LIB)/$(lib).lib $(EPICS_BASE_LIB)/$(lib)Obj.lib ) $(filter $(LIBRARY)$(OBJECT_LIB_EXT), $(lib)$(OBJECT_LIB_EXT)) )) +PROD_DEPLIBS = $(foreach lib,$(PROD_LIBS),$(firstword $(wildcard $($(lib)_DIR)/$(lib).lib $($(lib)_DIR)/$(lib)Obj.lib $(EPICS_BASE_LIB)/$(lib).lib $(EPICS_BASE_LIB)/$(lib)Obj.lib ) $(filter $(LIBRARY)$(OBJECT_LIB_EXT), $(lib)$(OBJECT_LIB_EXT)) )) + +USR_DEPLIBS = $(foreach lib,$(USR_LIBS),$(firstword $(wildcard $($(lib)_DIR)/$(lib).lib $($(lib)_DIR)/$(lib)Obj.lib $(EPICS_BASE_LIB)/$(lib).lib $(EPICS_BASE_LIB)/$(lib)Obj.lib ) $(filter $(LIBRARY)$(OBJECT_LIB_EXT), $(lib)$(OBJECT_LIB_EXT)) )) + +# by default the libraries used when linking the DLL are just +# PROD_LIBS ans SYS_PROD_LIBS minus the DLL name +DLL_LIBS = $(patsubst $(LIBRARY),, $(PROD_LIBS)) + +# +# EPICS libs that we need to link the DLL with +# (it isnt necessary to rebuild the dll if these change) +DLL_DEPLIBS = $(foreach lib,$(DLL_LIBS),$(firstword $(wildcard $($(lib)_DIR)/$(lib).lib $($(lib)_DIR)/$(lib)Obj.lib $(EPICS_BASE_LIB)/$(lib).lib $(EPICS_BASE_LIB)/$(lib)Obj.lib) $(filter $(LIBRARY)$(OBJECT_LIB_EXT), $(lib)$(OBJECT_LIB_EXT)) )) + +USR_LDLIBS = $(PRODNAME_DEPLIBS) $(PROD_DEPLIBS) $(USR_DEPLIBS) $(SYS_PROD_LIBS:%=%.lib) + +DLL_LDLIBS = $(DLL_DEPLIBS) $(SYS_PROD_LIBS:%=%.lib) $(OP_SYS_LDLIBS) + +#multithreaded import library +LIBSUF=mti +# -c case sensitive linking +# -C clear state before linking +# -Gn no state files +# -Tpe targets a Windows .EXE file +# -x no map +# -w display warnings on +LDFLAGS += -c -C -Gn -Tpe -x -w -L$(BORLAND_LIB) -L$(BORLAND_LIB)\\Psdk +LINKSTARTUP = c0x32.obj +LINKLIBS=import32.lib cw32$(LIBSUF).lib + +LINK.c = $(WINLINK) $(LDFLAGS) $(LINKSTARTUP) +LINK.cc = $(WINLINK) $(LDFLAGS) $(LINKSTARTUP) + diff --git a/config/CONFIG.Host.Darwin b/config/CONFIG.Host.Darwin new file mode 100644 index 000000000..b22e2b603 --- /dev/null +++ b/config/CONFIG.Host.Darwin @@ -0,0 +1,58 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.Darwin +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.Darwin + +ARCH_CLASS = Darwin + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +# +# The config files are a real mess. The following definitions seem to work +# for all the weird ways in which the extensions makefile fragments use them. +# +AR = ar +ARCMD = $(AR) -rc $@ +ARFLAGS = rcv +RANLIB = ranlib + +ANSI=GCC +CPLUSPLUS=G++ + +OP_SYS_CPPFLAGS += -DDarwin +OP_SYS_CFLAGS += -no-cpp-precomp +OP_SYS_INCLUDES = -I/sw/include -I/usr/X11R6/include +OP_SYS_LDFLAGS += -L/sw/lib -L/usr/X11R6/lib + +# Uncomment this if you're using the GNU readline library +#ARCH_DEP_LDLIBS += -lreadline + +# Uncomment this if you're using the libtecla library +#ARCH_DEP_LDLIBS += -ltecla_r -ltermcap + +# +# Compiler/linker problems prevent the use of shared libraries at the moment +# +SHARED_LIBRARIES_Darwin = NO + +# +# Something like this will be needed once shared libraries are built +# +SHRLIB_SUFFIX = .dylib$(SHARED_LIBRARIES) +GCC_SHRLIB_LDFLAGS_YES += -dynamiclib +G++_SHRLIB_LDFLAGS_YES += -dynamiclib + +# +# Java +# +JAVA_DIR=/usr/ diff --git a/config/CONFIG.Host.Linux b/config/CONFIG.Host.Linux new file mode 100644 index 000000000..cdb14154b --- /dev/null +++ b/config/CONFIG.Host.Linux @@ -0,0 +1,55 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.Linux +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.Linux + +ARCH_CLASS = Linux + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +AR = ar -rc +ARCMD = $(AR) $@ +RANLIB = ranlib -t + +ANSI=GCC +CPLUSPLUS=G++ + +GCC_SHRLIB_LDFLAGS_YES += -Wl,-soname,$@ +G++_SHRLIB_LDFLAGS_YES += -Wl,-soname,$@ + +OP_SYS_CFLAGS += -D_BSD_SOURCE + +ARCH_DEP_CFLAGS = -D_X86_ -Dlinux +ARCH_DEP_LDLIBS += -lpthread -lreadline -lcurses -lrt + +#Allows R3.13 built extensions to load R3.14 shared libs +SYS_DLL_LIBS_Linux += pthread readline curses rt + +#glibc FAQ +# "_GNU_SOURCE: glibc does not make the GNU extensions available +# automatically. If a program depends on the GNU extensions or some other +# non-standard functionality, it is necessary to compile it with the C +# compiler option -D_GNU_SOURCE, or better to put #define _GNU_SOURCE at +# the beginning of your source files, before any C library header files +# are included. This difference normally manifests itself in the form +# of missing prototypes and/or data type definitions. Thus, if you get +# such errors, the first thing you should do is try defining _GNU_SOURCE +# and see if that makes the problem go away." +#ARCH_DEP_CFLAGS += -D_GNU_SOURCE + +# Runtime ldflags +RUNTIME_LIBS =$(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)\ + $(EPICS_EXTENSIONS)/lib/$(EPICS_HOST_ARCH) +RUNTIME_LDFLAGS = $(RUNTIME_LIBS:%=-Wl,-rpath,%) + + diff --git a/config/CONFIG.Host.UnixCommon b/config/CONFIG.Host.UnixCommon new file mode 100644 index 000000000..3b9febb39 --- /dev/null +++ b/config/CONFIG.Host.UnixCommon @@ -0,0 +1,89 @@ +#************************************************************************* +# 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. +#************************************************************************* + +# +# Contains definitions common to all Unix archs +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.UnixCommon + + +#------------------------------------------------------- +# adjust names of libraries to build +# +# -> lib.a +LIBNAME = $(LIBRARY:%=lib%.a) + +#------------------------------------------------------- +# Shared library definitions + +# CONFIG.Host. files may override +SHRLIB_SUFFIX = .so + +SHRLIBNAME = lib$(LIBRARY)$(SHRLIB_SUFFIX)$(SHRLIB_VERSION:%=.%) + +SYS_DLL_LIBS += $(SYS_DLL_LIBS_$(OS_CLASS)) +DLL_LIBS = $(patsubst $(LIBRARY),, $(PROD_LIBS) $(USR_LIBS)) $(SYS_DLL_LIBS) + +INSTALL_SHRLIB = $(INSTALL_LIB) + +LINK.shrlib= $(SHRLIB_LINKER) -o $@ $(CFLAGS) $(CPPFLAGS) $(SHRLIB_LDFLAGS) $(DLL_LDFLAGS) $(LDFLAGS) + +#------------------------------------------------------- +# Unix command definitions + +CPP = cpp +RANLIB = ranlib + +# GNU compilers +GCC = gcc +G++ = g++ + +#------------------------------------------------------- +# Unix suffix definitions +EXE = +OBJ = .o + +#-------------------------------------------------- +# Dependancy definitions +COND_PROD_DEPLIBS = $(foreach prod,$(PROD), $(foreach lib, $($(basename $(prod))_LIBS),$(firstword $(wildcard $($(lib)_DIR)/lib$(lib).* $(EPICS_BASE_LIB)/lib$(lib).*)))) +PRODNAME_DEPLIBS = $(foreach lib,$(PRODNAME_LIBS),$(firstword $(wildcard $($(lib)_DIR)/lib$(lib).* $(EPICS_BASE_LIB)/lib$(lib).*))) +PROD_DEPLIBS = $(foreach lib,$(PROD_LIBS),$(firstword $(wildcard $($(lib)_DIR)/lib$(lib).* $(EPICS_BASE_LIB)/lib$(lib).*))) +USR_DEPLIBS = $(foreach lib,$(USR_LIBS),$(firstword $(wildcard $($(lib)_DIR)/lib$(lib).* $(EPICS_BASE_LIB)/lib$(lib).*))) +DLL_DEPLIBS = $(foreach lib,$(DLL_LIBS),$(firstword $(wildcard $($(lib)_DIR)/lib$(lib).* $(EPICS_BASE_LIB)/lib$(lib).*))) +#-------------------------------------------------- +# Determine ld flags +USR_DIRS = $(dir $(PRODNAME_DEPLIBS)) $(dir $(PROD_DEPLIBS))\ + $(dir $(USR_DEPLIBS)) +USR_LDFLAGS += $(sort $(USR_DIRS:%=-L%)) +DLL_DIRS = $(dir $(DLL_DEPLIBS)) +DLL_LDFLAGS = $(sort $(DLL_DIRS:%=-L%)) + +# Determine ld libs +USR_LDLIBS = $(PRODNAME_LIBS:%=-l%) $(PROD_LIBS:%=-l%) $(USR_LIBS:%=-l%)\ + $(SYS_PROD_LIBS:%=-l%) +DLL_LDLIBS = $(DLL_LIBS:%=-l%) + +#-------------------------------------------------- +# Operating system definitions +OP_SYS_INCLUDES = +OP_SYS_CFLAGS = -DUNIX + +OP_SYS_LDFLAGS = +OP_SYS_LDLIBS = -lm + +#-------------------------------------------------- +# Link definitions +LINK.c = $(CC) -o $@ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) +LINK.cc = $(CXX) -o $@ $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) + +#-------------------------------------------------- +# Allow site overrides +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.UnixCommon diff --git a/config/CONFIG.Host.WIN32 b/config/CONFIG.Host.WIN32 new file mode 100644 index 000000000..ebe61a37e --- /dev/null +++ b/config/CONFIG.Host.WIN32 @@ -0,0 +1,283 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.WIN32 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.WIN32 + +# +# You currently get Visual C++ even if you ask for GNU. +# +# !! borlund support needed here !! +# +ANSI = ACC +CPLUSPLUS = CCC + +# +# "\ " forces gnu make to keep this as one token +# +MSVISC = c:\\Program\ Files\\DevStudio\\Vc +WINLINK = link + +RCCMD = rc -l 0x409 -i . -i .. -I$(INSTALL_INCLUDE)/os/$(OS_CLASS) -I$(INSTALL_INCLUDE) $(INSTALL_INCLUDES) -I$(EPICS_BASE_INCLUDE)/os/$(OS_CLASS) -I$(EPICS_BASE_INCLUDE) -fo $@ $< + +ARCMD = lib /nologo /verbose /out:$@ + +# +# Configure OS vendor C compiler +ACC = cl + +# +# __STDC__=0 is a real great idea of Jeff that gives us both: +# 1) define STDC for EPICS code (pretend ANSI conformance) +# 2) set it to 0 to use MS C "extensions" (open for _open etc.) +# because MS uses: if __STDC__ ... disable many nice things +# +# Use of /Za would dissable DLL import/export keywords which +# EPICS include/excludes using architecture neutral macros +ACC_ANSI = $(ACC) /nologo /D__STDC__=0 +ACC_STRICT = $(ACC) /nologo /D__STDC__=0 +ACC_TRAD = $(ACC) /nologo + +# +# /W use warning level N +# (maximum (lint type) warnings at level 4) +ACC_WARN_YES = /W3 +ACC_WARN_NO = /W1 + +# +# /Ox maximum optimizations +# /MD use MSVCRT (run-time as DLL, multi-thread support) +ACC_OPT_YES = /Ox + +# +# /Zi include debugging info in object files +# /Fr create source browser file +ACC_OPT_NO = /Zi + +# +# the following options are required when +# vis c++ compiles the code (and includes +# the header files) +# +# /MT static multithreaded C RTL +# /MTd static multithreaded C RTL (debug version) +# /MD multithreaded C RTL in DLL +# /MDd multithreaded C RTL in DLL (debug version) +VISC_EPICS_DLL_NO = -DEPICS_DLL_NO +VISC_EPICS_DLL_YES = +VISC_EPICS_DLL = $(VISC_EPICS_DLL_$(SHARED_LIBRARIES)) +VISC_SFLAGS_DEBUG_NO = d +VISC_SFLAGS_DEBUG_YES = +VISC_SFLAGS_DEBUG = $(VISC_SFLAGS_DEBUG_$(HOST_OPT)) +ACC_SFLAGS_YES= /MT$(VISC_SFLAGS_DEBUG) $(VISC_EPICS_DLL) +ACC_SFLAGS_NO= /MD$(VISC_SFLAGS_DEBUG) $(VISC_EPICS_DLL) + +# +# no special libs for static link +# +ACC_SLIBS_YES= +ACC_SLIBS_NO= + +# Configure OS vendor C++ compiler +# +# __STDC__=0 is a real great idea of Jeff that gives us both: +# 1) define STDC for EPICS code (pretend ANSI conformance) +# 2) set it to 0 to use MS C "extensions" (open for _open etc.) +# because MS uses: if __STDC__ ... disable many nice things +# +# Use of /Za would dissable DLL import/export keywords which +# EPICS include/excludes using architecture neutral macros +# +# /EHsc support C++ exceptions +# /GR generate RTTI information +# +CCC = cl /EHsc /GR +CCC_NORMAL = $(CCC) /nologo /D__STDC__=0 +CCC_STRICT = $(CCC) /nologo /D__STDC__=0 +CCC_TEMPL_INST_FLAG = +ARCH_DEP_CPPFLAGS += /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE + +# +# /W use warning level N +# (maximum lint level warnings at level 4) +CCC_WARN_YES = /W3 +CCC_WARN_NO = /W1 + +# +# /Ox maximum optimizations +CCC_OPT_YES = /Ox + +# +# /Zi include debugging info in object files +# /Fr create source browser file +CCC_OPT_NO = /Zi + +# +# the following options are required when +# vis c++ compiles the code (and includes +# the header files) +# +# /MT static multithreaded C RTL +# /MTd static multithreaded C RTL (debug version) +# /MD multithreaded C RTL in DLL +# /MDd multithreaded C RTL in DLL (debug version) +CCC_SFLAGS_YES= /MT$(VISC_SFLAGS_DEBUG) $(VISC_EPICS_DLL) +CCC_SFLAGS_NO= /MD$(VISC_SFLAGS_DEBUG) $(VISC_EPICS_DLL) + +# +# no special libs for static link +# +CCC_SLIBS_YES= +CCC_SLIBS_NO= + +LINK_OPT_FLAGS_YES = /warn:3 /incremental:no /opt:ref\ +/release $(PROD_VERSION:%=/version:%) +LINK_OPT_FLAGS_NO = /warn:3 /debug /incremental:no +OPT_LDFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT)) + +ARCH_DEP_CFLAGS= + +# to identify the general architecture class: +# should be BSD, SYSV, WIN32, ... +# is: WIN32, sun4, hpux, linux, ... +# +ARCH_CLASS=WIN32 + +# ifdef WIN32 looks better that ifeq ($(ARCH_CLASS),WIN32) ?? +WIN32=1 + +EXE=.exe +OBJ=.obj +RES=.res + +# Problem: MS Visual C++ does not recognize *.cc as C++ source, +# we have to compile xx.cc using the flag -Tp xx.cc, +# i.e. -Tp has to be immediately before the source file name +SOURCE_CXXFLAG = /Tp + +# Operating system flags +OP_SYS_CFLAGS = + +# +# WIN32 specific include files +# +#OP_SYS_INCLUDES = -I$(EPICS_BASE_INCLUDE)\\os\\WIN32 + +# +# These are now added to the individual makefiles that use them in order to +# speed up the build +# +#OP_SYS_LDLIBS = user32.lib kernel32.lib advapi32.lib winmm.lib +OP_SYS_LDLIBS = + +# Files and flags needed to link DLLs (used in RULES.Host) +# +# Strange but seems to work without: WIN32_DLLFLAGS should contain +# an entry point: +# '-entry:_DllMainCRTStartup$(DLLENTRY)' +DLLENTRY = @12 + +WIN32_DLLFLAGS = /subsystem:windows /dll $(OPT_LDFLAGS) + +# +# specify dll .def file only if it exists +# +DLL_DEF_FLAG = $(addprefix /def:,$(wildcard ../$(LIBRARY).def)) + +# HOST_OPT_FLAGS is part of CFLAGS/CXXFLAGS, +# which in turn are used in COMPILE.c[c] +# +# If we compile a .c, .cc into an $(OBJ), +# we test if this object is part of the +# library objects LIBOBJS. +# If so, we define _WINDLL so that +# e.g. include/shareLib.h works correctly. +# +HOST_OPT_FLAGS += $(subst $@, /_WINDLL, $(findstring $@,$(LIBOBJS))) + +# +# A WIN32 dll has three parts: +# x.dll: the real dll (SHRLIBNAME) +# x.lib: what you link to progs that use the dll (LIBNAME) +# x.exp: what you need to build the dll (in no variable) +# +LINK.shrlib = $(WINLINK) /nologo $(WIN32_DLLFLAGS) /implib:$(DLL_LINK_LIBNAME) /out:$(SHRLIBNAME) $(DLL_DEF_FLAG) + +# adjust names of libraries to build +# +# But: if there are no objects LIBOBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +SHRLIBNAME = $(LIBRARY).dll + +# +# Under WIN32 we have the unique situation where the DLL link creates the +# DLL link library xxx.lib and we need to be very careful to avoid replacing +# the xxx.lib created by the dll link with an xxx.lib created by $(AR). +# Therefore, the object library is named xxxObj.lib +# +# SHARED_LIBRARIES is YES if we are building a DLL and NO if we aren't +# +DLL_LINK_LIBNAME_YES = $(LIBRARY).lib +DLL_LINK_LIBNAME = $(DLL_LINK_LIBNAME_$(SHARED_LIBRARIES)) +LIBNAME = $(LIBRARY)Obj.lib + +# dll install location +INSTALL_SHRLIB = $(INSTALL_BIN) + +#-------------------------------------------------- +# Dependancy definitions +OBJECT_LIB_EXT_YES = Obj.lib # object library extension for static build +OBJECT_LIB_EXT_NO = .lib # object library extension for dynamic build +OBJECT_LIB_EXT = $(OBJECT_LIB_EXT_$(STATIC_BUILD)) +COND_PROD_DEPLIBS = $(foreach prod,$(PROD), $(foreach lib, $($(basename $(prod))_LIBS),\ + $(firstword $($(lib)_DIR) $(EPICS_BASE_LIB))/$(lib)$(OBJECT_LIB_EXT))) +PRODNAME_DEPLIBS = $(foreach lib,$(PRODNAME_LIBS),$(firstword $($(lib)_DIR) $(EPICS_BASE_LIB))/$(lib)$(OBJECT_LIB_EXT)) +PROD_DEPLIBS = $(foreach lib,$(PROD_LIBS),$(firstword $($(lib)_DIR) $(EPICS_BASE_LIB))/$(lib)$(OBJECT_LIB_EXT)) + +USR_DEPLIBS = $(foreach lib,$(USR_LIBS),$(firstword $($(lib)_DIR) $(EPICS_BASE_LIB))/$(lib)$(OBJECT_LIB_EXT)) + +# by default the libraries used when linking the DLL are just +# PROD_LIBS ans SYS_PROD_LIBS minus the DLL name +DLL_LIBS = $(patsubst $(LIBRARY),, $(PROD_LIBS)) + +# +# EPICS libs that we need to link the DLL with +# (it isnt necessary to rebuild the dll if these change) +DLL_DEPLIBS = $(foreach lib,$(DLL_LIBS),$(firstword $($(lib)_DIR) $(EPICS_BASE_LIB))/$(lib).lib) + +USR_LDLIBS = $(PRODNAME_DEPLIBS) $(PROD_DEPLIBS) $(USR_DEPLIBS) $(SYS_PROD_LIBS:%=%.lib) + +DLL_LDLIBS = $(DLL_DEPLIBS) $(SYS_PROD_LIBS:%=%.lib) $(OP_SYS_LDLIBS) + +LINK.c = $(WINLINK) -nologo $(LDFLAGS) -out:$@ +LINK.cc = $(WINLINK) -nologo $(LDFLAGS) -out:$@ + +#-------------------------------------------------- +# Determine ld flags +#USR_DIRS = $(dir $(PRODNAME_DEPLIBS)) $(dir $(PROD_DEPLIBS))\ +# $(dir $(USR_DEPLIBS)) +#USR_LDFLAGS += $(sort $(USR_DIRS:%=-L%)) + +# Overrides for CONFIG_COMMON default +POSIX_CPPFLAGS_YES = + +#-------------------------------------------------- +# UseManifestTool.pl checks MS Visual c++ compiler version number to +# decide whether or not to use the Manifest Tool command to embed the +# linker created .manifest file into a library or product target. +# useManifestTool.pl returns 0(don't use) or 1(use). +# +ifeq ($(shell $(PERL) $(EPICS_BASE_TOOLS)/useManifestTool.pl),1) +MT_DLL_COMMAND = mt.exe /manifest $@.manifest "/outputresource:$@;\#2" +MT_EXE_COMMAND = mt.exe /manifest $@.manifest "/outputresource:$@;\#1" +endif + diff --git a/config/CONFIG.Host.cygwin-x86 b/config/CONFIG.Host.cygwin-x86 new file mode 100644 index 000000000..06da4d594 --- /dev/null +++ b/config/CONFIG.Host.cygwin-x86 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# cygwin-x86 is the new name for cygwin32 +-include $(EPICS_BASE)/config/CONFIG.Host.cygwin32 diff --git a/config/CONFIG.Host.cygwin32 b/config/CONFIG.Host.cygwin32 new file mode 100644 index 000000000..cdae20d8c --- /dev/null +++ b/config/CONFIG.Host.cygwin32 @@ -0,0 +1,63 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.cygwin32 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.cygwin32 + +ARCH_CLASS = cygwin32 + +# cygwin32 is a unix-like arch +# include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +EXE=.exe + +#========================== +#These overrides remove -ansi because -ansi eliminates strdup +GCC_ANSI = $(GCC) +GCC_STRICT = $(GCC) -pedantic +G++_NORMAL = $(G++) -pedantic +G++_STRICT = $(G++) -pedantic + +#========================== +#This override will eliminate warnings for old R3.13 extensions +#GCC_WARN_YES = + +#========================== +# These are overrides of ANSI and CPLUSPLUS values in CONFIG_SITE +# since OS vendor compilers ACC and CCC are not defined here for cygwin32 +ANSI=GCC +CPLUSPLUS=G++ +# Shared libraries not implemented yet +SHARED_LIBRARIES=NO +#========================== +# Fix for cygwin32 beta17.1 includes +#OP_SYS_INCLUDES += /usr/local/include/g++ +#========================== + +ARCH_DEP_CPPFLAGS = -DCYGWIN32 -U_WIN32 +ARCH_DEP_LDLIBS = +OP_SYS_LDLIBS = + +# cygwin32 overrides to eliminate following warning message - +# -fPIC ignored for target (all code is position independent) +GCC_DEP_CFLAGS = -D_REENTRANT +G++_DEP_CFLAGS = -D_REENTRANT + +#POSIX_SOURCE eliminates select() +#POSIX_CPPFLAGS_YES = -D_POSIX_SOURCE +POSIX_CPPFLAGS_YES = -D_POSIX_THREADS -D_POSIX_TIMERS + +# Runtime ldflags +RUNTIME_LIBS =$(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)\ + $(EPICS_EXTENSIONS)/lib/$(EPICS_HOST_ARCH) +RUNTIME_LDFLAGS = $(RUNTIME_LIBS:%=-Wl,-rpath,%) + diff --git a/config/CONFIG.Host.darwin-ppc b/config/CONFIG.Host.darwin-ppc new file mode 100644 index 000000000..360c87bc9 --- /dev/null +++ b/config/CONFIG.Host.darwin-ppc @@ -0,0 +1,12 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.Darwin diff --git a/config/CONFIG.Host.darwin-ppcx86 b/config/CONFIG.Host.darwin-ppcx86 new file mode 100644 index 000000000..1f703bacb --- /dev/null +++ b/config/CONFIG.Host.darwin-ppcx86 @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.Darwin +OP_SYS_CFLAGS += "-isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386" +OP_SYS_LDFLAGS += "-Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386" diff --git a/config/CONFIG.Host.darwin-x86 b/config/CONFIG.Host.darwin-x86 new file mode 100644 index 000000000..360c87bc9 --- /dev/null +++ b/config/CONFIG.Host.darwin-x86 @@ -0,0 +1,12 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.Darwin diff --git a/config/CONFIG.Host.freebsd b/config/CONFIG.Host.freebsd new file mode 100644 index 000000000..fb646c1ca --- /dev/null +++ b/config/CONFIG.Host.freebsd @@ -0,0 +1,35 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.freebsd + +OS_CLASS = freebsd + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +CODE_CPPFLAGS = -D_REENTRANT + +POSIX_CPPFLAGS = -D_POSIX_THREADS +POSIX_LDLIBS = -lpthread + +# -D_BSD_SOURCE for gethostname() in unistd.h as needed by cacChannelIO.cpp. +OP_SYS_CPPFLAGS += -D_BSD_SOURCE +OP_SYS_CPPFLAGS += -Dfreebsd + +# Set runtime path for shared libraries +RUNTIME_LDFLAGS = $(SHRLIB_SEARCH_FULLPATHDIRS:%=-Wl,-rpath,%) + +# Definitions used when COMMANDLINE_LIBRARY is READLINE +LDLIBS_READLINE = -lreadline -lcurses + +GNU_LDLIBS_YES = -lgcc_pic + diff --git a/config/CONFIG.Host.freebsd-x86 b/config/CONFIG.Host.freebsd-x86 new file mode 100644 index 000000000..f6fb332f1 --- /dev/null +++ b/config/CONFIG.Host.freebsd-x86 @@ -0,0 +1,15 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 + +ARCH_CLASS = x86 + +-include $(EPICS_BASE)/config/CONFIG.Host.freebsd + +ARCH_DEP_CPPFLAGS += -D_X86_ diff --git a/config/CONFIG.Host.freebsd-x86_64 b/config/CONFIG.Host.freebsd-x86_64 new file mode 100644 index 000000000..5b6a8c3c6 --- /dev/null +++ b/config/CONFIG.Host.freebsd-x86_64 @@ -0,0 +1,15 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 + +ARCH_CLASS = x86_64 + +-include $(EPICS_BASE)/config/CONFIG.Host.freebsd + +ARCH_DEP_CPPFLAGS += -D_X86_64_ diff --git a/config/CONFIG.Host.hp700 b/config/CONFIG.Host.hp700 new file mode 100644 index 000000000..df7bd2050 --- /dev/null +++ b/config/CONFIG.Host.hp700 @@ -0,0 +1,74 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.hp700 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.hp700 + +ARCH_CLASS = hpux + +SHARED_LIBRARIES=YES + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +SHRLIB_SUFFIX = .sl + +AR = ar +ARFLAGS = -rc + +# Configure OS vendor C compiler +ACC = cc +ACC_ANSI = $(ACC) -Aa +ACC_STRICT = $(ACC) -Aa +ACC_TRAD = $(ACC) -Ac +ACC_WARN_YES = +ACC_WARN_NO = -w +ACC_OPT_YES = -O +ACC_OPT_NO = -g +# Always keep libc shared to force using the one supplied with the +# target machine (HP system and libc must match - important e.g. for +# multi-CPU-systems) +ACC_SLDFLAGS_YES= -Wl,-a,archive -l:libc.sl +ACC_SFLAGS_NO = +ACC_SLIBS_YES = +ACC_SLIBS_NO = +ACC_SHRLIB_LDFLAGS_YES = -b + +# aCC HP C++ compiler +CCC = aCC +# Suppress bogus warnings created by the aCC compiler +CCC_NORMAL = $(CCC) -AA -Aa -mt +W302 +W829 +W818 +W392 +W469 +W495 +W749 +W667 +W392 +W684 +CCC_STRICT = $(CCC) -AA -Aa -mt +CCC_TEMPL_INST_FLAG = +CCC_WARN_YES = +w +CCC_WARN_NO = +CCC_OPT_YES = +O3 +CCC_OPT_NO = -g +# Always keep libc shared to force using the one supplied with the +# target machine (HP system and libc must match - important e.g. for +# multi-CPU-systems) +CCC_SLDFLAGS_YES= -Wl,-a,archive -l:libc.sl +CCC_SFLAGS_NO= +CCC_SLIBS_YES = +CCC_SLIBS_NO= +CCC_SHRLIB_LDFLAGS_YES = -b +CCC_DEPENDS_FLAG = +m + +# Portability across different PA-RISC architecture versions, position +# independent code, "-mt" (s.a.) handles all the posix stuff +ARCH_DEP_CPPFLAGS = -DHP_UX +DAportable +z +ARCH_DEP_CFLAGS = -D_HPUX_SOURCE + +# Set runtime path for shared libraries +empty:= # trick from the make docs... +space:= $(empty) $(empty) +RUNTIME_LDFLAGS_YES = -Wl,+b$(subst $(space),:,$(sort $(SHRLIB_SEARCH_DIRS))),+s +RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(SHARED_LIBRARIES)) diff --git a/config/CONFIG.Host.hpux-parisc b/config/CONFIG.Host.hpux-parisc new file mode 100644 index 000000000..da6b4bec7 --- /dev/null +++ b/config/CONFIG.Host.hpux-parisc @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# hpux-parisc is the new name for hp700 +-include $(EPICS_BASE)/config/CONFIG.Host.hp700 diff --git a/config/CONFIG.Host.hpux-parisc-gnu b/config/CONFIG.Host.hpux-parisc-gnu new file mode 100644 index 000000000..21219688d --- /dev/null +++ b/config/CONFIG.Host.hpux-parisc-gnu @@ -0,0 +1,49 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.hpux-parisc-gnu +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.hpux-parisc-gnu + +ARCH_CLASS = hpux + +SHARED_LIBRARIES=YES + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +AR = ar -rc +ARCMD = $(AR) $@ + +#========================== +# These are overrides of ANSI and CPLUSPLUS values in CONFIG_SITE +ANSI=GCC +CPLUSPLUS=G++ +#========================== + +# Always keep libc shared to force using the one supplied with the +# target machine (HP system and libc must match - important e.g. for +# multi-CPU-systems) +GCC_SLDFLAGS_YES = -l:libc.sl +G++_SLDFLAGS_YES = -l:libc.sl + +# socket and nsl needed by libca.a +#ARCH_DEP_LDLIBS += -lsocket -lnsl +ARCH_DEP_LDLIBS += -lpthread +ARCH_DEP_CPPFLAGS += -D_PTHREADS -DOSITHREAD_USE_DEFAULT_STACK + +# Allows R3.13 built extensions to load R3.14 shared libs +SYS_DLL_LIBS_hpux += pthread + +# Set runtime path for shared libraries +empty:= # trick from the make docs... +space:= $(empty) $(empty) +RUNTIME_LDFLAGS_YES = -Wl,+b$(subst $(space),:,$(sort $(SHRLIB_SEARCH_DIRS))),+s +RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(SHARED_LIBRARIES)) diff --git a/config/CONFIG.Host.linux-ppc b/config/CONFIG.Host.linux-ppc new file mode 100644 index 000000000..dae633a29 --- /dev/null +++ b/config/CONFIG.Host.linux-ppc @@ -0,0 +1,24 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.Linux + +ARCH_CLASS = ppc + +ARCH_DEP_CPPFLAGS += -D_ppc_ + +GNU_DIR = /usr + +GCC = $(GNU_BIN)/gcc +G++ = $(GNU_BIN)/g++ + +ARCH_DEP_LDLIBS = -lgcc + diff --git a/config/CONFIG.Host.linux-x86 b/config/CONFIG.Host.linux-x86 new file mode 100644 index 000000000..4713a4370 --- /dev/null +++ b/config/CONFIG.Host.linux-x86 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# linux-x86 is the new name for linux +-include $(EPICS_BASE)/config/CONFIG.Host.Linux diff --git a/config/CONFIG.Host.linux-x86-debug b/config/CONFIG.Host.linux-x86-debug new file mode 100644 index 000000000..e023fbbc3 --- /dev/null +++ b/config/CONFIG.Host.linux-x86-debug @@ -0,0 +1,12 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.Linux diff --git a/config/CONFIG.Host.linux-x86_64 b/config/CONFIG.Host.linux-x86_64 new file mode 100644 index 000000000..2842f8258 --- /dev/null +++ b/config/CONFIG.Host.linux-x86_64 @@ -0,0 +1,17 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.Linux + +ARCH_CLASS = Linux + +ARCH_DEP_CFLAGS = -D_X86_64_ -Dlinux + diff --git a/config/CONFIG.Host.linux-x86_64-debug b/config/CONFIG.Host.linux-x86_64-debug new file mode 100644 index 000000000..c6e6ddfd8 --- /dev/null +++ b/config/CONFIG.Host.linux-x86_64-debug @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.linux-x86_64 + diff --git a/config/CONFIG.Host.sgi b/config/CONFIG.Host.sgi new file mode 100644 index 000000000..6ad0ee521 --- /dev/null +++ b/config/CONFIG.Host.sgi @@ -0,0 +1,59 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.sgi +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.sgi + +ARCH_CLASS = sgi + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +AR = ar + +# Configure OS vendor C compiler +ACC = cc +ACC_ANSI = $(ACC) -xansi +ACC_STRICT = $(ACC) -xansi +ACC_TRAD = $(ACC) -cckr -Xs +ACC_WARN_YES = +ACC_WARN_NO = -w +ACC_OPT_YES = -O +ACC_OPT_NO = -g +ACC_SFLAGS_YES= -Bstatic +ACC_SFLAGS_NO= +ACC_SLIBS_YES = +ACC_SLIBS_NO= +ACC_SHRLIB_CFLAGS_YES = -KPIC +ACC_SHRLIB_LDFLAGS_YES = -shared + +# Configure OS vendor C++ compiler +CCC = CC +CCC_NORMAL = $(CCC) +CCC_STRICT = $(CCC) +CCC_TEMPL_INST_FLAG = +CCC_WARN_YES = +CCC_WARN_NO = +CCC_OPT_YES = -O +CCC_OPT_NO = -g +CCC_SFLAGS_YES= -Bstatic +CCC_SFLAGS_NO= +CCC_SLIBS_YES = +CCC_SLIBS_NO= +CCC_DEPENDS_FLAG = +CCC_SHRLIB_CFLAGS_YES = -KPIC +CCC_SHRLIB_LDFLAGS_YES = -shared + +####KRCC = ?? + +ARCH_DEP_CFLAGS = -DSGI +ARCH_DEP_LDFLAGS = + diff --git a/config/CONFIG.Host.solaris b/config/CONFIG.Host.solaris new file mode 100644 index 000000000..7eb59483a --- /dev/null +++ b/config/CONFIG.Host.solaris @@ -0,0 +1,95 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.solaris +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.solaris + +ARCH_CLASS = solaris + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +# +# required by sun's C++ compiler +# +AR = ar +_AR = $(AR) $(ARFLAGS) +G++_AR = $(_AR) +CCC_AR = $(CCC) -xar -o +ARCMD = $($(CPLUSPLUS)_AR) $@ + +RANLIB = + +SPARCWORKS = /opt/SUNWspro + +# Configure OS vendor C compiler +ACC = $(SPARCWORKS)/bin/cc +ACC_ANSI = $(ACC) -Xa -v +ACC_STRICT = $(ACC) -Xc -v +ACC_TRAD = $(ACC) -Xs +ACC_DEP_CFLAGS = -KPIC -D_REENTRANT +ACC_WARN_YES = +ACC_WARN_NO = -w +ACC_OPT_YES = -O +ACC_OPT_NO = -g +ACC_SFLAGS_YES= -Bstatic +ACC_SFLAGS_NO= +ACC_SLIBS_YES= -lw -lintl -Bdynamic -ldl -Bstatic +ACC_SLIBS_NO= +ACC_SHRLIB_CFLAGS_YES = +ACC_SHRLIB_LDFLAGS_YES = -G -h $@ + +# Configure OS vendor C++ compiler +CCC = $(SPARCWORKS)/bin/CC +CCC_NORMAL = $(CCC) +p +CCC_STRICT = $(CCC) +p +CCC_DEP_CFLAGS = -KPIC -D_REENTRANT +CCC_TEMPL_INST_FLAG = +CCC_WARN_YES = +w +CCC_WARN_NO = +CCC_OPT_YES = -O +CCC_OPT_NO = -g +CCC_SFLAGS_YES= -Bstatic +CCC_SFLAGS_NO= +CCC_SLIBS_YES= -lw -lintl -Bdynamic -ldl -Bstatic +CCC_SLIBS_NO= +CCC_DEPENDS_FLAG = -xM1 +CCC_SHRLIB_CFLAGS_YES = +CCC_SHRLIB_LDFLAGS_YES = -G -h $@ + +SOLARIS_VERSION = $(subst 5.,,$(shell uname -r)) +ARCH_DEP_CPPFLAGS += -DSOLARIS=$(SOLARIS_VERSION) +POSIX_CPPFLAGS_YES += -D__EXTENSIONS__ -mt + +ARCH_DEP_LDFLAGS += -mt + +# socket and nsl needed by libca.a when SHARED_LIBRARIES = NO +ARCH_DEP_LDLIBS += -lsocket -lnsl +ARCH_DEP_LDLIBS += -lposix4 -lpthread + +ARCH_DEP_LDLIBS_8 += -lCrun -lc -lCstd +ARCH_DEP_LDLIBS_9 += -lCrun -lc -lCstd +ARCH_DEP_LDLIBS_10 += -lCrun -lc -lCstd +ARCH_DEP_LDLIBS += $(ARCH_DEP_LDLIBS_$(SOLARIS_VERSION)) + +#Allows R3.13 built extensions to load R3.14 shared libs +SYS_DLL_LIBS_solaris_8 = Crun +SYS_DLL_LIBS_solaris_9 = Crun +SYS_DLL_LIBS_solaris_10 = Crun +SYS_DLL_LIBS_solaris += posix4 pthread $(SYS_DLL_LIBS_solaris_$(SOLARIS_VERSION)) + +# Runtime ldflags +RUNTIME_LDFLAGS =$(addprefix -R,$(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)\ + $(EPICS_EXTENSIONS)/lib/$(EPICS_HOST_ARCH)) + +OP_SYS_LDFLAGS = -z ignore -z combreloc -z lazyload + + diff --git a/config/CONFIG.Host.solaris-sparc b/config/CONFIG.Host.solaris-sparc new file mode 100644 index 000000000..bfdc2204c --- /dev/null +++ b/config/CONFIG.Host.solaris-sparc @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG.Host.solaris diff --git a/config/CONFIG.Host.solaris-sparc-debug b/config/CONFIG.Host.solaris-sparc-debug new file mode 100644 index 000000000..c5f3520d7 --- /dev/null +++ b/config/CONFIG.Host.solaris-sparc-debug @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG.Host.solaris + diff --git a/config/CONFIG.Host.solaris-sparc-gnu b/config/CONFIG.Host.solaris-sparc-gnu new file mode 100644 index 000000000..6e22e0e2e --- /dev/null +++ b/config/CONFIG.Host.solaris-sparc-gnu @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG.Host.solarisGnu + diff --git a/config/CONFIG.Host.solaris-sparc-staticlib b/config/CONFIG.Host.solaris-sparc-staticlib new file mode 100644 index 000000000..c5f3520d7 --- /dev/null +++ b/config/CONFIG.Host.solaris-sparc-staticlib @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG.Host.solaris + diff --git a/config/CONFIG.Host.solaris-sparc64 b/config/CONFIG.Host.solaris-sparc64 new file mode 100644 index 000000000..965b88d2a --- /dev/null +++ b/config/CONFIG.Host.solaris-sparc64 @@ -0,0 +1,18 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG.Host.solaris + +ARCH_DEP_CFLAGS += -xarch=generic64 +ARCH_DEP_CXXFLAGS += -xarch=generic64 +ARCH_DEP_LDFLAGS += -xarch=generic64 + diff --git a/config/CONFIG.Host.solaris-sparc64-gnu b/config/CONFIG.Host.solaris-sparc64-gnu new file mode 100644 index 000000000..f1eac573f --- /dev/null +++ b/config/CONFIG.Host.solaris-sparc64-gnu @@ -0,0 +1,18 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG.Host.solarisGnu + +ARCH_DEP_CFLAGS += -mcpu=v9 -m64 +ARCH_DEP_CXXFLAGS += -mcpu=v9 -m64 +ARCH_DEP_LDFLAGS += -mcpu=v9 -m64 + diff --git a/config/CONFIG.Host.solaris-x86 b/config/CONFIG.Host.solaris-x86 new file mode 100644 index 000000000..4ee29a35e --- /dev/null +++ b/config/CONFIG.Host.solaris-x86 @@ -0,0 +1,19 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.solaris-x86 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.solaris-x86 + +-include $(EPICS_BASE)/config/CONFIG.Host.solaris + +# Solaris on x86 +ARCH_DEP_CPPFLAGS += -D_X86_ + diff --git a/config/CONFIG.Host.solaris-x86-gnu b/config/CONFIG.Host.solaris-x86-gnu new file mode 100644 index 000000000..180d5da60 --- /dev/null +++ b/config/CONFIG.Host.solaris-x86-gnu @@ -0,0 +1,19 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.solaris-x86-gnu +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.solaris-x86 + +-include $(EPICS_BASE)/config/CONFIG.Host.solarisGnu + +# Solaris on x86 +ARCH_DEP_CPPFLAGS += -D_X86_ + diff --git a/config/CONFIG.Host.solarisGnu b/config/CONFIG.Host.solarisGnu new file mode 100644 index 000000000..cd7dd634a --- /dev/null +++ b/config/CONFIG.Host.solarisGnu @@ -0,0 +1,22 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.solarisGnu +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.solarisGnu + +include $(EPICS_BASE)/config/CONFIG.Host.solaris + +#========================== +# These are overrides of ANSI and CPLUSPLUS values in CONFIG_SITE +ANSI=GCC +CPLUSPLUS=G++ +#========================== + diff --git a/config/CONFIG.Host.sun4 b/config/CONFIG.Host.sun4 new file mode 100644 index 000000000..170b259c7 --- /dev/null +++ b/config/CONFIG.Host.sun4 @@ -0,0 +1,98 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Host.sun4 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.sun4 + +ARCH_CLASS = sun4 + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +# +# required by sun's C++ compiler +# +AR = ar +_AR = $(AR) $(ARFLAGS) +G++_AR = $(_AR) +CCC_AR = $(CCC) -xar -o +ARCMD = $($(CPLUSPLUS)_AR) $@ + +RANLIB = ranlib +RANLIBFLAGS = -t + +# Configure OS vendor C compilers +ACC = /usr/lang/acc +ACC_ANSI = $(ACC) -Xa +ACC_STRICT = $(ACC) -Xc +ACC_TRAD = $(ACC) -Xs +ACC_WARN_YES = -vc +ACC_WARN_NO = -w +ACC_OPT_YES = -O +ACC_OPT_NO = -g +ACC_SFLAGS_YES= -Bstatic +ACC_SFLAGS_NO= +ACC_SLIBS_YES= +ACC_SLIBS_NO= +#ACC_OP_SYS_LDFLAGS = -L$(SPECIAL_LANG) +ACC_SHRLIB_CFLAGS_YES = -pic +ACC_SHRLIB_LDFLAGS_YES = -assert pure-text -h $@ + +SUNCC = cc +SUNCC_ANSI = echo SUNCC_ANSI not defined +SUNCC_STRICT = echo SUNCC_STRICT not defined +SUNCC_TRAD = $(SUNCC) +SUNCC_WARN_YES = +SUNCC_WARN_NO = -w +SUNCC_OPT_YES = -O +SUNCC_OPT_NO = -g +SUNCC_SFLAGS_YES= -Bstatic +SUNCC_SFLAGS_NO= +SUNCC_SLIBS_YES= +SUNCC_SLIBS_NO= +SUNCC_SHRLIB_CFLAGS_YES = -pic +SUNCC_SHRLIB_LDFLAGS_YES = -assert pure-text -h $@ + +# Configure OS vendor C++ compiler +CCC = /usr/lang/CC +CCC_NORMAL = $(CCC) +CCC_STRICT = $(CCC) +CCC_TEMPL_INST_FLAG = +CCC_WARN_YES = +CCC_WARN_NO = +CCC_TEMPL_INST_FLAG = +CCC_WARN_YES = +w +CCC_WARN_NO = +CCC_OPT_YES = -O +CCC_OPT_NO = -g +CCC_SFLAGS_YES= -Bstatic +CCC_SFLAGS_NO= +CCC_SLIBS_YES= +CCC_SLIBS_NO= +CCC_SHRLIB_CFLAGS_YES = -pic +CCC_SHRLIB_LDFLAGS_YES = -assert pure-text -h $@ +CCC_DEPENDS_FLAG = -xM1 + +GCC_ANSI += -D__USE_FIXED_PROTOTYPES__ +GCC_STRICT += -D__USE_FIXED_PROTOTYPES__ + +ACC_ARCH_DEP_LDLIBS = +GCC_ARCH_DEP_LDLIBS = -liberty +ARCH_DEP_LDLIBS = $($(ANSI)_ARCH_DEP_LDLIBS) + +ARCH_DEP_CFLAGS = -DSUNOS4 + +#SPECIAL_LANG = /usr/lang/SC3.0.1 +#SPECIAL_LANG = /usr/lang/lib + +# Runtime ldflags +RUNTIME_LDFLAGS =$(addprefix -R,$(EPICS_BASE)/lib/$(EPICS_HOST_ARCH)\ + $(EPICS_EXTENSIONS)/lib/$(EPICS_HOST_ARCH)) diff --git a/config/CONFIG.Host.sun4-sparc b/config/CONFIG.Host.sun4-sparc new file mode 100644 index 000000000..c3c00f312 --- /dev/null +++ b/config/CONFIG.Host.sun4-sparc @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# sun4-sparc is the new name for sun4 +-include $(EPICS_BASE)/config/CONFIG.Host.sun4 diff --git a/config/CONFIG.Host.win32-x86 b/config/CONFIG.Host.win32-x86 new file mode 100644 index 000000000..96a7d24f4 --- /dev/null +++ b/config/CONFIG.Host.win32-x86 @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +# win32-x86 is the new name for WIN32 +-include $(EPICS_BASE)/config/CONFIG.Host.WIN32 + diff --git a/config/CONFIG.Host.win32-x86-borland b/config/CONFIG.Host.win32-x86-borland new file mode 100644 index 000000000..0fa642e2b --- /dev/null +++ b/config/CONFIG.Host.win32-x86-borland @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# win32-x86-borland is the new name for Borland +-include $(EPICS_BASE)/config/CONFIG.Host.Borland diff --git a/config/CONFIG.Host.win32-x86-cygwin b/config/CONFIG.Host.win32-x86-cygwin new file mode 100644 index 000000000..6819a837b --- /dev/null +++ b/config/CONFIG.Host.win32-x86-cygwin @@ -0,0 +1,62 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.win32-x86-cygwin + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +EXE = .exe + +SHARED_LIBRARIES = NO + +ARCH_CLASS = WIN32 + +AR = ar -rc +ARCMD = $(AR) $@ +RANLIB = ranlib -t + +ANSI=GCC +CPLUSPLUS=G++ + +# Dont use -ansi: -ansi eliminates strdup and _tempnam +GCC_ANSI = $(GCC) +GCC_STRICT = $(GCC) -pedantic +G++_NORMAL = $(G++) -pedantic +G++_STRICT = $(G++) -pedantic + +# Dont use -fPIC: with -fPIC we get +# "warning: -fPIC ignored for target (all code is position independent) " +GCC_DEP_CFLAGS = +G++_DEP_CFLAGS = + +OP_SYS_CFLAGS = +OP_SYS_CPPFLAGS = -mno-cygwin +OP_SYS_CXXFLAGS += -D__cplusplus +OP_SYS_LDFLAGS += -mno-cygwin + +POSIX_CPPFLAGS_YES = + +# With no-cygwin option: +# compiler defines _X86_ 1 +# compiler defines __MSVCRT__ 1 +# compiler defines __MINGW32__ 1 +# compiler defines __WIN32 1 +# compiler defines __WIN32__ 1 +# compiler defines _WIN32 1 +# compiler defines WIN32 1 +# compiler defines WINNT 1 +# compiler does not define __unix __unix__ unix +# compiler does not define __CYGWIN__ __CYGWIN32__ + +ARCH_DEP_LDLIBS = -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm + diff --git a/config/CONFIG.Host.win32-x86-debug b/config/CONFIG.Host.win32-x86-debug new file mode 100644 index 000000000..7e1c50261 --- /dev/null +++ b/config/CONFIG.Host.win32-x86-debug @@ -0,0 +1,12 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG.Host.WIN32 diff --git a/config/CONFIG.Host.win32-x86-mingw b/config/CONFIG.Host.win32-x86-mingw new file mode 100644 index 000000000..9808cd36e --- /dev/null +++ b/config/CONFIG.Host.win32-x86-mingw @@ -0,0 +1,58 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. +# Sites may override these definitions in CONFIG_SITE.Host.win32-x86-mingw + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG.Host.UnixCommon + +EXE = .exe + +SHARED_LIBRARIES = NO + +ARCH_CLASS = WIN32 + +AR = ar -rc +ARCMD = $(AR) $@ +RANLIB = ranlib -t + +ANSI=GCC +CPLUSPLUS=G++ + +# Compiler defines _X86_ 1 + +# Compiler defines __MSVCRT__ 1 +# Compiler defines __MINGW32__ 1 +# Compiler defines __WIN32 1 +# Compiler defines __WINNT 1 +# Compiler defines __WINNT__ 1 +# Compiler defines __WIN32__ 1 +# Compiler defines _WIN32 1 +# Compiler defines WIN32 1 +# Compiler defines WINNT 1 +# Compiler does not define __unix __unix__ unix + +OP_SYS_CFLAGS = +OP_SYS_CXXFLAGS += -D__cplusplus + +GCC_DEP_CFLAGS = +G++_DEP_CFLAGS = + +POSIX_CPPFLAGS_YES = + +ARCH_DEP_LDLIBS = -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm + +# Remove -ansi compile option for gcc (does not allow c++ type comments). +GCC_ANSI = $(GCC) +GCC_STRICT = $(GCC) -pedantic +G++_NORMAL = $(G++) -pedantic +G++_STRICT = $(G++) -pedantic diff --git a/config/CONFIG.Vx b/config/CONFIG.Vx new file mode 100644 index 000000000..7978bca6b --- /dev/null +++ b/config/CONFIG.Vx @@ -0,0 +1,149 @@ +#************************************************************************* +# 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 +# +# This file contains definitions for Vx builds + +#-------------------------------------------------- +# operating system class (include/os/) +OS_CLASS = vxWorks + +#-------------------------------------------------- +# vxWorks directory definitions + +# The definitions VX_DIR, VX_GNU, GNU_DIR and GNU_LIB +# can be overridden for specific host architectures +# by creating a CONFIG_SITE.Vx. file with +# the override definitions. + +# Tornado directory definitions +VX_INCLUDE_YES = $(VX_DIR)/target/h +VX_GNU_YES = $(VX_DIR)/host/$(WIND_HOST_TYPE) +VX_GNU_BIN_YES = $(VX_GNU)/bin +VX_GNU_LIB_YES = $(VX_GNU)/lib + +# pre Torando directory definitions +VX_INCLUDE_NO = $(VX_DIR)/h +VX_GNU_BIN_NO = $(VX_GNU)/$(HOST_ARCH).$(ARCH_CLASS)/bin +VX_GNU_LIB_NO = $(VX_GNU)/$(HOST_ARCH).$(ARCH_CLASS)/lib + +# directory definitions +VX_DIR = $(VX_DIR_$(TORNADO)) +VX_INCLUDE = $(VX_INCLUDE_$(TORNADO)) +VX_GNU = $(VX_GNU_$(TORNADO)) +GNU_BIN = $(VX_GNU_BIN_$(TORNADO)) +GNU_LIB = $(VX_GNU_LIB_$(TORNADO)) +export GCC_EXEC_PREFIX = $(GNU_LIB)/gcc-lib/ + +#-------------------------------------------------- +# VxWorks command definitions + +GCC = $(GNU_BIN)/cc$(CMPLR_SUFFIX) -B$(GNU_LIB)/gcc-lib/ -nostdinc +AR = $(GNU_BIN)/ar$(CMPLR_SUFFIX) +CPP = $(GNU_BIN)/cc$(CMPLR_SUFFIX) -B$(GNU_LIB)/gcc-lib/ -nostdinc -x c -E +RANLIB = $(GNU_BIN)/ranlib$(CMPLR_SUFFIX) +LD = $(GNU_BIN)/ld$(CMPLR_SUFFIX) -r + +#-------------------------------------------------- +# Tornado C++ crosscompiler definitions + +CPLUSPLUS_YES = G++ +G++ = $(GNU_BIN)/cc$(CMPLR_SUFFIX) -B$(GNU_LIB)/gcc-lib/ -nostdinc +LD_G++ = $(GNU_BIN)/ld$(CMPLR_SUFFIX) -r +NM = $(GNU_BIN)/nm$(CMPLR_SUFFIX)$(HOSTEXE) + +#-------------------------------------------------- +# Pre Tornado C++ crosscompiler definitions +# These are pre tornado definitions for Hideos builds (defined for 68k only) + +CPLUSPLUS_NO = CCC +CCC = $(GNU_DIR)/bin/sun3-g++ -B$(GNU_DIR)/lib/gcc-lib/ -nostdinc -DEXPL_TEMPL +LD_CCC = $(GNU_DIR)/bin/sun3-ld $(OLD_ARCH_DEP_LDFLAGS) -r + +CCC_NORMAL = $(CCC) $(OLD_ARCH_DEP_CFLAGS) +CCC_STRICT = $(CCC) -ansi -pedantic -Wtraditional $(OLD_ARCH_DEP_CFLAGS) +CCC_TRAD = $(CCC) -traditional $(OLD_ARCH_DEP_CFLAGS) +CCC_TEMPL_INST_FLAG = +CCC_WARN_YES = -Wall +CCC_WARN_NO = -w +CCC_OPT_YES = -O2 -fstrength-reduce +CCC_OPT_NO = +CCC_DEPENDS_FLAG = -MM +CPU = 68000 +OS = VXWORKS +OLD_ARCH_DEP_CFLAGS = --no-builtin -Wa,"-m68040" -DOS_EQ_$(OS) \ + -DBOARD_EQ_$(BOARD) -DCPU_EQ_$(CPU) -DBOARD=$(BOARD) +OLD_ARCH_DEP_LDFLAGS = -Ur -N -T$(EPICS_BASE_BIN)/vxldscript.MRI + +#-------------------------------------------------- +# C compiler definitions +ANSI = GCC + +#-------------------------------------------------- +# Command definitions +CPLUSPLUS = $(CPLUSPLUS_$(TORNADO)) +LD_CXX = $(LD_$(CPLUSPLUS)) + +#-------------------------------------------------- +# Override flags in CONFIG_COMMON +GCC_DEP_CFLAGS = -D_REENTRANT +G++_DEP_CFLAGS = -D_REENTRANT +POSIX_CPPFLAGS_YES = -D_POSIX_SOURCE + +#-------------------------------------------------- +# Operating system flags +OP_SYS_INCLUDES = -I$(VX_INCLUDE) +OP_SYS_CFLAGS = -DvxWorks -DV5_vxWorks -fno-builtin +OP_SYS_LDFLAGS = +OP_SYS_LDLIBS = + +# Fix for vxWorks headers using macros defined in +# vxWorks.h but not including vxWorks.h +ifeq ($(TORNADO), YES) + OP_SYS_CFLAGS += -include $(VX_INCLUDE)/vxWorks.h +endif + +#-------------------------------------------------- +# Optimization flag overrides +GCC_OPT_YES = -O2 -fstrength-reduce +GCC_OPT_NO = -g +G++_OPT_YES = -O2 -fstrength-reduce +G++_OPT_NO = -g + +OPT_CFLAGS = $($(ANSI)_OPT_$(VX_OPT)) +OPT_CXXFLAGS = $($(CPLUSPLUS)_OPT_$(VX_OPT)) + +#-------------------------------------------------- +# Warning flag overrides +GCC_WARN_YES = -Wall +GCC_WARN_NO = -w +G++_WARN_YES = -Wall +G++_WARN_NO = -w + +WARN_CFLAGS = $($(ANSI)_WARN_$(VX_WARN)) +WARN_CXXFLAGS = $($(CPLUSPLUS)_WARN_$(VX_WARN)) + +#-------------------------------------------------- +# Link definitions +LINK.c = $(LD) $(LDFLAGS) -o +LINK.cc = $(LD_CXX) $(LDFLAGS) -o + + +#-------------------------------------------------- +# Munch definitions +MUNCH_SUFFIX=.munch +MUNCHNAME = $(LIBNAME:%=%$(MUNCH_SUFFIX)) + +#-------------------------------------------------- +# The follow 2 exports prevent gnu cross-compiler +# from finding wrong assembler (as). +export WIND_BASE = $(VX_DIR) +export WIND_HOST_TYPE + diff --git a/config/CONFIG.Vx.frc40 b/config/CONFIG.Vx.frc40 new file mode 100644 index 000000000..2ae49653b --- /dev/null +++ b/config/CONFIG.Vx.frc40 @@ -0,0 +1,24 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68040 -DCPU_FAMILY=MC680X0 +ARCH_DEP_CFLAGS = -m68040 + diff --git a/config/CONFIG.Vx.frc5ce b/config/CONFIG.Vx.frc5ce new file mode 100644 index 000000000..37a476bd9 --- /dev/null +++ b/config/CONFIG.Vx.frc5ce @@ -0,0 +1,30 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG.Vx.frc5ce + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = sparc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = sparc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=SPARC -DCPU_FAMILY=SPARC +ARCH_DEP_CFLAGS = +# Removed -O2 [24/12/96 PMM] +OLD_ARCH_DEP_CXXFLAGS = -fno-builtin -ansi -pipe -nostdinc -DCPU=SPARC \ + -DVXWORKS -Dsigned= -Dvolatile= +V + +# Definitions for pre Tornado c++ builds +CCC = CENTERLINE +CCC_NORMAL = $(VX_DIR)/bin/sun4/CCsparc +CCC_STRICT = $(VX_DIR)/bin/sun4/CCsparc + diff --git a/config/CONFIG.Vx.hkbaja47 b/config/CONFIG.Vx.hkbaja47 new file mode 100644 index 000000000..d1313dc38 --- /dev/null +++ b/config/CONFIG.Vx.hkbaja47 @@ -0,0 +1,29 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = mips + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = mips + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=R4000 +ARCH_DEP_CFLAGS = -EB -mcpu=r4000 -mips3 -mgp32 -mfp32 -non_shared -G 0 + +# Arch specific flags +GCC += -fsigned-char +LD += -EB -X + diff --git a/config/CONFIG.Vx.hkbaja60 b/config/CONFIG.Vx.hkbaja60 new file mode 100644 index 000000000..62fda4e7f --- /dev/null +++ b/config/CONFIG.Vx.hkbaja60 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68060 -DCPU_FAMILY=MC680X0 +ARCH_DEP_CFLAGS = -m68040 + diff --git a/config/CONFIG.Vx.hkv2f b/config/CONFIG.Vx.hkv2f new file mode 100644 index 000000000..43bedbed4 --- /dev/null +++ b/config/CONFIG.Vx.hkv2f @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68020 +ARCH_DEP_CFLAGS = -m68020 + diff --git a/config/CONFIG.Vx.mv147 b/config/CONFIG.Vx.mv147 new file mode 100644 index 000000000..db5ed7ed4 --- /dev/null +++ b/config/CONFIG.Vx.mv147 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68030 +ARCH_DEP_CFLAGS = -m68030 + diff --git a/config/CONFIG.Vx.mv1604 b/config/CONFIG.Vx.mv1604 new file mode 100644 index 000000000..85cc22b9b --- /dev/null +++ b/config/CONFIG.Vx.mv1604 @@ -0,0 +1,24 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = ppc + +# Architecture specific build flags (TRUE=1 via LBL; needed in camessage.c) +ARCH_DEP_CPPFLAGS = -DCPU_FAMILY=PPC -DCPU=PPC604 -D_GNU_TOOL -DTRUE=1 +ARCH_DEP_CFLAGS = -fno-for-scope + diff --git a/config/CONFIG.Vx.mv162 b/config/CONFIG.Vx.mv162 new file mode 100644 index 000000000..3dc2e7450 --- /dev/null +++ b/config/CONFIG.Vx.mv162 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68040 +ARCH_DEP_CFLAGS = -m68040 + diff --git a/config/CONFIG.Vx.mv162lc b/config/CONFIG.Vx.mv162lc new file mode 100644 index 000000000..633057178 --- /dev/null +++ b/config/CONFIG.Vx.mv162lc @@ -0,0 +1,24 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68LC040 +ARCH_DEP_CFLAGS = -m68040 -msoft-float diff --git a/config/CONFIG.Vx.mv167 b/config/CONFIG.Vx.mv167 new file mode 100644 index 000000000..3dc2e7450 --- /dev/null +++ b/config/CONFIG.Vx.mv167 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68040 +ARCH_DEP_CFLAGS = -m68040 + diff --git a/config/CONFIG.Vx.mv177 b/config/CONFIG.Vx.mv177 new file mode 100644 index 000000000..3b7a2ea7d --- /dev/null +++ b/config/CONFIG.Vx.mv177 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68060 +ARCH_DEP_CFLAGS = -m68040 + diff --git a/config/CONFIG.Vx.mv2700 b/config/CONFIG.Vx.mv2700 new file mode 100644 index 000000000..c4751a496 --- /dev/null +++ b/config/CONFIG.Vx.mv2700 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = mv2700 + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC604 +ARCH_DEP_CFLAGS = -mcpu=604 + diff --git a/config/CONFIG.Vx.niCpu030 b/config/CONFIG.Vx.niCpu030 new file mode 100644 index 000000000..6a607d313 --- /dev/null +++ b/config/CONFIG.Vx.niCpu030 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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. +#************************************************************************* + +# CONFIG.Vx.niCpu030 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68030 -DNICPU030 +ARCH_DEP_CFLAGS = -m68030 + diff --git a/config/CONFIG.Vx.pc486 b/config/CONFIG.Vx.pc486 new file mode 100644 index 000000000..b19588c5b --- /dev/null +++ b/config/CONFIG.Vx.pc486 @@ -0,0 +1,26 @@ +#************************************************************************* +# 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. +#************************************************************************* + +# CONFIG.Vx.pc486 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 386 + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = pc486 + +ARCH_DEP_CPPFLAGS = -DCPU=I80486 -DCPU_FAMILY=I80x86 -D_X86_ +ARCH_DEP_CFLAGS = -m486 +ARCH_DEP_CXXFLAGS += -x 'c++' +ARCH_DEP_CFLAGS += -fno-defer-pop + diff --git a/config/CONFIG.Vx.pcPentium b/config/CONFIG.Vx.pcPentium new file mode 100644 index 000000000..0f41fa755 --- /dev/null +++ b/config/CONFIG.Vx.pcPentium @@ -0,0 +1,26 @@ +#************************************************************************* +# 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. +#************************************************************************* + +# CONFIG.Vx.pentium +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 386 + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = pcPentium + +ARCH_DEP_CPPFLAGS = -DCPU=PENTIUM -D_X86_ +ARCH_DEP_CFLAGS = -mpentium +ARCH_DEP_CXXFLAGS += -x 'c++' +ARCH_DEP_CFLAGS += -fno-defer-pop + diff --git a/config/CONFIG.Vx.ppc603 b/config/CONFIG.Vx.ppc603 new file mode 100644 index 000000000..ee61700c2 --- /dev/null +++ b/config/CONFIG.Vx.ppc603 @@ -0,0 +1,25 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC603 -DTRUE=1 +ARCH_DEP_CFLAGS = -mcpu=603 --no-builtin -mstrict-align + +# ARCH_DEP_CFLAGS+= -fsigned-char #May need for calcPerform.c diff --git a/config/CONFIG.Vx.ppc603_long b/config/CONFIG.Vx.ppc603_long new file mode 100644 index 000000000..65d79f997 --- /dev/null +++ b/config/CONFIG.Vx.ppc603_long @@ -0,0 +1,16 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC603 -DTRUE=1 +ARCH_DEP_CFLAGS = -mcpu=603 --no-builtin -mstrict-align -mlongcall + +# ARCH_DEP_CFLAGS+= -fsigned-char #May need for calcPerform.c diff --git a/config/CONFIG.Vx.ppc604 b/config/CONFIG.Vx.ppc604 new file mode 100644 index 000000000..20051505b --- /dev/null +++ b/config/CONFIG.Vx.ppc604 @@ -0,0 +1,26 @@ +#************************************************************************* +# 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 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC604 -D_GNU_TOOL -DTRUE=1 +ARCH_DEP_CFLAGS = -mcpu=604 --no-builtin -mstrict-align + +# ARCH_DEP_CFLAGS+= -fsigned-char #May need for calcPerform.c + diff --git a/config/CONFIG.Vx.ppc604_long b/config/CONFIG.Vx.ppc604_long new file mode 100644 index 000000000..5a9520b43 --- /dev/null +++ b/config/CONFIG.Vx.ppc604_long @@ -0,0 +1,17 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC604 -D_GNU_TOOL -DTRUE=1 +ARCH_DEP_CFLAGS = -mcpu=604 --no-builtin -mstrict-align -mlongcall + +# ARCH_DEP_CFLAGS+= -fsigned-char #May need for calcPerform.c + diff --git a/config/CONFIG.Vx.sbs_pc6 b/config/CONFIG.Vx.sbs_pc6 new file mode 100644 index 000000000..0a9d7ddfe --- /dev/null +++ b/config/CONFIG.Vx.sbs_pc6 @@ -0,0 +1,16 @@ +# Created by Korobov for SBS PC6 +# CONFIG.Vx.sbs_pc6 +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 386 + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = sbs_pc6 + +ARCH_DEP_CPPFLAGS = -DCPU=PENTIUM -DCPU_VARIANT=PENTIUM -D_X86_ +ARCH_DEP_CFLAGS = -mpentium +ARCH_DEP_CXXFLAGS += -x 'c++' +ARCH_DEP_CFLAGS += -fno-builtin -fno-defer-pop diff --git a/config/CONFIG.Vx.simpc b/config/CONFIG.Vx.simpc new file mode 100644 index 000000000..0eae5f1c1 --- /dev/null +++ b/config/CONFIG.Vx.simpc @@ -0,0 +1,30 @@ +#************************************************************************* +# 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. +#************************************************************************* + +# CONFIG.Vx.simpc +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = simpc + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = simpc + +ARCH_DEP_CPPFLAGS = -DCPU=SIMNT -DCPU_FAMILY=SIMNT -D_X86_ +ARCH_DEP_CFLAGS = -mpentium +ARCH_DEP_CXXFLAGS += -x 'c++' +ARCH_DEP_CFLAGS += -fno-defer-pop -DRW_MULTI_THREAD -D_REENTRANT -nostdlib + +# +# no drivers on the Tornado II simulator +# +DIRS = $(filter-out drv dev devOpt, $(DIRS)) diff --git a/config/CONFIG.Vx.vxWorks-486 b/config/CONFIG.Vx.vxWorks-486 new file mode 100644 index 000000000..b1b2d561e --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-486 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-486 is the new R3.14 name for pc486 +include $(EPICS_BASE)/config/CONFIG.Vx.pc486 diff --git a/config/CONFIG.Vx.vxWorks-68040 b/config/CONFIG.Vx.vxWorks-68040 new file mode 100644 index 000000000..cae60dddc --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-68040 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-68040 is the new R3.14 name for mv167 +include $(EPICS_BASE)/config/CONFIG.Vx.mv167 diff --git a/config/CONFIG.Vx.vxWorks-68040lc b/config/CONFIG.Vx.vxWorks-68040lc new file mode 100644 index 000000000..d388a500f --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-68040lc @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-68040lc is the new R3.14 name for mv162lc +include $(EPICS_BASE)/config/CONFIG.Vx.mv162lc diff --git a/config/CONFIG.Vx.vxWorks-68060 b/config/CONFIG.Vx.vxWorks-68060 new file mode 100644 index 000000000..dc6c5ed93 --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-68060 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-68060 is the new R3.14 name for mv177 +include $(EPICS_BASE)/config/CONFIG.Vx.mv177 diff --git a/config/CONFIG.Vx.vxWorks-ppc603 b/config/CONFIG.Vx.vxWorks-ppc603 new file mode 100644 index 000000000..257f27cb1 --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-ppc603 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-ppc603 is the new R3.14 name for ppc603 +include $(EPICS_BASE)/config/CONFIG.Vx.ppc603 diff --git a/config/CONFIG.Vx.vxWorks-ppc603_long b/config/CONFIG.Vx.vxWorks-ppc603_long new file mode 100644 index 000000000..ce6a22288 --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-ppc603_long @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-ppc603 is the new R3.14 name for ppc603 +include $(EPICS_BASE)/config/CONFIG.Vx.ppc603_long diff --git a/config/CONFIG.Vx.vxWorks-ppc604 b/config/CONFIG.Vx.vxWorks-ppc604 new file mode 100644 index 000000000..1b6cbe99e --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-ppc604 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-ppc604 is the new R3.14 name for ppc604 +include $(EPICS_BASE)/config/CONFIG.Vx.ppc604 diff --git a/config/CONFIG.Vx.vxWorks-ppc604_long b/config/CONFIG.Vx.vxWorks-ppc604_long new file mode 100644 index 000000000..653cf6fa4 --- /dev/null +++ b/config/CONFIG.Vx.vxWorks-ppc604_long @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# vxWorks-ppc604 is the new R3.14 name for ppc604 +include $(EPICS_BASE)/config/CONFIG.Vx.ppc604_long diff --git a/config/CONFIG.Vx.vxipc b/config/CONFIG.Vx.vxipc new file mode 100644 index 000000000..c8970667e --- /dev/null +++ b/config/CONFIG.Vx.vxipc @@ -0,0 +1,26 @@ +#************************************************************************* +# 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. +#************************************************************************* + +# CONFIG.Vx.VXIpc +# +# This file is maintained by the EPICS community. + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 386 + +# For Vx directories of form: +# $(VX_DIR)/$(HOST_ARCH).$(ARCH_CLASS)/bin +ARCH_CLASS = pc486 + +ARCH_DEP_CPPFLAGS = -DCPU=I80486 -DCPU_FAMILY=I80x86 -D_X86_ +ARCH_DEP_CFLAGS = -m486 +ARCH_DEP_CXXFLAGS += -x 'c++' +ARCH_DEP_CFLAGS += -fno-defer-pop + diff --git a/config/CONFIG_BASE b/config/CONFIG_BASE new file mode 100644 index 000000000..f108f619e --- /dev/null +++ b/config/CONFIG_BASE @@ -0,0 +1,85 @@ +#************************************************************************* +# 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 +# + +# the order of following list is important + +# +# for c++ build add the dirs gdd and cas after ca +# (and uncomment the c++ files in src/lbCom/Makefile.Unix) +# +DIRS = tools +DIRS += include +DIRS += cxxTemplates +DIRS += toolsComm +DIRS += makeBaseApp +DIRS += libCom +DIRS += libvxWorks +DIRS += cvtDctsdr +DIRS += dbStatic +DIRS += db +DIRS += bpt +DIRS += ca +DIRS += util +DIRS += misc +DIRS += sequencer +DIRS += dbtools +DIRS += rsrv +DIRS += rec +DIRS += as +DIRS += drv +DIRS += dev +DIRS += devOpt +DIRS += iocCore + +# +# if CPLUSPLUS isnt empty then include C++ src codes +# +DIRS += $(patsubst %,gdd,$(strip $(CPLUSPLUS))) +DIRS += $(patsubst %,cas,$(strip $(CPLUSPLUS))) + +EPICS_BASE = $(TOP) + +# EPICS include config file +include $(EPICS_BASE)/config/CONFIG + +-include $(EPICS_BASE)/configure/RELEASE +-include $(EPICS_BASE)/configure/RELEASE.$(EPICS_HOST_ARCH) + +ifdef INSTALL_LOCATION_BASE +INSTALL_LOCATION = $(INSTALL_LOCATION_BASE) +endif + +EPICS_BASE = $(INSTALL_LOCATION) + +Com_DIR = $(EPICS_BASE_LIB) +asHost_DIR = $(EPICS_BASE_LIB) +asIoc_DIR = $(EPICS_BASE_LIB) +ca_DIR = $(EPICS_BASE_LIB) +cas_DIR = $(EPICS_BASE_LIB) +dbIoc_DIR = $(EPICS_BASE_LIB) +dbStaticHost_DIR = $(EPICS_BASE_LIB) +dbStaticIoc_DIR = $(EPICS_BASE_LIB) +dbtoolsIoc_DIR = $(EPICS_BASE_LIB) +gdd_DIR = $(EPICS_BASE_LIB) +iocsh_DIR = $(EPICS_BASE_LIB) +miscIoc_DIR = $(EPICS_BASE_LIB) +recIoc_DIR = $(EPICS_BASE_LIB) +registryIoc_DIR = $(EPICS_BASE_LIB) +rsrvIoc_DIR = $(EPICS_BASE_LIB) +softDevIoc_DIR = $(EPICS_BASE_LIB) +testDevIoc_DIR = $(EPICS_BASE_LIB) +recIocObj_DIR = $(EPICS_BASE_LIB) +softDevIocObj_DIR = $(EPICS_BASE_LIB) +testDevIocObj_DIR = $(EPICS_BASE_LIB) +iocCore_DIR = $(EPICS_BASE_LIB) + diff --git a/config/CONFIG_BASE_VERSION b/config/CONFIG_BASE_VERSION new file mode 100644 index 000000000..246c7a3a5 --- /dev/null +++ b/config/CONFIG_BASE_VERSION @@ -0,0 +1,15 @@ +#************************************************************************* +# 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 +# +# EPICS Version information now comes from configure... + +include $(EPICS_BASE)/configure/CONFIG_BASE_VERSION diff --git a/config/CONFIG_COMMON b/config/CONFIG_COMMON new file mode 100644 index 000000000..ee0f5e2f0 --- /dev/null +++ b/config/CONFIG_COMMON @@ -0,0 +1,258 @@ +#************************************************************************* +# 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 +# +# CONFIG_COMMON - Another EPICS BASE config file +# by Matthew Needes and Mike Bordua +# +# This file is to be maintained by the EPICS community. +# + +# Common Configuration Information + +# CROSS1 will be defined only when CROSS_COMPILER_HOST_ARCHS is NOT defined +CROSS1 = $(CROSS_COMPILER_TARGET_ARCHS$(word 1,$(CROSS_COMPILER_HOST_ARCHS))) + +# CROSS2 will be defined only when CROSS_COMPILER_HOST_ARCHS is defined +# and HOST_ARCH is one of it's words +CROSS2 = $(CROSS_COMPILER_TARGET_ARCHS$(filter-out 1,$(words $(filter $(HOST_ARCH),$(CROSS_COMPILER_HOST_ARCHS))))) + +BUILD_ARCHS = $(HOST_ARCH) $(CROSS1) $(CROSS2) + +INSTALL_LOCATION = $(TOP) + +INSTALL_LOCATION_LIB = $(INSTALL_LOCATION)/lib +INSTALL_LOCATION_BIN = $(INSTALL_LOCATION)/bin + +INSTALL_INCLUDE = $(INSTALL_LOCATION)/include +INSTALL_DOC = $(INSTALL_LOCATION)/doc +INSTALL_HTML = $(INSTALL_LOCATION)/html +INSTALL_MAN = $(INSTALL_LOCATION)/man +INSTALL_TEMPLATES = $(INSTALL_LOCATION)/templates +INSTALL_DBD = $(INSTALL_LOCATION)/dbd +INSTALL_CONFIG = $(INSTALL_LOCATION)/config +INSTALL_JAVA = $(INSTALL_LOCATION)/javalib + +EPICS_BASE_INCLUDE = $(EPICS_BASE)/include +EPICS_BASE_TOOLS = $(EPICS_BASE)/config/tools + +DIVIDER = . +OBJ = . +RES = . + +EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(HOST_ARCH) +EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(HOST_ARCH) +INSTALL_HOST_LIB = $(INSTALL_LOCATION)/lib/$(HOST_ARCH) + + +# private versions of lex/yacc from EPICS +EYACC = $(EPICS_BASE_HOST_BIN)/antelope$(EXE) +ELEX = $(EPICS_BASE_HOST_BIN)/e_flex$(EXE) -S$(EPICS_BASE_INCLUDE)/flex.skel.static +YACC = $(EYACC) +LEX = $(ELEX) + +# Default for perl if it's on the PATH, +# otherwise override this in CONFIG_SITE_HOST_ARCH. +PERL=perl + +# install from EPICS +INSTALL = $(PERL) $(EPICS_BASE_TOOLS)/installEpics.pl +INSTALL_PRODUCT = $(INSTALL) +INSTALL_LIBRARY = $(INSTALL) + +# dbtools from EPICS + +# state notation language compiler (removed from base for R3.14) +SNC = $(SEQ)/bin/$(HOST_ARCH)/snc$(EXE) + +ifdef T_A + +INSTALL_LIB = $(INSTALL_LOCATION_LIB)/$(T_A) +INSTALL_TCLLIB = $(INSTALL_LOCATION_LIB)/$(T_A) +INSTALL_BIN = $(INSTALL_LOCATION_BIN)/$(T_A) + +EPICS_BASE_LIB = $(EPICS_BASE)/lib/$(T_A) +EPICS_BASE_BIN = $(EPICS_BASE)/bin/$(T_A) + +#-------------------------------------------------- +# GNU compiler defaults + +GCC_ANSI = $(GCC) -ansi +GCC_STRICT = $(GCC) -ansi -pedantic +GCC_TRAD = $(GCC) +GCC_DEP_CFLAGS = -fPIC -D_REENTRANT + +GCC_WARN_YES = -Wall -Wmissing-prototypes -Wstrict-prototypes +GCC_WARN_NO = -w +GCC_OPT_YES = -O3 +GCC_OPT_NO = -g +GCC_SFLAGS_YES = -static +GCC_SFLAGS_NO = +GCC_SLIBS_YES = +GCC_SLIBS_NO = +GCC_SHRLIB_CFLAGS_YES = +GCC_SHRLIB_LDFLAGS_YES = -shared + +G++_NORMAL = $(G++) -ansi -pedantic +G++_STRICT = $(G++) -ansi -pedantic +G++_TRAD = $(G++) +G++_TEMPL_INST_FLAG = -DEXPL_TEMPL +G++_DEP_CFLAGS = -fPIC -D_REENTRANT +G++_WARN_YES = -Wall \ + -Woverloaded-virtual \ + -Wwrite-strings -Wconversion\ + -Wpointer-arith -Winline +G++_WARN_NO = -w +G++_OPT_YES = -O3 +G++_OPT_NO = -g +G++_SFLAGS_YES = -static +G++_SFLAGS_NO = +G++_SLIBS_YES = +G++_SLIBS_NO = +G++_DEPENDS_FLAG = -MM +G++_SHRLIB_CFLAGS_YES = +G++_SHRLIB_LDFLAGS_YES = -shared + +#-------------------------------------------------- +# C compiler + +CC = $($(ANSI)_$(CMPLR)) + +#--------------------------------------------------------------- +# Vendor compiler dependent options +VENDOR_DEP_CFLAGS = $($(ANSI)_DEP_CFLAGS) +VENDOR_DEP_CXXFLAGS = $($(CPLUSPLUS)_DEP_CFLAGS) + +#-------------------------------------------------- +# C++ compiler + +CXX = $($(CPLUSPLUS)_$(CXXCMPLR)) + +#--------------------------------------------------------------- +# Architecture dependent options (solaris,sun4,mv167,...) +ARCH_DEP_CFLAGS = +ARCH_DEP_CXXFLAGS = $(ARCH_DEP_CFLAGS) +ARCH_DEP_LDFLAGS = +ARCH_DEP_LDLIBS = + +#--------------------------------------------------------------- +# Operating system dependent options (Unix,Vx,WIN32...) +OP_SYS_CFLAGS = +OP_SYS_LDFLAGS = +OP_SYS_INCLUDES = +OP_SYS_LDLIBS = + +#-------------------------------------------------- +# SOURCE* used by WIN32 only +SOURCE_CFLAG = +SOURCE_CXXCFLAG = + +#-------------------------------------------------- +# Makefile specific options +USR_INCLUDES = +USR_CFLAGS = +USR_CXXFLAGS = +USR_LDFLAGS = +USR_LDLIBS = +USR_CPPFLAGS = +USR_DBDFLAGS = + +#-------------------------------------------------- +# Target specific options +TARGET_INCLUDES = $($(basename $@)_INCLUDES) +TARGET_CFLAGS = $($(basename $@)_CFLAGS) +TARGET_CXXFLAGS = $($(basename $@)_CXXFLAGS) +TARGET_LDFLAGS = $($(basename $@)_LDFLAGS) +TARGET_LDLIBS = $($(basename $@)_LDLIBS) +TARGET_CPPFLAGS = $($(basename $@)_CPPFLAGS) +TARGET_SNCFLAGS = $($(basename $@)_SNCFLAGS) + +#-------------------------------------------------- +# Depends flag +DEPENDS_FLAG = $($(CPLUSPLUS)_DEPENDS_FLAG) + +#-------------------------------------------------- +# C++ template flag option +TEMPL_INST_CXXFLAG = $($(CPLUSPLUS)_TEMPL_INST_FLAG) + +#-------------------------------------------------- +# Epics includes (CONFIG.Vx will override OS_CLASS) +OS_CLASS = $(ARCH_CLASS) +EPICS_INCLUDES = -I$(INSTALL_INCLUDE)/os/$(OS_CLASS) -I$(INSTALL_INCLUDE) +EPICS_DBDFLAGS = -I $(INSTALL_LOCATION)/dbd + +#-------------------------------------------------- +# Posix flags +POSIX=YES +POSIX_CPPFLAGS_YES += -D_POSIX_C_SOURCE=199506L +POSIX_LDLIBS_YES += + +POSIX_CPPFLAGS += $(POSIX_CPPFLAGS_$(POSIX)) +POSIX_LDLIBS += $(POSIX_LDLIBS_$(POSIX)) + +#-------------------------------------------------- +# Warnings flags (CONFIG.Vx will override) +WARN_CFLAGS = $($(ANSI)_WARN_$(HOST_WARN)) +WARN_CXXFLAGS = $($(CPLUSPLUS)_WARN_$(HOST_WARN)) + +#-------------------------------------------------- +# Optimization flags (CONFIG.Vx will override) +OPT_CFLAGS = $($(ANSI)_OPT_$(HOST_OPT)) +OPT_CXXFLAGS = $($(CPLUSPLUS)_OPT_$(HOST_OPT)) +OPT_LDFLAGS = + +#-------------------------------------------------- +# Static build options +STATIC_CFLAGS = $($(ANSI)_SFLAGS_$(STATIC_BUILD)) +STATIC_CXXCFLAGS = $($(CPLUSPLUS)_SFLAGS_$(STATIC_BUILD)) +STATIC_LDFLAGS = $($(ANSI)_SLDFLAGS_$(STATIC_BUILD)) +STATIC_LDLIBS = $($(ANSI)_SLIBS_$(STATIC_BUILD)) + +#-------------------------------------------------- +# ar definition default +ARCMD = $(AR) $(ARFLAGS) $@ + +#-------------------------------------------------- +# depends definition +DEPENDS_RULE = -$(COMPILE.cc) $(DEPENDS_FLAG) $^ > DEPENDS + +#-------------------------------------------------- + +# Include files + +INCLUDES = -I. -I.. $(USR_INCLUDES) $(EPICS_INCLUDES) $(TARGET_INCLUDES) $(OP_SYS_INCLUDES) + +CFLAGS = $(OPT_CFLAGS) $(DEBUG_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS)\ + $(USR_CFLAGS) $(ARCH_DEP_CFLAGS) $(VENDOR_DEP_CFLAGS) $(STATIC_CFLAGS)\ + $(OP_SYS_CFLAGS) $(INCLUDES) + +CXXFLAGS = $(OPT_CXXFLAGS) $(DEBUG_CXXFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS)\ + $(USR_CXXFLAGS) $(ARCH_DEP_CXXFLAGS) $(VENDOR_DEP_CXXFLAGS) $(STATIC_CXXCFLAGS)\ + $(OP_SYS_CFLAGS) $(TEMPL_INST_CXXFLAG) $(INCLUDES) + +LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS)\ + $(ARCH_DEP_LDFLAGS) $(STATIC_LDFLAGS) $(OP_SYS_LDFLAGS) $(RUNTIME_LDFLAGS) + +LDLIBS = $(TARGET_LDLIBS) $(USR_LDLIBS) $(STATIC_LDLIBS) $(ARCH_DEP_LDLIBS)\ + $(OP_SYS_LDLIBS) $(POSIX_LDLIBS) + +CPPFLAGS += $(TARGET_CPPFLAGS) $(USR_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(POSIX_CPPFLAGS) $(OP_SYS_CPPFLAGS) + +CPPSNCFLAGS = $(INCLUDES) + +DBDFLAGS = -I . -I .. $(USR_DBDFLAGS) $(EPICS_DBDFLAGS) + +# Build compile line here +COMPILE.c = $(CC) $(CPPFLAGS) $(CFLAGS) -c $(SOURCE_FLAG) +COMPILE.cc = $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(SOURCE_CXXFLAG) + +endif + diff --git a/config/CONFIG_COMPAT b/config/CONFIG_COMPAT new file mode 100644 index 000000000..0ba47c55b --- /dev/null +++ b/config/CONFIG_COMPAT @@ -0,0 +1,49 @@ +#************************************************************************* +# 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-20101014190140-s9gxgtnoyhbck6id +# +# Convert old HOST_ARCH environment variable + +#Syntax: +# ARCH_ = +ARCH_solarisGnu = solaris-sparc-gnu +ARCH_solarisNonshared = solaris-sparc-nonshared +#ARCH_solaris = solaris-sparc-static +#ARCH_solaris = solaris-sparc-debug +ARCH_solaris = solaris-sparc +ARCH_sun4 = sun4-sparc +ARCH_Linux = linux-x86 +ARCH_freebsd = freebsd-x86_64 +ARCH_WIN32 = win32-x86 +ARCH_hp700 = hpux-parisc +ARCH_Borland = win32-x86-borland +ARCH_cygwin32 = cygwin-x86 + +ifndef EPICS_HOST_ARCH +ifdef HOST_ARCH +EPICS_HOST_ARCH = $(firstword $(ARCH_$(HOST_ARCH)) $(HOST_ARCH)) +else +EPICS_HOST_ARCH = unsupported +endif +endif + +HOST_ARCH := $(EPICS_HOST_ARCH) + +# Note: +# The EPICS_HOST_ARCH solaris--gnu is new for base release R3.14 +# so existing R3.13 applications, when HOST_ARCH is set to solaris and +# when built with R3.14, will look for base solaris- directories +# and not solaris--gnu. If you plan to build R3.13 applications +# against a solaris--gnu built R3.14 base, you should set +# ANSI=GCC and CPLUSPLUS=G++ in base/config/CONFIG_SITE.Host.solaris +# and change ARCH_solaris=solaris- to ARCH_solaris=solaris--gnu +# in base/config/CONFIG_COMPAT. + diff --git a/config/CONFIG_ENV b/config/CONFIG_ENV new file mode 100644 index 000000000..3e3891cbc --- /dev/null +++ b/config/CONFIG_ENV @@ -0,0 +1,137 @@ +#************************************************************************* +# 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: Andrew Johnson +# Date: 20 April 1995 +# +# Experimental Physics and Industrial Control System (EPICS) +# +# CONFIG_ENV - EPICS Environment Parameter configuration file +# +# This file is interpreted by the Bourne Shell, so spaces are +# not allowed around the '=' signs or in unquoted values. +# Makefile variables are not defined here. +# +# Note: This file is read by base/src/libCom/bldEnvdata.pl, +# so the variable definitions in here should be kept 'simple': +# VAR=VALUE +# each one on a single line. +# + + +# Default environment settings + +# Channel Access: +# EPICS_CA_ADDR_LIST +# Augment beacon/search dest ip addr list +# from white space separated ip addresses in this +# environment variable. "Quote" if more than one addr. +# EPICS_CA_AUTO_ADDR_LIST +# YES = augment beacon/search dest ip +# addr list from network interfaces found; NO = only use +# EPICS_CA_ADDR_LIST to create this list. +# EPICS_CA_CONN_TMO +# after not seeing a server beacon for this number +# of seconds the clients will send an echo request over +# tcp/ip to verify the connection. +# EPICS_CA_REPEATER_PORT CA repeater port number. +# EPICS_CA_SERVER_PORT CA server port number. + +EPICS_CA_ADDR_LIST="" +EPICS_CA_AUTO_ADDR_LIST=YES +EPICS_CA_CONN_TMO=30.0 +EPICS_CA_REPEATER_PORT=5065 +EPICS_CA_SERVER_PORT=5064 + +# +# These parameters are only used by the CA server library +# +# EPICS_CA_BEACON_PERIOD +# quiescent sec between server beacons. +# EPICS_CAS_INTF_ADDR_LIST - list of IP addresses identifying +# a limited set of network interfaces for server communication. +# Specifically, this parameter specifies the interfaces from +# which the server will accept TCP/IP connections. It also +# specifies that UDP search messages addressed to both +# the IP addresses in EPICS_CAS_INTF_ADDR_LIST and also +# the broadcast addresses (or the destination addresses of +# point to point interfaces) of the corresponding interfaces +# will be accepted by the server. If this parameter is empty +# then TCP/IP connections are accepted from any interface +# (and any search messages addressed to the host are accepted). +# Beacons are sent only to the broadcast address of each interface +# in this list (or the destination address of point to point +# interfaces) if EPICS_CA_AUTO_ADDR_LIST is YES. +# Type "ifconfig -a" in order to determine which interfaces +# are available. +# EPICS_CAS_BEACON_ADDR_LIST - If this parameter is specified +# or if EPICS_CAS_INTF_ADDR_LIST isnt empty then this parameter +# is used instead of EPICS_CA_ADDR_LIST by the server library +# to augment the list of addresses to send beacons to. +# EPICS_CAS_SERVER_PORT - If this parameter is specified then it +# and not EPICS_CA_SERVER_PORT is used to determine the server's +# port number. It is possible to have multiple servers on the +# same host on the same EPICS_CAS_SERVER_PORT port if the IP +# kernel is modern (if it has multicast enhancements). In this +# situation all servers will share the same user specified +# UDP port number and will be assigned unique TCP port numbers. +# The fact that the servers may not be using the user specified +# port number for TCP will be completely transparent to EPICS +# 3.13 or higher clients that will be locating the servers +# using the user specified UDP port number, but will be connecting to +# the servers using whatever TCP port has been assigned. +# +# If experiencing trouble getting clients to connect +# -------------------------------------------------- +# +# o Make sure that the broadcast addresses are identical on the +# server's host and on the client's host. This can be checked with +# "netstat -i" or "ifconfig -a". +# +# o Make sure that the client and server are using the same UDP +# port. Check the server's port by running "netstat -a | grep nnn" +# where nnn is the port number involved. If you do not set +# EPICS_CA_SERVER_PORT or EPICS_CAS_SERVER_PORT +# then the default port will be 5064. +# +# o Two servers can run on the same host with the same server port number +# if the following restrictions are understood (good luck). If the host has a +# modern IP kernel it is possible to have two servers on the same UDP port. +# It is not possible for two servers to run on the same host using the same +# TCP IP server port. If the CA server library detects that a 2nd server is attempting +# to start on the same port as an existing CA server then both servers will use the +# same UDP port if it is a modern IP kernel, and the 2nd server will +# be allocated a new TCP port. In this situation the clients will still be +# configured to use the same port number for both servers. The clients +# will find the 2nd server via the shared UDP port, and transparently +# connect to the 2nd server's allocated TCP port. Be aware that If there are +# two server's running on the same host on the same UDP port then they will +# both receive all UDP search requests sent as broadcasts, but will unfortunately (due to +# what I consider to be a real weakness of most IP kernel implementations) only one +# of the servers will typically receive UDP search requests sent to unicast addresses (i.e. +# a specific host's ip address). +# + + +EPICS_CA_BEACON_PERIOD=15.0 +EPICS_CAS_INTF_ADDR_LIST="" +EPICS_CAS_BEACON_ADDR_LIST="" +EPICS_CAS_SERVER_PORT= + + +# Log Server: +# EPICS_IOC_LOG_PORT Log server port number etc. +EPICS_IOC_LOG_PORT=7004 + +# Other services: + +EPICS_CMD_PROTO_PORT= +EPICS_AR_PORT=7002 + diff --git a/config/CONFIG_HOST_ARCH.Borland b/config/CONFIG_HOST_ARCH.Borland new file mode 100644 index 000000000..c8e26ce48 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.Borland @@ -0,0 +1,32 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.Borland +# + +EXE=.exe +HOSTEXE=.exe + +CP =$(PERL) $(EPICS_BASE_TOOLS)/cp.pl +MV =$(PERL) $(EPICS_BASE_TOOLS)/mv.pl +RM =$(PERL) $(EPICS_BASE_TOOLS)/rm.pl -f +MKDIR=$(PERL) $(EPICS_BASE_TOOLS)/mkdir.pl +RMDIR=$(PERL) $(EPICS_BASE_TOOLS)/rm.pl -rf +FN =$(PERL) $(EPICS_BASE_TOOLS)/fullName.pl +CHMOD=echo +ECHO=echo + +WIND_HOST_TYPE = x86-win32 + +# Does not work if using cygwin make +# because HOME is always defined +ifndef HOME +HOME = $(HOMEDRIVE)$(HOMEPATH) +endif + diff --git a/config/CONFIG_HOST_ARCH.Darwin b/config/CONFIG_HOST_ARCH.Darwin new file mode 100644 index 000000000..b1ba56bc8 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.Darwin @@ -0,0 +1,14 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.Darwin +# + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon diff --git a/config/CONFIG_HOST_ARCH.Linux b/config/CONFIG_HOST_ARCH.Linux new file mode 100644 index 000000000..4c7ad5c38 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.Linux @@ -0,0 +1,17 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.Linux +# + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = i386-linux2 + diff --git a/config/CONFIG_HOST_ARCH.UnixCommon b/config/CONFIG_HOST_ARCH.UnixCommon new file mode 100644 index 000000000..d7f5812dc --- /dev/null +++ b/config/CONFIG_HOST_ARCH.UnixCommon @@ -0,0 +1,27 @@ +#************************************************************************* +# 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. +#************************************************************************* + +# CONFIG_HOST_ARCH.UnixCommon +# + +# Unix command definitions +CP = cp +MV = mv +RM = rm -f +MKDIR = mkdir +RMDIR = rm -rf +CHMOD = "/bin/chmod" + +# Set LD_LIBRARY_PATH for shared library builds +ifneq ($(EPICS_BASE),$(INSTALL_LOCATION)) +export LD_LIBRARY_PATH := $(INSTALL_HOST_LIB):$(EPICS_BASE_HOST_LIB):$(LD_LIBRARY_PATH) +else +export LD_LIBRARY_PATH := $(EPICS_BASE_HOST_LIB):$(LD_LIBRARY_PATH) +endif diff --git a/config/CONFIG_HOST_ARCH.WIN32 b/config/CONFIG_HOST_ARCH.WIN32 new file mode 100644 index 000000000..31c539e46 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.WIN32 @@ -0,0 +1,31 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.WIN32 +# + +EXE=.exe +HOSTEXE=.exe + +CP =$(PERL) $(EPICS_BASE_TOOLS)/cp.pl +MV =$(PERL) $(EPICS_BASE_TOOLS)/mv.pl +RM =$(PERL) $(EPICS_BASE_TOOLS)/rm.pl -f +MKDIR=$(PERL) $(EPICS_BASE_TOOLS)/mkdir.pl +RMDIR=$(PERL) $(EPICS_BASE_TOOLS)/rm.pl -rf +CHMOD=echo +ECHO=echo + +WIND_HOST_TYPE = x86-win32 + +# Does not work if using cygwin make +# because HOME is always defined +ifndef HOME +HOME = $(HOMEDRIVE)$(HOMEPATH) +endif + diff --git a/config/CONFIG_HOST_ARCH.cygwin-x86 b/config/CONFIG_HOST_ARCH.cygwin-x86 new file mode 100644 index 000000000..28ffe187c --- /dev/null +++ b/config/CONFIG_HOST_ARCH.cygwin-x86 @@ -0,0 +1,23 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.cygwin-x86 +# +# This file is maintained by the EPICS community. + +# Include definitions common to all Unix archs +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +EXE=.exe +HOSTEXE=.exe + +TORNADO=YES + +WIND_HOST_TYPE = x86-win32 + diff --git a/config/CONFIG_HOST_ARCH.cygwin32 b/config/CONFIG_HOST_ARCH.cygwin32 new file mode 100644 index 000000000..c2d47d127 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.cygwin32 @@ -0,0 +1,23 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.cygwin32 +# +# This file is maintained by the EPICS community. + +# Include definitions common to all Unix archs +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +EXE=.exe +HOSTEXE=.exe + +TORNADO=YES + +WIND_HOST_TYPE = x86-win32 + diff --git a/config/CONFIG_HOST_ARCH.darwin-ppc b/config/CONFIG_HOST_ARCH.darwin-ppc new file mode 100644 index 000000000..4125f38ce --- /dev/null +++ b/config/CONFIG_HOST_ARCH.darwin-ppc @@ -0,0 +1,12 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.Darwin diff --git a/config/CONFIG_HOST_ARCH.darwin-ppcx86 b/config/CONFIG_HOST_ARCH.darwin-ppcx86 new file mode 100644 index 000000000..4125f38ce --- /dev/null +++ b/config/CONFIG_HOST_ARCH.darwin-ppcx86 @@ -0,0 +1,12 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.Darwin diff --git a/config/CONFIG_HOST_ARCH.darwin-x86 b/config/CONFIG_HOST_ARCH.darwin-x86 new file mode 100644 index 000000000..4125f38ce --- /dev/null +++ b/config/CONFIG_HOST_ARCH.darwin-x86 @@ -0,0 +1,12 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.Darwin diff --git a/config/CONFIG_HOST_ARCH.freebsd-x86_64 b/config/CONFIG_HOST_ARCH.freebsd-x86_64 new file mode 100644 index 000000000..5b5188f0b --- /dev/null +++ b/config/CONFIG_HOST_ARCH.freebsd-x86_64 @@ -0,0 +1,11 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon diff --git a/config/CONFIG_HOST_ARCH.hp700 b/config/CONFIG_HOST_ARCH.hp700 new file mode 100644 index 000000000..da00860dd --- /dev/null +++ b/config/CONFIG_HOST_ARCH.hp700 @@ -0,0 +1,16 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.hp700 +# + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = parisc-hpux10 diff --git a/config/CONFIG_HOST_ARCH.hpux-parisc b/config/CONFIG_HOST_ARCH.hpux-parisc new file mode 100644 index 000000000..770dd7138 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.hpux-parisc @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# hpux-parisc is the new name for hp700 +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.hp700 diff --git a/config/CONFIG_HOST_ARCH.hpux-parisc-gnu b/config/CONFIG_HOST_ARCH.hpux-parisc-gnu new file mode 100644 index 000000000..770dd7138 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.hpux-parisc-gnu @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# hpux-parisc is the new name for hp700 +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.hp700 diff --git a/config/CONFIG_HOST_ARCH.linux-ppc b/config/CONFIG_HOST_ARCH.linux-ppc new file mode 100644 index 000000000..9604f114f --- /dev/null +++ b/config/CONFIG_HOST_ARCH.linux-ppc @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.Linux + +WIND_HOST_TYPE = ppc-linux diff --git a/config/CONFIG_HOST_ARCH.linux-x86 b/config/CONFIG_HOST_ARCH.linux-x86 new file mode 100644 index 000000000..c684acbb4 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.linux-x86 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# linux-x86 is the new name for linux +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.Linux diff --git a/config/CONFIG_HOST_ARCH.linux-x86-debug b/config/CONFIG_HOST_ARCH.linux-x86-debug new file mode 100644 index 000000000..384ccd1a9 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.linux-x86-debug @@ -0,0 +1,15 @@ +#************************************************************************* +# 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 + +# linux-x86 is the new name for linux +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.Linux + +HOST_OPT = NO diff --git a/config/CONFIG_HOST_ARCH.linux-x86_64 b/config/CONFIG_HOST_ARCH.linux-x86_64 new file mode 100644 index 000000000..2cc036b23 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.linux-x86_64 @@ -0,0 +1,15 @@ +#************************************************************************* +# 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 + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = x86-linux2 diff --git a/config/CONFIG_HOST_ARCH.linux-x86_64-debug b/config/CONFIG_HOST_ARCH.linux-x86_64-debug new file mode 100644 index 000000000..2179e733b --- /dev/null +++ b/config/CONFIG_HOST_ARCH.linux-x86_64-debug @@ -0,0 +1,15 @@ +#************************************************************************* +# 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 + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.linux-x86_64 + +HOST_OPT = NO diff --git a/config/CONFIG_HOST_ARCH.sgi b/config/CONFIG_HOST_ARCH.sgi new file mode 100644 index 000000000..7af635400 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.sgi @@ -0,0 +1,17 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.sgi +# + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = sgi + diff --git a/config/CONFIG_HOST_ARCH.solaris b/config/CONFIG_HOST_ARCH.solaris new file mode 100644 index 000000000..03eea9f75 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris @@ -0,0 +1,16 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.solaris + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = sun4-solaris2 + diff --git a/config/CONFIG_HOST_ARCH.solaris-sparc b/config/CONFIG_HOST_ARCH.solaris-sparc new file mode 100644 index 000000000..9baf63454 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-sparc @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.solaris diff --git a/config/CONFIG_HOST_ARCH.solaris-sparc-debug b/config/CONFIG_HOST_ARCH.solaris-sparc-debug new file mode 100644 index 000000000..8241d0f98 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-sparc-debug @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.solaris + +HOST_OPT = NO diff --git a/config/CONFIG_HOST_ARCH.solaris-sparc-gnu b/config/CONFIG_HOST_ARCH.solaris-sparc-gnu new file mode 100644 index 000000000..79b870663 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-sparc-gnu @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.solarisGnu + diff --git a/config/CONFIG_HOST_ARCH.solaris-sparc-staticlib b/config/CONFIG_HOST_ARCH.solaris-sparc-staticlib new file mode 100644 index 000000000..0bd86e34d --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-sparc-staticlib @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.solaris + diff --git a/config/CONFIG_HOST_ARCH.solaris-sparc64 b/config/CONFIG_HOST_ARCH.solaris-sparc64 new file mode 100644 index 000000000..9baf63454 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-sparc64 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.solaris diff --git a/config/CONFIG_HOST_ARCH.solaris-sparc64-gnu b/config/CONFIG_HOST_ARCH.solaris-sparc64-gnu new file mode 100644 index 000000000..79b870663 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-sparc64-gnu @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.solarisGnu + diff --git a/config/CONFIG_HOST_ARCH.solaris-x86 b/config/CONFIG_HOST_ARCH.solaris-x86 new file mode 100644 index 000000000..eb5d7b8bf --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-x86 @@ -0,0 +1,16 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.solaris-x86 + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = x86-solaris2 + diff --git a/config/CONFIG_HOST_ARCH.solaris-x86-gnu b/config/CONFIG_HOST_ARCH.solaris-x86-gnu new file mode 100644 index 000000000..37f86f3cf --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solaris-x86-gnu @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.solarisGnu + diff --git a/config/CONFIG_HOST_ARCH.solarisGnu b/config/CONFIG_HOST_ARCH.solarisGnu new file mode 100644 index 000000000..4bec2d223 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.solarisGnu @@ -0,0 +1,8 @@ +# CONFIG_HOST_ARCH.solarisGnu +# + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = sun4-solaris2 + diff --git a/config/CONFIG_HOST_ARCH.sun4 b/config/CONFIG_HOST_ARCH.sun4 new file mode 100644 index 000000000..69f3880d1 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.sun4 @@ -0,0 +1,22 @@ +#************************************************************************* +# 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. +#************************************************************************* +# CONFIG_HOST_ARCH.sun4 +# + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +WIND_HOST_TYPE = sun4-sunos4 + +########################## +# added temporarily because gdd will not compile - 2/18/98 +CPLUSPLUS = +########################## + diff --git a/config/CONFIG_HOST_ARCH.sun4-sparc b/config/CONFIG_HOST_ARCH.sun4-sparc new file mode 100644 index 000000000..0ec576c78 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.sun4-sparc @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# sun4-sparc is the new name for sun4 +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.sun4 diff --git a/config/CONFIG_HOST_ARCH.win32-x86 b/config/CONFIG_HOST_ARCH.win32-x86 new file mode 100644 index 000000000..a5c04e7ad --- /dev/null +++ b/config/CONFIG_HOST_ARCH.win32-x86 @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# win32-x86 is the new name for WIN32 +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.WIN32 diff --git a/config/CONFIG_HOST_ARCH.win32-x86-borland b/config/CONFIG_HOST_ARCH.win32-x86-borland new file mode 100644 index 000000000..6ecf794f0 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.win32-x86-borland @@ -0,0 +1,13 @@ +#************************************************************************* +# 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 + +# win32-x86-borland is the new name for Borland +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.Borland diff --git a/config/CONFIG_HOST_ARCH.win32-x86-cygwin b/config/CONFIG_HOST_ARCH.win32-x86-cygwin new file mode 100644 index 000000000..0da6f1390 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.win32-x86-cygwin @@ -0,0 +1,20 @@ +#************************************************************************* +# 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 +# +# Override values in CONFIG_SITE_HOST_ARCH.win32-x86-cygwin + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +HOSTEXE=.exe + +WIND_HOST_TYPE = x86-win32 + diff --git a/config/CONFIG_HOST_ARCH.win32-x86-debug b/config/CONFIG_HOST_ARCH.win32-x86-debug new file mode 100644 index 000000000..ab34b58c3 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.win32-x86-debug @@ -0,0 +1,14 @@ +#************************************************************************* +# 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 + +-include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.WIN32 + +HOST_OPT = NO diff --git a/config/CONFIG_HOST_ARCH.win32-x86-mingw b/config/CONFIG_HOST_ARCH.win32-x86-mingw new file mode 100644 index 000000000..2dc11f320 --- /dev/null +++ b/config/CONFIG_HOST_ARCH.win32-x86-mingw @@ -0,0 +1,29 @@ +#************************************************************************* +# 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 +# +# Override values in CONFIG_SITE_HOST_ARCH.win32-x86-mingw + +# Include definitions common to all Unix archs +include $(EPICS_BASE)/config/CONFIG_HOST_ARCH.UnixCommon + +EXE=.exe +HOSTEXE=.exe + +CP =$(PERL) $(EPICS_BASE_TOOLS)/cp.pl +MV =$(PERL) $(EPICS_BASE_TOOLS)/mv.pl +RM =$(PERL) $(EPICS_BASE_TOOLS)/rm.pl -f +MKDIR=$(PERL) $(EPICS_BASE_TOOLS)/mkdir.pl +RMDIR=$(PERL) $(EPICS_BASE_TOOLS)/rm.pl -rf +CHMOD=echo +ECHO=echo + +WIND_HOST_TYPE = x86-win32 + diff --git a/config/CONFIG_SITE b/config/CONFIG_SITE new file mode 100644 index 000000000..701c50d90 --- /dev/null +++ b/config/CONFIG_SITE @@ -0,0 +1,161 @@ +# +# Revision-Id: anj@aps.anl.gov-20101014190140-s9gxgtnoyhbck6id +# +# CONFIG_SITE - EPICS BASE config file +# by Matthew Needes and Mike Bordua +# +# This file is to be modified by the EPICS system manager +# only. +# + + +# The host architecture performing the build, +# i.e.: the arch running DCT/getrel/etc. +# +# Currently Supporting: +# Borland +# Linux +# WIN32 +# cygwin32 +# hp700 +# sgi +# solaris +# solarisGnu (GNU compiler) +# solaris-x86 +# sun4 + +# +# HOST_ARCH now an environment variable +# HOST_ARCH=$(shell /usr/local/epics/startup/HostArch) +ifndef HOST_ARCH + HOST_ARCH=unsupported +endif + +# The R3.14 architectures to build EPICS for +# +# Currently Supporting: +# vxWorks-486 +# vxWorks-68040 +# vxWorks-68040lc +# vxWorks-68060 +# vxWorks-ppc603 +# vxWorks-ppc604 +# vxWorks-ppc603_long +# vxWorks-ppc604_long + +CROSS_COMPILER_TARGET_ARCHS= + + +# If only a subset of the host architectures perform +# the build for the CROSS_COMPILER_TARGET_ARCHS +# uncomment the following line and specify them. +# +#CROSS_COMPILER_HOST_ARCHS=sun4 + +# Vx release Tornado? +# must be either YES or NO +TORNADO=YES +#TORNADO=NO + +# VxWorks directory for TORNADO=YES +#VX_DIR_YES=c:\\Tornado +#VX_DIR_YES = /usr/local/vw/tornado101 +VX_DIR_YES = /usr/local/vw/tornado202 + +# VxWorks directory for TORNADO=NO +#VX_DIR_NO=$(VW) +#VX_DIR_NO=/usr/local/vw/vxV51.mm +#VX_DIR_NO=/usr/local/vw/vxV52/vw +#VX_DIR_NO=c:/Tornado/target +VX_DIR_NO=/usr/local/vw/vxV52p1/vw + +# Directory for TORNADO=NO gnu gcc crosscompiler +VX_GNU_NO = $(VX_DIR_NO)/../vxgccV2.2.3.1 + +# Directory for TORNADO=NO gnu g++ crosscompiler +# used by applications for hideos builds only +#GNU_DIR = $(LOCAL_GNU) +GNU_DIR = /usr/local/hideos/gnu_install-2.7.2 + +# Client ANSI C Compiler (for Host builds) +# GCC (gcc -ansi) GNU +# ACC (acc) OS VENDOR +# HPACC (cc -Aa) OTHER VENDOR +ANSI=ACC + +# C++ Compiler (for Host builds) +# G++ (g++) GNU C++ +# CCC (CC) OS VENDOR C++ +#Note: if CPLUSPLUS is empty then C++ src codes are not built +#CPLUSPLUS= # use this if site does not have C++ compiler +CPLUSPLUS=CCC + +# Default ANSI level, individual Makefiles will override +# if they cannot support ANSI compilation. +# STRICT - ANSI C - force warning flags +# ANSI - ANSI C +# OLD - Standard C - Not appropriate here. +CMPLR=STRICT + +# Default C++ compiler ANSI level +# STRICT - ANSI C++ force strict warning flags +# NORMAL - ANSI C++ optional warning flags +# Individual Makefiles may override +CXXCMPLR=STRICT + +# Build should install all include files first? +# must be either YES or NO +MAKE_INC_TARGET_FIRST=NO + +# Build shared libraries? +# (archive libraries will also be built) +# must be either YES or NO +# NOTE: CONFIG_SITE.Host.$(HOST_ARCH) files may override +# +# NOTE WIN32: YES results in a DLL. CONFIG_SITE.Host.WIN32 +# distribution file contains YES override +# +# NOTE solaris,Linux, and sun4: If YES then LD_LIBRARY_PATH must +# include fullpathname $(INSTALL_LOCATION)/lib/$(HOST_ARCH) +# for both the base build and when invoking base executables +SHARED_LIBRARIES=NO + +# Build client objects statically ? +# must be either YES or NO +STATIC_BUILD=NO + +# Unix Optimization +# must be either YES or NO +HOST_OPT=YES + +# VxWorks Optimization +# must be either YES or NO +VX_OPT=YES + +# Generate Verbose Compiler Warnings for Unix +# must be either YES or NO +HOST_WARN=YES + +# Generate Verbose Compiler Warnings for VxWorks +# must be either YES or NO +VX_WARN=YES + +# adl2dl +ADL2DL = $(EPICS_EXTENSIONS)/bin/$(HOST_ARCH)/adl2dl + +# sch2edif compiler and flags +SCH2EDIF = sch2edif +SCH2EDIF_PATH = +SCH2EDIF_SYSFLAGS = -n -ap -p.+..+$(SCH2EDIF_PATH)+$(EPICS_EXTENSIONS)/templates/capfast/sym+ +SCH2EDIF_FLAGS = + +# e2db and flags +# - again there is an assumption where edb.def is installed. +E2DB = $(EPICS_EXTENSIONS)/bin/$(HOST_ARCH)/e2db +E2DB_SYSFLAGS = -ate -d $(EPICS_EXTENSIONS)/templates/capfast/edb.def +E2DB_FLAGS = + +#dbst +DBST = $(EPICS_EXTENSIONS)/bin/$(HOST_ARCH)/dbst + +include $(EPICS_BASE)/config/CONFIG_COMPAT diff --git a/config/CONFIG_SITE.Host.Borland b/config/CONFIG_SITE.Host.Borland new file mode 100644 index 000000000..c4444dc68 --- /dev/null +++ b/config/CONFIG_SITE.Host.Borland @@ -0,0 +1,10 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +SHARED_LIBRARIES = YES + +BORLAND=C:\\Borland\\bcc55 + diff --git a/config/CONFIG_SITE.Host.WIN32 b/config/CONFIG_SITE.Host.WIN32 new file mode 100644 index 000000000..49a774151 --- /dev/null +++ b/config/CONFIG_SITE.Host.WIN32 @@ -0,0 +1,8 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +SHARED_LIBRARIES = YES + diff --git a/config/CONFIG_SITE.Host.darwin-ppc b/config/CONFIG_SITE.Host.darwin-ppc new file mode 100644 index 000000000..a9bf27659 --- /dev/null +++ b/config/CONFIG_SITE.Host.darwin-ppc @@ -0,0 +1,5 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.Darwin + +SHARED_LIBRARIES=NO diff --git a/config/CONFIG_SITE.Host.darwin-x86 b/config/CONFIG_SITE.Host.darwin-x86 new file mode 100644 index 000000000..a9bf27659 --- /dev/null +++ b/config/CONFIG_SITE.Host.darwin-x86 @@ -0,0 +1,5 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.Darwin + +SHARED_LIBRARIES=NO diff --git a/config/CONFIG_SITE.Host.hp700 b/config/CONFIG_SITE.Host.hp700 new file mode 100644 index 000000000..dfa9358c5 --- /dev/null +++ b/config/CONFIG_SITE.Host.hp700 @@ -0,0 +1,11 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# By default, shared libraries are built and used. Override here. +#SHARED_LIBRARIES=NO + +# This is the absolute path to the generic INSTALL_LOCATION, for SHARED_LIBRARY searches. +SHRLIB_SEARCH_DIRS += /opt/epics/R$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)/support/base/$(EPICS_VERSION)-$(EPICS_REVISION)-$(EPICS_MODIFICATION)-$(EPICS_UPDATE_NAME)$(EPICS_UPDATE_LEVEL)/lib/$(EPICS_HOST_ARCH) diff --git a/config/CONFIG_SITE.Host.hpux-parisc b/config/CONFIG_SITE.Host.hpux-parisc new file mode 100644 index 000000000..59981047b --- /dev/null +++ b/config/CONFIG_SITE.Host.hpux-parisc @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# hpux-parisc is the new name for hp700 +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.hp700 diff --git a/config/CONFIG_SITE.Host.hpux-parisc-gnu b/config/CONFIG_SITE.Host.hpux-parisc-gnu new file mode 100644 index 000000000..dfa9358c5 --- /dev/null +++ b/config/CONFIG_SITE.Host.hpux-parisc-gnu @@ -0,0 +1,11 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# By default, shared libraries are built and used. Override here. +#SHARED_LIBRARIES=NO + +# This is the absolute path to the generic INSTALL_LOCATION, for SHARED_LIBRARY searches. +SHRLIB_SEARCH_DIRS += /opt/epics/R$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)/support/base/$(EPICS_VERSION)-$(EPICS_REVISION)-$(EPICS_MODIFICATION)-$(EPICS_UPDATE_NAME)$(EPICS_UPDATE_LEVEL)/lib/$(EPICS_HOST_ARCH) diff --git a/config/CONFIG_SITE.Host.linux-x86 b/config/CONFIG_SITE.Host.linux-x86 new file mode 100644 index 000000000..aa15a32f5 --- /dev/null +++ b/config/CONFIG_SITE.Host.linux-x86 @@ -0,0 +1,7 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# linux-x86 is the new name for linux +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.Linux + +#SHARED_LIBRARIES=YES + diff --git a/config/CONFIG_SITE.Host.solaris b/config/CONFIG_SITE.Host.solaris new file mode 100644 index 000000000..f47c67330 --- /dev/null +++ b/config/CONFIG_SITE.Host.solaris @@ -0,0 +1,11 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# APS overrides of definitions + +#GCC = /opt/gnu/bin/gcc +#G++ = /opt/gnu/bin/g++ + diff --git a/config/CONFIG_SITE.Host.solaris-sparc b/config/CONFIG_SITE.Host.solaris-sparc new file mode 100644 index 000000000..ac009d5d2 --- /dev/null +++ b/config/CONFIG_SITE.Host.solaris-sparc @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.solaris diff --git a/config/CONFIG_SITE.Host.solaris-sparc-debug b/config/CONFIG_SITE.Host.solaris-sparc-debug new file mode 100644 index 000000000..ac009d5d2 --- /dev/null +++ b/config/CONFIG_SITE.Host.solaris-sparc-debug @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.solaris diff --git a/config/CONFIG_SITE.Host.solaris-sparc-gnu b/config/CONFIG_SITE.Host.solaris-sparc-gnu new file mode 100644 index 000000000..ac009d5d2 --- /dev/null +++ b/config/CONFIG_SITE.Host.solaris-sparc-gnu @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.solaris diff --git a/config/CONFIG_SITE.Host.solaris-sparc-staticlib b/config/CONFIG_SITE.Host.solaris-sparc-staticlib new file mode 100644 index 000000000..ac009d5d2 --- /dev/null +++ b/config/CONFIG_SITE.Host.solaris-sparc-staticlib @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.solaris diff --git a/config/CONFIG_SITE.Host.solaris-sparc64 b/config/CONFIG_SITE.Host.solaris-sparc64 new file mode 100644 index 000000000..ac009d5d2 --- /dev/null +++ b/config/CONFIG_SITE.Host.solaris-sparc64 @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# solaris-sparc is the new name for solaris +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.solaris diff --git a/config/CONFIG_SITE.Host.win32-x86 b/config/CONFIG_SITE.Host.win32-x86 new file mode 100644 index 000000000..7d2b0e218 --- /dev/null +++ b/config/CONFIG_SITE.Host.win32-x86 @@ -0,0 +1,5 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# win32-x86 is the new name for WIN32 +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.WIN32 + diff --git a/config/CONFIG_SITE.Host.win32-x86-borland b/config/CONFIG_SITE.Host.win32-x86-borland new file mode 100644 index 000000000..8075bcd12 --- /dev/null +++ b/config/CONFIG_SITE.Host.win32-x86-borland @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# win32-x86-borland is the new name for Borland +-include $(EPICS_BASE)/config/CONFIG_SITE.Host.Borland diff --git a/config/CONFIG_SITE.Vx.Linux b/config/CONFIG_SITE.Vx.Linux new file mode 100644 index 000000000..313327c26 --- /dev/null +++ b/config/CONFIG_SITE.Vx.Linux @@ -0,0 +1,10 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file contains overrides for Vx builds + +# The definitions VX_DIR, VX_GNU, GNU_DIR, GNU_LIB, etc. +# can be overridden for specific Linux-target architecture +# combinations by creating a CONFIG_SITE.Vx.Linux. +# file with the override definitions. + +-include $(EPICS_BASE)/config/CONFIG_SITE.$(BUILD_TYPE).$(HOST_ARCH).$(T_A) diff --git a/config/CONFIG_SITE.Vx.Linux.mv167 b/config/CONFIG_SITE.Vx.Linux.mv167 new file mode 100644 index 000000000..07fcfd880 --- /dev/null +++ b/config/CONFIG_SITE.Vx.Linux.mv167 @@ -0,0 +1,11 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file contains overrides for Vx builds + +# ORNL SNS overrides for cross compilers +#VX_DIR_YES = /opt/tornado20/ +#VX_CONFIG_DIR_YES = $(VX_DIR)/target/config +#VX_INCLUDE_YES = /usr/local/crossgcc/m68k/m68k-wrs-vxworks/sys-include +#VX_GNU_YES = /usr/local/crossgcc/m68k/ +#VX_GNU_BIN_YES = $(VX_GNU)/bin +#VX_GNU_LIB_YES = /usr/local/crossgcc/m68k/lib/gcc-lib/m68k-wrs-vxworks/2.95.2 diff --git a/config/CONFIG_SITE.Vx.Linux.ppc603 b/config/CONFIG_SITE.Vx.Linux.ppc603 new file mode 100644 index 000000000..7681f1353 --- /dev/null +++ b/config/CONFIG_SITE.Vx.Linux.ppc603 @@ -0,0 +1,10 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file contains overrides for Vx builds + +# ORNL SNS overrides for cross compilers +#VX_DIR_YES = /opt/tornado20/ +#VX_INCLUDE_YES = /usr/local/crossgcc/ppc/powerpc-wrs-vxworks/sys-include +#VX_GNU_YES = /usr/local/crossgcc/ppc/ +#VX_GNU_BIN_YES = $(VX_GNU)/bin +#VX_GNU_LIB_YES = /usr/local/crossgcc/ppc/lib/gcc-lib/powerpc-wrs-vxworks/2.95.2 diff --git a/config/CONFIG_SITE.Vx.linux-x86 b/config/CONFIG_SITE.Vx.linux-x86 new file mode 100644 index 000000000..efb74c9b2 --- /dev/null +++ b/config/CONFIG_SITE.Vx.linux-x86 @@ -0,0 +1,6 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file contains overrides for Vx builds + +-include $(EPICS_BASE)/config/CONFIG_SITE.Vx.Linux + diff --git a/config/CONFIG_SITE.Vx.ppc604 b/config/CONFIG_SITE.Vx.ppc604 new file mode 100644 index 000000000..a6b09c5ac --- /dev/null +++ b/config/CONFIG_SITE.Vx.ppc604 @@ -0,0 +1,4 @@ +# CONFIG_SITE.Vx.ppc604 + +# APS override of tornado directory for ppc +#VX_DIR_YES = /usr/local/vw/tornado101ppc2 diff --git a/config/CONFIG_SITE_ENV b/config/CONFIG_SITE_ENV new file mode 100644 index 000000000..0e4aabc12 --- /dev/null +++ b/config/CONFIG_SITE_ENV @@ -0,0 +1,45 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: Andrew Johnson +# Date: 1 May 1995 +# +# Experimental Physics and Industrial Control System (EPICS) +# +# CONFIG_SITE_ENV - EPICS Environment Parameter Site configuration file +# +# This file is interpreted by the Bourne Shell, so spaces are +# not allowed around the '=' signs or in unquoted values. +# Makefile variables are not defined here. +# +# Note: This file is read by base/src/libCom/bldEnvdata.pl, +# so the variable definitions in here should be kept 'simple': +# VAR=VALUE +# each one on a single line. +# + +# Site-specific environment settings + +# Time service: +# EPICS_TS_MIN_WEST the local time difference from GMT. +# EPICS_TS_NTP_INET ntp or Unix time server ip addr. + +EPICS_TS_MIN_WEST=360 +EPICS_TS_NTP_INET= + + +# Log Server: +# EPICS_IOC_LOG_INET +# Log server ip addr. +# EPICS_IOC_LOG_FILE_NAME +# pathname to the log file. +# EPICS_IOC_LOG_FILE_LIMIT +# maximum log file size. +# EPICS_IOC_LOG_FILE_COMMAND +# A shell command string used to obtain a new +# path name in response to SIGHUP - the new path name will +# replace any path name supplied in EPICS_IOC_LOG_FILE_NAME + +EPICS_IOC_LOG_INET= +EPICS_IOC_LOG_FILE_NAME= +EPICS_IOC_LOG_FILE_COMMAND= +EPICS_IOC_LOG_FILE_LIMIT=1000000 + diff --git a/config/Makefile b/config/Makefile new file mode 100644 index 000000000..669b66dc4 --- /dev/null +++ b/config/Makefile @@ -0,0 +1,29 @@ +#************************************************************************* +# 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 +# + +TOP=.. + +include $(TOP)/configure/CONFIG + +ifeq ($(findstring YES,$(COMPAT_313) $(COMPAT_TOOLS_313)),YES) +INSTALL_CONFIG = $(INSTALL_LOCATION)/config + +CONFIGS += $(subst ../,,$(wildcard ../CONFIG*)) +CONFIGS += $(subst ../,,$(wildcard ../RULES*)) + +CONFIGS += $(subst ../,,$(wildcard ../tools/*.pl)) + +endif + +include $(TOP)/configure/RULES + diff --git a/config/RULES.Db b/config/RULES.Db new file mode 100644 index 000000000..583bb8878 --- /dev/null +++ b/config/RULES.Db @@ -0,0 +1,215 @@ +#************************************************************************* +# 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 +# +# Rules for making things related to databases +# +MAKEBPT = $(EPICS_BASE_HOST_BIN)/makeBpt$(EXE) + +ifndef MSI +# Tool from R3.14 extensions bin, R3.13 extensions bin, or user path +MSI = $(firstword $(wildcard $(EPICS_EXTENSIONS_HOST_BIN)/msi$(HOSTEXE) \ + $(EPICS_EXTENSIONS)/bin/$(HOST_ARCH)/msi$(HOSTEXE)) msi$(HOSTEXE)) +endif + +DBEXPAND = $(EPICS_BASE_HOST_BIN)/dbExpand$(EXE) +DBST = dbst +MAKEDBDEPENDS = $(PERL) $(TOP)/config/makeDbDepends.pl +REPLACEVAR = $(PERL) $(TOP)/config/replaceVAR.pl +ifndef WIN32 +TOUCH = touch +else +TOUCH = type NUL >> +endif + +#----------------------------------------------------------------- +# if we are not building base add base dbd dirs + +ifneq ($(EPICS_BASE),$(TOP)) +ifneq ($(EPICS_BASE),$(INSTALL_LOCATION)) +EPICS_DBDFLAGS += -I $(EPICS_BASE)/dbd +endif +endif + +#--------------------------------------------------------------- +# ---------------------------------------------------- +# create names (lists) for installed things +# ---------------------------------------------------- + +INSTALL_BPTS = $(BPTS:%= $(INSTALL_DBD)/%) +INSTALL_DBDS = $(DBDINSTALL:%= $(INSTALL_DBD)/%) +INSTALL_DBDNAME = $(DBDNAME:%= $(INSTALL_DBD)/%) +INSTALL_DATA = $(INSTALLDB:%=$(INSTALL_DB)/%) +INSTALL_TEMPLATES = $(filter %.template,$(INSTALL_DATA)) + +#--------------------------------------------------------------- +# Main targets + +all:: install + +inc:: $(INSTALL_DBDS) $(INSTALL_BPTS) $(INSTALL_TEMPLATES) + +rebuild:: clean install + +install:: inc buildInstall + +buildInstall:: build $(INSTALL_DATA) + +clean:: + @echo "Cleaning" + @$(RM) $(DB) $(DBDNAME) *.template *.substitutions *.db.raw \ + *.db-stamp *.edf esiread.cnf + +##################################################### "Foreign" templates + +TEMPLATE_LINKS = $(filter-out $(notdir $(USES_TEMPLATE)), $(USES_TEMPLATE)) +TEMPLATE_FILES = $(filter $(notdir $(USES_TEMPLATE)), $(USES_TEMPLATE)) +DB_STAMP = $(patsubst %.db, %.db-stamp, $(DB)) +DB_REALTARGET = $(patsubst %.db-stamp, %.db, $@) + +ifneq '$(TEMPLATE_LINKS)' '' +build:: $(notdir $(TEMPLATE_LINKS)) +endif +build:: $(INSTALL_DBDNAME) $(TEMPLATE_FILES) $(DB_STAMP) + +$(notdir $(TEMPLATE_LINKS)): %.template: +ifndef WIN32 + @$(RM) $(notdir $(TEMPLATE_LINKS)) + ln -s $(TEMPLATE_LINKS) . +# Workaround for dbLoadTemplate bug: terminate here if link target doesn't exist + @cat $(TEMPLATE_LINKS) > /dev/null +else + @$(RM) $(notdir $(TEMPLATE_LINKS)) + $(CP) $(TEMPLATE_LINKS) . +endif + +##################################################### Inflated or plain databases + +$(INSTALL_DB)/%.db: %.db-stamp + @echo "Installing database $@" + @$(INSTALL) -d -m 644 $(patsubst %.db-stamp, %.db, $<) $(@D) + +# Must have DBDNAME defined to use dbst optimization +ifndef DBDNAME +DB_OPT = NO +endif + +# dbst based database optimization +ifeq '$(DB_OPT)' 'YES' +.PRECIOUS: %.db.raw +%.db-stamp: %.db.raw $(INSTALL_DBD)/$(DBDNAME) + @echo "Optimizing database $@" + $(DBST) $(INSTALL_DBD)/$(DBDNAME) $< -d > $(DB_REALTARGET) + @$(TOUCH) $@ +%.db-stamp: %.t.db.raw $(INSTALL_DBD)/$(DBDNAME) + @echo "Optimizing database $@" + $(DBST) $(INSTALL_DBD)/$(DBDNAME) $< -d > $(DB_REALTARGET) + @$(TOUCH) $@ +else +# NO optimization => move it and keep a stamp +%.db-stamp: %.db.raw + @$(MV) $< $(DB_REALTARGET) + @$(TOUCH) $@ + @$(TOUCH) $< +%.db-stamp: %.t.db.raw + @$(MV) $< $(DB_REALTARGET) + @$(TOUCH) $@ + @$(TOUCH) $< +endif + +%.t.db.raw: %.substitutions + @echo "Inflating database from $<" + @$(RM) $@ + @$(MSI) $< > $@ + +##################################################### CapFast filter + +%.edf:: ../%.sch $(DEPSCHS) + @if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi + $(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) $< + +##################################################### Substitution files + +$(INSTALL_DB)/%.substitutions: %.substitutions + @echo "Installing $@" + @$(INSTALL) -d -m 644 $(@F) $(@D) + +%.substitutions:: ../%.substitutions + @$(CP) $< $@ + +ifdef CREATESUBSTITUTIONS +%.substitutions:: $(word $(words $(CREATESUBSTITUTIONS)),$(CREATESUBSTITUTIONS)) + @$(CREATESUBSTITUTIONS) $* +endif + +# Better make it PRECIOUS (to get around make bug) +.PRECIOUS: %.substitutions + +##################################################### Template databases + +# Installed template files (dbLoadTemplate() on IOC side) +$(INSTALL_DB)/%.template: %.template + @echo "Installing $@" + @$(INSTALL) -d -m 644 $(@F) $(@D) + +%.template:: ../%.template + @$(CP) $< $@ + +%.template: %.edf + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $< + @$(REPLACEVAR) < $@.VAR > $@ + @$(RM) $@.VAR + +##################################################### Flat databases + +%.db.raw:: ../%.db + $(CP) $< $@ + +%.db.raw: %.edf + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $< + @$(REPLACEVAR) < $@.VAR > $@ + @$(RM) $@.VAR + +##################################################### DBD stuff + +$(INSTALL_DBD)/%: % + @echo "Installing $@" + @$(INSTALL) -d -m 644 $< $(@D) + +$(INSTALL_DBD)/%:: ../% + @echo "Installing $@" + @$(INSTALL) -d -m 644 $< $(@D) + +bpt%.dbd: bpt%.data + $(RM) $@ + $(MAKEBPT) $< + +bpt%.dbd:: ../bpt%.data + $(RM) $@ + $(MAKEBPT) $< + +# Patch for old applications +ifdef USER_DBDFLAGS +DBDFLAGS = $(USER_DBDFLAGS) +endif + +ifdef DBDEXPAND +$(DBDNAME): ../$(DBDEXPAND) + @echo "Expanding dbd" + @$(RM) $@ + $(DBEXPAND) $(DBDFLAGS) -o $@ $< +endif + +##################################################### Dependencies + +DEPENDS: $(filter $(patsubst %.db, %.substitutions, $(DB)), $(wildcard *.substitutions)) + @$(MAKEDBDEPENDS) $^ + +-include DEPENDS diff --git a/config/RULES.Host b/config/RULES.Host new file mode 100644 index 000000000..5471b4f21 --- /dev/null +++ b/config/RULES.Host @@ -0,0 +1,855 @@ +#************************************************************************* +# 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 +# +# Rules for making things specified in Makefile.Host +# Some rules for filename-massage are system specific +# and have "ifdefs" here instead of using definitions +# from CONFIG.Host.$(ARCH_CLASS) - sorry about this, +# but so far the rules are quite similar on all systems +# except WIN32 has some specials. +# +# Maybe there is a way to use indentation to make it +# easier to read this file? +# +# Most things may also work if you say e.g. +# VAR+=ADD +# even if ADD is not there, but this way "VAR" would +# be defined in any case, that's why I try to use +# ifdef ADD +# VAR+=ADD +# endif +# +# -kuk- + +# we are in O.$(ARCH_CLASS), but most sources are one dir above: +# +# The use of VPATH (no suffix specification) caused everything +# to break several times. +# vpath, of course, has the disadvantage that we need explicit rules +# for scripts or similar os-specific filed which have _no_ suffix... +vpath %.h $(USER_VPATH) +vpath %.c $(USER_VPATH) ../os/$(ARCH_CLASS) ../os/generic .. +vpath %.cc $(USER_VPATH) ../os/$(ARCH_CLASS) ../os/generic .. +vpath %.cpp $(USER_VPATH) ../os/$(ARCH_CLASS) ../os/generic .. +vpath %.rc $(USER_VPATH) ../os/$(ARCH_CLASS) ../os/generic .. +vpath %.jar $(USER_VPATH) .. + +# check for add-on CFLAGS and CXXFLAGS +# +# Rules: +# 1) USR_CFLAGS is used +# 2) if there is a special USR_CFLAGS_$(ARCH_CLASS), it's +# appended to 1) +# 3) if there is no special defined, but a generic USR_CFLAGS_DEFAULT, +# this one is appended +# 4) if you have the special case that your USR_CFLAGS_$(ARCH_CLASS) is +# empty but you don't want 3), you have to define it as '-nil-', e.g.: +# USR_CFLAGS = +# USR_CFLAGS_sun4 = -nil- +# USR_CFLAGS_DEFAULT = +# +# These rules apply to these Makefile-variables: +# USR_CFLAGS C flags +# USR_CXXFLAGS C++ flags +# INC include-files to install +# LIBSRCS source files for building library +# PROD_LIBS EPICS libs needed by PROD and TESTPROD +# USR_LIBS NONEPICS libs needed by PROD and TESTPROD +# SYS_PROD_LIBS system libs needed by PROD and TESTPROD +# PROD products to build and install +# SCRIPTS scripts to install +# +# Remark: +# If you define a special INC, e.g. INC_WIN32 = getopt.h, +# the source (getopt.h) has to be in os/WIN32 (or os/) +# +# This makes INC_$(ARCH_CLASS) slightly different from OSINC: +# OSINC = a_file.h +# means that you have a special os/$(ARCH_CLASS)/a_file.h +# for _every_ ARCH_CLASS. +# If you use INC_$(ARCH_CLASS), you need the special include +# only for the specified ARCH_CLASS! +# +ifneq ($(strip $(USR_CFLAGS_$(ARCH_CLASS))),) +USR_CFLAGS+=$(subst -nil-,,$(USR_CFLAGS_$(ARCH_CLASS))) +else +ifdef USR_CFLAGS_DEFAULT +USR_CFLAGS+=$(USR_CFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_CXXFLAGS_$(ARCH_CLASS))),) +USR_CXXFLAGS+=$(subst -nil-,,$(USR_CXXFLAGS_$(ARCH_CLASS))) +else +ifdef USR_CXXFLAGS_DEFAULT +USR_CXXFLAGS+=$(USR_CXXFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_CPPFLAGS_$(ARCH_CLASS))),) +USR_CPPFLAGS+=$(subst -nil-,,$(USR_CPPFLAGS_$(ARCH_CLASS))) +else +ifdef USR_CPPFLAGS_DEFAULT +USR_CPPFLAGS+=$(USR_CPPFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_LDFLAGS_$(ARCH_CLASS))),) +USR_LDFLAGS+=$(subst -nil-,,$(USR_LDFLAGS_$(ARCH_CLASS))) +else +ifdef USR_LDFLAGS_DEFAULT +USR_LDFLAGS+=$(USR_LDFLAGS_DEFAULT) +endif +endif + +# check for special includes: +# +ifneq ($(strip $(INC_$(ARCH_CLASS))),) +# os-specific includes go to the include/os-directory: +OSINC += $(subst -nil-,,$(INC_$(ARCH_CLASS))) +else +ifdef INC_DEFAULT +INC += $(INC_DEFAULT) +endif +endif + +# concat specific library contents (if defined) to LIBCONTENS +# +ifneq ($(strip $(LIBSRCS_$(ARCH_CLASS))),) +LIBSRCS += $(subst -nil-,,$(LIBSRCS_$(ARCH_CLASS))) +else + +ifdef LIBSRCS_DEFAULT +LIBSRCS+=$(LIBSRCS_DEFAULT) +endif + +endif + +# adjust object names for library contents +# +ifdef LIBSRCS +LIBOBJS=$(addsuffix $(OBJ), $(basename $(LIBSRCS))) +endif + +# +# concat specific library contents (if defined) to PROD_LIBS +# +ifneq ($(strip $(PROD_LIBS_$(ARCH_CLASS))),) +PROD_LIBS += $(subst -nil-,,$(PROD_LIBS_$(ARCH_CLASS))) + +else + +ifdef PROD_LIBS_DEFAULT +PROD_LIBS += $(PROD_LIBS_DEFAULT) +endif + +endif + +# +# concat specific library contents (if defined) to USR_LIBS +# +ifneq ($(strip $(USR_LIBS_$(ARCH_CLASS))),) +USR_LIBS += $(subst -nil-,,$(USR_LIBS_$(ARCH_CLASS))) + +else + +ifdef USR_LIBS_DEFAULT +USR_LIBS += $(USR_LIBS_DEFAULT) +endif + +endif + +# +# concat specific library contents (if defined) to SYS_PROD_LIBS +# +ifneq ($(strip $(SYS_PROD_LIBS_$(ARCH_CLASS))),) +SYS_PROD_LIBS += $(subst -nil-,,$(SYS_PROD_LIBS_$(ARCH_CLASS))) + +else + +ifdef SYS_PROD_LIBS_DEFAULT +SYS_PROD_LIBS += $(SYS_PROD_LIBS_DEFAULT) +endif + +endif + +# +# concat specific products +# +ifneq ($(strip $(PROD_$(ARCH_CLASS))),) +PROD += $(subst -nil-,,$(PROD_$(ARCH_CLASS))) + +else + +ifdef PROD_DEFAULT +PROD += $(PROD_DEFAULT) +endif + +endif + +# +# concat specific scripts +# +ifneq ($(strip $(SCRIPTS_$(ARCH_CLASS))),) +SCRIPTS += $(subst -nil-,,$(SCRIPTS_$(ARCH_CLASS))) + +else + +ifdef SCRIPTS_DEFAULT +SCRIPTS += $(SCRIPTS_DEFAULT) +endif + +endif + +# +# concat specific resource files +# +ifneq ($(strip $(RCS_$(ARCH_CLASS))),) +RCS += $(subst -nil-,,$(RCS_$(ARCH_CLASS))) +else +ifdef RCS_DEFAULT +RCS += $(RCS_DEFAULT) +endif +endif + +# adjust executables +ifdef TESTPROD +TESTPROD := $(addsuffix $(EXE), $(TESTPROD)) +endif + +# adjust executables +ifdef PROD +PROD := $(addsuffix $(EXE), $(PROD)) +endif + +#----------------------------------------------------------------- +# if we are not building base add base includes and dbd dirs +# (convenience for extensions and applications) +ifneq ($(EPICS_BASE),$(TOP)) +ifneq ($(EPICS_BASE),$(INSTALL_LOCATION)) +EPICS_INCLUDES += -I$(EPICS_BASE_INCLUDE)/os/$(OS_CLASS) -I$(EPICS_BASE_INCLUDE) +EPICS_DBDFLAGS += -I $(EPICS_BASE)/dbd +endif +endif + +#--------------------------------------------------------------- +# ---------------------------------------------------- +# create names (lists) for installed things +# ---------------------------------------------------- + +INCREC +=$(RECTYPES) $(MENUS) + +INSTALL_PROD= $(PROD:%= $(INSTALL_BIN)/%) +INSTALL_LIBS= $(LIBNAME:%=$(INSTALL_LIB)/%) +INSTALL_SHRLIBS= $(SHRLIBNAME:%=$(INSTALL_SHRLIB)/%) +INSTALL_DLL_LINK_LIBS=$(DLL_LINK_LIBNAME:%=$(INSTALL_LIB)/%) +INSTALL_TCLLIBS=$(TCLLIBNAME:%=$(INSTALL_TCLLIB)/%) +INSTALL_TCLINDEX=$(TCLINDEX:%=$(INSTALL_TCLLIB)/%) + +INSTALL_INC= $(INC:%=$(INSTALL_INCLUDE)/%) +INSTALL_OSINCLUDE=$(INSTALL_INCLUDE)/os/$(ARCH_CLASS) +INSTALL_OSINC= $(OSINC:%= $(INSTALL_OSINCLUDE)/%) +INSTALL_INCREC = $(INCREC:%= $(INSTALL_INCLUDE)/%) +MANLIST = 1 2 3 4 5 6 7 8 9 +INSTALL_MANS = $(foreach n, \ + $(MANLIST), $(MAN$(n):%= $(INSTALL_MAN)/man$(n)/%)) +INSTALL_DOCS = $(DOCS:%= $(INSTALL_DOC)/%) +INSTALL_HTMLS = $(HTMLS:%= $(INSTALL_HTML)/$(HTMLS_DIR)/%) +INSTALL_SCRIPTS = $(SCRIPTS:%= $(INSTALL_BIN)/%) +ifdef TEMPLATES_DIR +INSTALL_TEMPLATES_SUBDIR = $(INSTALL_TEMPLATES)/$(TEMPLATES_DIR) +else +INSTALL_TEMPLATES_SUBDIR = $(INSTALL_TEMPLATES) +endif +INSTALL_TEMPLATE = $(TEMPLATES:%= $(INSTALL_TEMPLATES_SUBDIR)/%) +INSTALL_CONFIGS = $(CONFIGS:%= $(INSTALL_CONFIG)/%) + +INSTALL_BPTS = $(BPTS:%= $(INSTALL_DBD)/%) +INSTALL_DBS = $(DBDINSTALL:%= $(INSTALL_DBD)/%)\ + $(RECTYPES:%.h= $(INSTALL_DBD)/%.dbd)\ + $(MENUS:%.h= $(INSTALL_DBD)/%.dbd) + +INSTALL_DBDNAME = $(DBDNAME:%= $(INSTALL_DBD)/%) + +MAN_DIRECTORY_TARGETS = $(foreach n, $(MANLIST),$(INSTALL_MAN)/man$(n)) + +#--------------------------------------------------------------- +ifneq ($(strip $(SHARED_LIBRARIES_$(ARCH_CLASS))),) +SHARED_LIBRARIES+=$(subst -nil-,,$(SHARED_LIBRARIES_$(ARCH_CLASS))) +else +ifdef SHARED_LIBRARIES_DEFAULT +SHARED_LIBRARIES_+=$(SHARED_LIBRARIES_DEFAULT) +endif +endif + +#--------------------------------------------------------------- +# always use c++ linker +ifneq ($(strip $(CPLUSPLUS)),) +LINK.c = $(LINK.cc) +endif # CPLUSPLUS + +#--------------------------------------------------------------- +# Version number for base shared libraries (and win32 products) +ifeq ($(EPICS_BASE),$(TOP)) +SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION) +PROD_VERSION = $(EPICS_VERSION).$(EPICS_REVISION) +endif # EPICS_BASE + +#--------------------------------------------------------------- +# Libraries +# +# if there are no objects LIBOBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +ifdef LIBRARY +ifdef LIBOBJS +LIBTARGETS += $(LIBNAME) $(INSTALL_LIBS) + +LIBNAME_RCS=$(foreach lib, $(basename $(LIBRARY)), $(RCS) $($(lib)_RCS) $($(lib)_RCS_$(ARCH_CLASS))) +LIBNAME_RESS=$(addsuffix $(RES), $(basename $(LIBNAME_RCS))) + +# check if shared libraries requested +ifeq ($(strip $(SHARED_LIBRARIES)),YES) + +CFLAGS += $($(ANSI)_SHRLIB_CFLAGS_YES) +CXXFLAGS += $($(CPLUSPLUS)_SHRLIB_CFLAGS_YES) +SHRLIB_LDFLAGS = $($(CPLUSPLUS)_SHRLIB_LDFLAGS_YES) + +PROD_VERSION =$(SHRLIB_VERSION) +# always use c++ linker +SHRLIB_LINKER = $(CXX) +LIBTARGETS += $(SHRLIBNAME) $(INSTALL_SHRLIBS) $(INSTALL_DLL_LINK_LIBS) + +endif # SHARED_LIBRARIES=YES + +endif # LIBOBJS +endif # LIBRARY + +#--------------------------------------------------------------- +all:: install + +build:: inc + +build:: $(LIBTARGETS) $(PROD) $(TESTPROD) $(INSTALLS) \ + $(MENUS) $(RECTYPES) $(BPTS) +ifdef DBDEXPAND +build:: $(DBDNAME) +endif + +inc:: $(INSTALL_INC) $(INSTALL_OSINC) + +rebuild:: clean install + +install:: buildInstall + +buildInstall :: build $(TARGETS) \ + $(INSTALL_SCRIPTS) $(INSTALL_PROD) \ + $(INSTALL_MANS) \ + $(INSTALL_DOCS) \ + $(INSTALL_HTMLS) \ + $(INSTALL_TEMPLATE) \ + $(INSTALL_CONFIGS) \ + $(INSTALL_DBS) $(INSTALL_BPTS) \ + $(INSTALL_DBDNAME) $(INSTALL_INCREC) \ + $(INSTALL_TCLLIBS) $(INSTALL_TCLINDEX) + +clean:: + @echo "Cleaning" + @$(RM) *.i *$(OBJ) *.a $(PROD) $(TESTPROD) $(LIBNAME) $(SHRLIBNAME) $(INC) \ + *$(RES) $(TARGETS) *.dbd $(MENUS) $(RECTYPES) $(BPTS) +ifdef DBDEXPAND + @$(RM) $(DBDNAME) +endif + +#--------------------------------------------------------------- +# Products +# + +PROD += $(TESTPROD) +ifdef PROD + +COND_PROD_SRCS=$(foreach prod, $(basename $(PROD)), $($(prod)_SRCS)) +COND_PROD_RCS=$(foreach prod, $(basename $(PROD)), $($(prod)_RCS) $($(prod)_RCS_$(ARCH_CLASS))) + +ifdef PRODNAME + +ifneq ($(strip $(PRODNAME_SRCS_$(ARCH_CLASS))),) +PRODNAME_SRCS += $(subst -nil-,,$(PRODNAME_SRCS_$(ARCH_CLASS))) +else +ifdef PRODNAME_SRCS_DEFAULT +PRODNAME_SRCS += $(PRODNAME_SRCS_DEFAULT) +endif +endif + +ifneq ($(strip $(PRODNAME_OBJS_$(ARCH_CLASS))),) +PRODNAME_OBJS += $(subst -nil-,,$(PRODNAME_OBJS_$(ARCH_CLASS))) +else +ifdef PRODNAME_OBJS_DEFAULT +PRODNAME_OBJS += $(PRODNAME_OBJS_DEFAULT) +endif +endif + +ifneq ($(strip $(PRODNAME_RCS_$(ARCH_CLASS))),) +PRODNAME_RCS += $(subst -nil-,,$(PRODNAME_RCS_$(ARCH_CLASS))) +else +ifdef PRODNAME_RCS_DEFAULT +PRODNAME_RCS += $(PRODNAME_RCS_DEFAULT) +endif +endif + +$(PRODNAME): $(PROD_DEPLIBS) $(PRODNAME_DEPLIBS) + +ifdef PRODNAME_SRCS + +ifeq ($(findstring cc,$(suffix $(PRODNAME_SRCS))),cc) +PRODNAME_LINKER = $(LINK.cc) +else +PRODNAME_LINKER = $(LINK.c) +endif + +PRODNAME_OBJS+=$(addsuffix $(OBJ), $(basename $(PRODNAME_SRCS))) +PRODNAME_RESS+=$(addsuffix $(RES), $(basename $(PRODNAME_RCS))) + +ifdef BORLANDC +$(PRODNAME): $(PRODNAME_OBJS) $(PRODNAME_RESS) + @$(RM) $@ + $(PRODNAME_LINKER) $(PRODNAME_OBJS) , $@ ,,$(LINKLIBS) $(subst /,\\,$(LDLIBS)),, $(PRODNAME_RESS) + +else +$(PRODNAME): $(PRODNAME_OBJS) $(PRODNAME_RESS) + @$(RM) $@ + $(PRODNAME_LINKER) $(PRODNAME_OBJS) $(PRODNAME_RESS) $(LDLIBS) + $(MT_EXE_COMMAND) + +endif +endif # ifdef PRODNAME_SRCS + +else # PRODNAME not defined + +# We have to use the product's true dependancies and +# call make again to determine if product should be rebuilt + +ifneq ($(strip $(SRCS) $(COND_PROD_SRCS)),) +PROD_OBJS=$(addsuffix $(OBJ), $(basename $(SRCS) $(COND_PROD_SRCS))) + +PROD_RESS=$(addsuffix $(RES), $(basename $(RCS) $(COND_PROD_RCS))) + +PROD_MAKE_COMMAND=$(MAKE) $@\ + PRODNAME="$@"\ + PRODNAME_SRCS="$(SRCS) $($(basename $@)_SRCS)"\ + PRODNAME_SRCS_DEFAULT="$($(basename $@)_SRCS_DEFAULT)"\ + PRODNAME_SRCS_$(ARCH_CLASS)="$($(basename $@)_SRCS_$(ARCH_CLASS))"\ + PRODNAME_RCS="$(RCS) $($(basename $@)_RCS)"\ + PRODNAME_RCS_DEFAULT="$($(basename $@)_RCS_DEFAULT)"\ + PRODNAME_RCS_$(ARCH_CLASS)="$($(basename $@)_RCS_$(ARCH_CLASS))"\ + PRODNAME_LIBS="$($(basename $@)_LIBS)" + +$(PROD): $(SRCS) $(PROD_OBJS) $(COND_PROD_SRCS) $(PROD_RESS) $(PROD_DEPLIBS) $(COND_PROD_DEPLIBS) + @$(PROD_MAKE_COMMAND) + +endif +endif #ifdef PRODNAME + +endif #ifdef PROD + +#--------------------------------------------------------------- +# Java classes and packages +# + +ifdef JAVA +DIRECTORY_TARGETS += $(INSTALL_JAVA) +ifdef PACKAGE +DIRECTORY_TARGETS += $(INSTALL_JAVA)/$(PACKAGE) +endif +endif + +vpath %.class $(INSTALL_JAVA)/$(PACKAGE) + +CLASSES += $(subst .java,.class,$(JAVA)) +TESTCLASSES += $(subst .java,.class,$(TESTJAVA)) +INSTALL_CLASSES =$(CLASSES:%=$(INSTALL_JAVA)/$(PACKAGE)/%) +INSTALL_JAR =$(JAR:%=$(INSTALL_JAVA)/%) + +ifeq ($(strip $(JAVADOC)),YES) +DIRECTORY_TARGETS += $(INSTALL_HTML) +INSTALL_JAVADOC = $(CLASSES:%.class=$(INSTALL_HTML)/%.html) +ifdef PACKAGE +DIRECTORY_TARGETS += $(INSTALL_HTML)/$(PACKAGE) +INSTALL_JAVADOC = $(CLASSES:%.class=$(INSTALL_HTML)/$(PACKAGE)/$(PACKAGE)/%.html) +endif +endif + +JAR_OPTIONS = cvf +ifdef MANIFEST +JAR_OPTIONS = cvmf +endif +#JAR_DEPFILES += $(wildcard $(JAR_INPUT) $(addsuffix /*,$(JAR_INPUT))) +JAR_DEPFILES += $(JAR_INPUT) + +$(DIRECTORY_TARGETS) : + $(MKDIR) $@ + +build:: $(TESTCLASSES) $(JAR) + +buildInstall :: $(DIRECTORY_TARGETS) $(INSTALL_CLASSES) $(INSTALL_JAR) $(INSTALL_JAVADOC) + +clean:: + @$(RM) $(TESTCLASSES) $(JAR) $(INSTALL_CLASSES) $(INSTALL_JAVADOC) + +%.class:%.java + @echo Creating java class file $@ + $(RM) $@ + $(JAVACCMD) $< + +$(INSTALL_JAVA)/$(PACKAGE)/%.class:%.java + @echo Creating java class file $@ + @$(RM) $@ + $(JAVACCMD) -d $(INSTALL_JAVA) $< + +$(INSTALL_JAVADOC):$(JAVA) + @echo Creating javadoc html files $@ + @$(RM) $@ + $(JAVADOCCMD) -d $(INSTALL_HTML)/$(PACKAGE) $(PACKAGE) + +$(JAR):%.jar: $(JAR_DEPFILES) + @echo Creating java jar file $@ + @$(RM) $@ + $(JARCMD) + +$(INSTALL_JAVA)/%.jar: %.jar + @echo "Installing jar file $@" + @$(INSTALL) -d -m 644 $< $(@D) + +$(PACKAGE)_%.h:$(INSTALL_JAVA)/$(PACKAGE)/%.class + $(JAVAHCMD) $(PACKAGE).$* + +.PRECIOUS: $(INSTALL_CLASSES) $(INSTALL_JAVADOC) + +#--------------------------------------------------------------- +#--------------------------------------------------------------- +# Generic Rules for 'simple' targets that +# can be generated from a single source with same basename. +# +# The usual two rules .c* -> $(OBJ) and then $(OBJ) -> $(EXE) +# do not work because the $(OBJ)->$(EXE) rule wouldn't +# know if the original source was C or C++. +# +# Hint: The $(subst...) construct removes the .c or .cc +# as well as the '../' from the filename and adds $(OBJ): +# e.g. $< = '../abc.c' -> 'abc.o' +# +# The order of the following rules is +# VERY IMPORTANT !!!! + +depends:: $(LIBSRCS) $(SRCS) $(COND_PROD_SRCS) $(SRCS.c) $(SRCS.cc) + $(RM) DEPENDS +ifneq ($(strip $(LIBSRCS) $(SRCS) $(COND_PROD_SRCS) $(SRCS.c) $(SRCS.cc)),) + $(DEPENDS_RULE) +endif + +ifdef BORLANDC +%$(EXE): %.c + @$(RM) $@ + $(COMPILE.c) $< + $(LINK.c) $(subst ../,,$(basename $<))$(OBJ) , $@ ,,$(LINKLIBS) $(subst /,\\,$(LDLIBS)) + +%$(EXE): %.cc + @$(RM) $@ + $(COMPILE.cc) $< + $(LINK.cc) $(subst ../,,$(basename $<))$(OBJ) , $@ ,,$(LINKLIBS) $(subst /,\\,$(LDLIBS)) + +%$(EXE): %.cpp + @$(RM) $@ + $(COMPILE.cc) $< + $(LINK.cc) $(subst ../,,$(basename $<))$(OBJ) , $@ ,,$(LINKLIBS) $(subst /,\\,$(LDLIBS)) + +%$(EXE): %.C + @$(RM) $@ + $(COMPILE.cc) $< + $(LINK.cc) $(subst ../,,$(basename $<))$(OBJ) , $@ ,,$(LINKLIBS) $(subst /,\\,$(LDLIBS)) + +else + +%$(EXE): %.c + @$(RM) $@ + $(COMPILE.c) $< + $(LINK.c) $(subst ../,,$(basename $<))$(OBJ) $(LDLIBS) + +%$(EXE): %.cc + @$(RM) $@ + $(COMPILE.cc) $< + $(LINK.cc) $(subst ../,,$(basename $<))$(OBJ) $(LDLIBS) + +%$(EXE): %.cpp + @$(RM) $@ + $(COMPILE.cc) $< + $(LINK.cc) $(subst ../,,$(basename $<))$(OBJ) $(LDLIBS) + +%$(EXE): %.C + @$(RM) $@ + $(COMPILE.cc) $< + $(LINK.cc) $(subst ../,,$(basename $<))$(OBJ) $(LDLIBS) + +endif + +%$(OBJ): %.c + @$(RM) $@ + $(COMPILE.c) $< + +%$(OBJ): %.cc + @$(RM) $@ + $(COMPILE.cc) $< + +%$(OBJ): %.cpp + @$(RM) $@ + $(COMPILE.cc) $< + +%$(OBJ): %.C + @$(RM) $@ + $(COMPILE.cc) $< + +# WIN95/NT resource compiler +%$(RES): %.rc + @$(RM) $@ + $(RCCMD) + +# +# rename the y.tab.h file only if we +# are creating it +# +%.h %.c: ../%.y + $(RM) $*.c y.tab.c +ifeq ($(findstring -d, $(YACCOPT)),-d) + $(RM) $*.h y.tab.h +endif + $(YACC) $(YACCOPT) $< + $(MV) y.tab.c $*.c +ifeq ($(findstring -d, $(YACCOPT)),-d) + $(MV) y.tab.h $*.h +endif + +%.c: ../%.l + @$(RM) lex.yy.c + $(LEX) $(LEXOPT) $< + @$(RM) $@ + $(MV) lex.yy.c $@ + +#state notation language rule +%.c: ../%.st + @echo "preprocessing $*.st" + @$(RM) $*.i + $(CPP) $(CPPFLAGS) $< $*.i + @echo "converting $*.i" + @$(RM) $@ + $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $*.i + +%.c: ../%.stt + @echo "converting $<" + ln -s $< $*.st + $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $*.st + @$(RM) $*.st + +# Capfast Rules: +%.db: %.edf + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) $< + +%.db: ../%.edf + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) $< + +%.edf: ../%.sch $(DEPSCHS) + @if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi + $(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) $< + +# Adl2dl rule +%.dl : ../%.adl + -$(ADL2DL) $< $@ + +# Mangen Rule: +%.1:% + $(MANGEN) -s $< + $(MV) $( nm.out + $(PERL) $(EPICS_BASE_TOOLS)/munch.pl < nm.out > ctdt.c + $(COMPILE.c) -traditional ctdt.c + $(LINK.cc) $@ $< ctdt.o + +# C++ munching for VxWorks +$(MUNCHNAME):%.munch : % + @ $(RM) ctct.o ctdt.c nm.out + $(NM) $< > nm.out + $(PERL) $(EPICS_BASE_TOOLS)/munch.pl < nm.out > ctdt.c + $(COMPILE.c) -traditional ctdt.c + $(LINK.cc) $@ $(LDFLAGS) $< ctdt.o + +# +# rename the y.tab.h file only if we +# are creating it +# +%.h %.c: ../%.y + $(RM) $*.c y.tab.c +ifeq ($(findstring -d, $(YACCOPT)),-d) + $(RM) $*.h y.tab.h +endif + $(YACC) $(YACCOPT) $< + $(MV) y.tab.c $*.c +ifeq ($(findstring -d, $(YACCOPT)),-d) + $(MV) y.tab.h $*.h +endif + +%.c: ../%.l + $(RM) lex.yy.c + $(LEX) $(LEXOPT) $< + $(RM) $@ + $(MV) lex.yy.c $@ + +#state notation language rules +%.c: ../%.st + @echo "preprocessing $*.st" + @$(RM) $*.i + $(CPP) $(CPPFLAGS) $(CPPSNCFLAGS) $< > $*.i + @echo "converting $*.i" + @$(RM) $@ + $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $*.i + +%.c: ../%.stt + @echo "converting $<" + ln -s $< $*.st + $(SNC) $(TARGET_SNCFLAGS) $(SNCFLAGS) $*.st + @$(RM) $*.st + +# Capfast Rules: +%.db: %.edf + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) $< + +%.db: ../%.edf + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) $< + +%.edf: ../%.sch + @if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi + $(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) $< + +# Adl2dl rule +%.dl : ../%.adl + -$(ADL2DL) $< $@ + +# Mangen Rule: +%.1:% + $(MANGEN) -s $< + $(MV) $( ???? the following line causes a rebuild every time +#=====> $(LIBNAME): ../Makefile.Vx + diff --git a/config/RULES_ARCHS b/config/RULES_ARCHS new file mode 100644 index 000000000..3afacca2c --- /dev/null +++ b/config/RULES_ARCHS @@ -0,0 +1,99 @@ +#************************************************************************* +# 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 +# +# EPICS RULES_ARCH +# by Matthew Needes and Mike Bordua and Janet Anderson and Jeff Hill +# + +all:: install + +ACTIONS += inc +ACTIONS += build +ACTIONS += install +ACTIONS += buildInstall +ACTIONS += depends +ACTIONS += rebuild + +actionPart = $(word 1, $(subst $(DIVIDER), ,$@)) +archPart = $(word 2, $(subst $(DIVIDER), ,$@)) + +# +# hostActionArchTargets +# +hostArchs = $(HOST_ARCH) +hostActionArchTargets = $(foreach x, $(ACTIONS),\ + $(foreach arch,$(hostArchs), $(x)$(DIVIDER)$(arch))) +ifeq (Makefile.Host, $(wildcard Makefile.Host)) +hostDirs = $(addprefix O.,$(hostArchs)) +$(hostActionArchTargets) : $(hostDirs) + $(MAKE) -C O.$(archPart) -f ../Makefile.Host T_A=$(archPart) BUILD_TYPE=Host $(actionPart) +$(hostArchs) : % : O.% + $(MAKE) -C O.$@ -f ../Makefile.Host T_A=$@ BUILD_TYPE=Host +else +$(hostActionArchTargets) : +$(hostArchs) : +endif + +# +# crossActionArchTargets +# +crossArchs = $(filter-out $(hostArchs),$(BUILD_ARCHS)) +crossActionArchTargets = $(foreach x, $(ACTIONS), \ + $(foreach arch, $(CROSS_COMPILER_TARGET_ARCHS), $(x)$(DIVIDER)$(arch))) +ifeq (Makefile.Vx, $(wildcard Makefile.Vx)) +crossDirs = $(addprefix O.,$(crossArchs)) +$(crossActionArchTargets) : $(crossDirs) + $(MAKE) -C O.$(archPart) -f ../Makefile.Vx T_A=$(archPart) BUILD_TYPE=Vx $(actionPart) +$(crossArchs) : % : O.% + $(MAKE) -C O.$@ -f ../Makefile.Vx T_A=$@ BUILD_TYPE=Vx +else +$(crossActionArchTargets) : +$(crossArchs) : +endif + +$(hostDirs) : $(EPICS_BASE_TOOLS)/makeMakefile.pl + $(PERL) $(EPICS_BASE_TOOLS)/makeMakefile.pl $@ Host + +$(crossDirs) : $(EPICS_BASE_TOOLS)/makeMakefile.pl + $(PERL) $(EPICS_BASE_TOOLS)/makeMakefile.pl $@ Vx + +# +# host/cross action targets +# +$(ACTIONS) :: % : %$(DIVIDER)host %$(DIVIDER)cross +HostActionTargets = $(foreach x, $(ACTIONS) clean, $(x)$(DIVIDER)host) +CrossActionTargets = $(foreach x, $(ACTIONS) clean, $(x)$(DIVIDER)cross) +$(HostActionTargets) : %$(DIVIDER)host : $(addprefix %$(DIVIDER), $(hostArchs)) +$(CrossActionTargets) : %$(DIVIDER)cross : $(addprefix %$(DIVIDER), $(crossArchs)) + + +# +# arch targets +# +host : $(hostArchs) +cross : $(crossArchs) + +# +# special clean rule +# +clean :: + $(RMDIR) $(hostDirs) $(crossDirs) +clean$(DIVIDER)% : + $(RMDIR) O.$* + +.PHONY :: $(HostActionTargets) +.PHONY :: $(CrossActionTargets) +.PHONY :: $(crossActionArchTargets) +.PHONY :: $(hostActionArchTargets) +.PHONY :: $(hostArchs) $(crossArchs) +.PHONY :: $(ACTIONS) clean all host cross + diff --git a/config/RULES_DIRS b/config/RULES_DIRS new file mode 100644 index 000000000..00af7b166 --- /dev/null +++ b/config/RULES_DIRS @@ -0,0 +1,57 @@ +#************************************************************************* +# 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 +# + + +ARCHS += $(BUILD_ARCHS) host cross +ACTIONS += clean inc depends build install buildInstall + +dirPart = $(word 1, $(subst $(DIVIDER), ,$@)) +actionArchPart = $(join $(word 2, $(subst $(DIVIDER), ,$@)), \ + $(addprefix $(DIVIDER),$(word 3, $(subst $(DIVIDER), ,$@)))) + +dirActionArchTargets = $(foreach dir, $(DIRS), \ + $(foreach action, $(ACTIONS),\ + $(foreach arch, $(ARCHS), \ + $(dir)$(DIVIDER)$(action)$(DIVIDER)$(arch)))) +dirArchTargets += $(foreach dir, $(DIRS), \ + $(foreach arch, $(ARCHS),\ + $(dir)$(DIVIDER)$(arch))) +dirActionTargets += $(foreach dir, $(DIRS), \ + $(foreach action, $(ACTIONS),\ + $(dir)$(DIVIDER)$(action))) +actionArchTargets = $(foreach action, $(ACTIONS),\ + $(foreach arch, $(ARCHS), \ + $(action)$(DIVIDER)$(arch))) +ifeq ($(MAKE_INC_TARGET_FIRST),YES) +all install :: inc buildInstall +$(ARCHS) $(installArchTargets) :: inc +else +all install :: buildInstall +endif + + +rebuild:: clean all + +$(DIRS) $(dirActionTargets) $(dirArchTargets)$(dirActionArchTargets) :: + $(MAKE) -C $(dirPart) $(actionArchPart) + +$(ARCHS) $(ACTIONS) $(actionArchTargets) ::%: \ + $(foreach dir, $(DIRS), $(dir)$(DIVIDER)%) + + +.PHONY :: $(DIRS) all rebuild +.PHONY :: $(ARCHS) $(ACTIONS) +.PHONY :: $(dirActionTargets) $(dirArchTargets) +.PHONY :: $(dirActionArchTargets) +.PHONY :: $(actionArchTargets) + diff --git a/config/RULES_TOP b/config/RULES_TOP new file mode 100644 index 000000000..3d8430f2e --- /dev/null +++ b/config/RULES_TOP @@ -0,0 +1,73 @@ +#************************************************************************* +# 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 +# + +include $(TOP)/config/RULES_DIRS + +uninstall$(DIVIDER)%: uninstallDirs + @$(RMDIR) $(INSTALL_LOCATION_BIN)/$* $(INSTALL_LOCATION_LIB)/$* + +cleandirs: + @echo " " #stops "nothing to be done for cleandirs" message +ifeq ($(wildcard $(INSTALL_LOCATION_BIN)/*),) + @$(RMDIR) $(INSTALL_LOCATION_BIN) +endif +ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),) + @$(RMDIR) $(INSTALL_LOCATION_LIB) +endif + +distclean: clean uninstall + +uninstall:: $(addprefix uninstall$(DIVIDER),$(BUILD_ARCHS)) + @$(MAKE) -f Makefile cleandirs + +uninstallDirs: + @$(RMDIR) rec.bak rec + @$(RMDIR) $(INSTALL_DBD) $(INSTALL_MAN) $(INSTALL_INCLUDE) $(INSTALL_DOC)\ + $(INSTALL_HTML) $(INSTALL_JAVA) $(INSTALL_TEMPLATES) + +tar: + @DIRNAME=$(notdir $(shell pwd)); \ + echo "TOP: Creating $$DIRNAME.tar file..."; \ + ls Makefile* | xargs tar vcf $$DIRNAME.tar; \ + if [ -f .current_rel_hist ]; then \ + ls .current_rel_hist | xargs tar vrf $$DIRNAME.tar ; \ + fi ;\ + if [ -f EPICS_BASE ]; then \ + ls EPICS_BASE | xargs tar vrf $$DIRNAME.tar ; \ + fi ;\ + for DIR in ${DIRS}; do \ + find $${DIR} -name CVS -prune -o ! -type d -print \ + | grep -v "/O\..*$$" | xargs tar vrf $$DIRNAME.tar; \ + done + +help: + @echo "Usage: gnumake [options] [target] ..." + @echo "Targets supported by all Makefiles:" + @echo " install - Installs executables in bin/ (default rule)" + @echo " build - Builds objects, using libraries from build_libs" + @echo " clean - Cleans objects. Clean removes the O. dirs" + @echo " in all except the O. level Makefile" + @echo " depends - Generates include dependencies" + @echo "\"Partial\" build targets supported by Makefiles:" + @echo " install. - Builds and installs only." + @echo " clean. - Cleans binaries in O. dirs only." + @echo " build. - Builds only." + @echo " depends. - Generates dependencies only." + @echo "Targets supported by top level Makefile:" + @echo " uninstall - Cleans directories created by the install." + @echo " tar - Create tar file " + @echo "Indiv. object targets are supported by O. level Makefile .e.g" + @echo " xxxRecord.o" + +.PHONY :: uninstall tar help cleandirs distclean uninstallDirs + diff --git a/config/Sample.Makefile.Host b/config/Sample.Makefile.Host new file mode 100644 index 000000000..4722b59a7 --- /dev/null +++ b/config/Sample.Makefile.Host @@ -0,0 +1,192 @@ +#************************************************************************* +# 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. +#************************************************************************* +# Makefile.Host for base/src/sample +# +# +# Sample Makefile.Host showing all possible entries +# that are allowed using RULES.Host. +# + +TOP = ../../.. +include $(TOP)/config/CONFIG_BASE +CMPLR = STRICT + +# Add-on CFLAGS that are needed by this Makefile. +# (If possible, all system specific flags should be +# defined in CONFIG.Host.$(ARCH_CLASS)) +# +# These CFLAGS rules also apply to these Makefile-variables: +# CXXFLAGS C++ flags +# LDFLAGS link flags +# +# This is used on all systems: +USR_CFLAGS = -DVAR=value -Ddefine_for_all_systems +# This is added to the above, but only for ARCH_CLASS=BSD: +USR_CFLAGS_BSD = -DVERSION='Berkeley enhanced' +# ..only for WIN32: +USR_CFLAGS_WIN32 = -DVERSION='WIN32 port' +# +# -nil- is special: +# if USR_CFLAGS_SYSV was undefined or empty, .._DEFAULT would have +# been used. +# To indicate +# "yes, there is a special USR_CFLAGS for SYSV, but it's empty" +# you have to set it to -nil-: +USR_CFLAGS_SYSV = -nil- +# .. for all other arch classes: +USR_CFLAGS_DEFAULT = -DVERSION='generic Unix' + +# CFLAGS that are only used to compile a_file.c or a_file.cc: +# +a_file_CFLAGS = -DIN_A_FILE +a_file_CFLAGS_WIN32 = -DVERSION='WIN32 port' + +# --------------------------------------------------------- +# general rule for all .c .cc .h .hh files and scripts: +# +# In here you supply just the filename without '../' etc. +# While building in an O.xxx subdir, the +# sources are extracted from either the +# '..' +# dir or - if it exists - the dir +# '../$(ARCH_CLASS)' +# is preferred. +# --------------------------------------------------------- + + +# includes to install from this Makefile +# +# again: if INC_$(ARCH_CLASS) is defined, it is added to INC, +# otherwise INC_DEFAULT (if defined) is added: +# +INC_DEFAULT = for_all_but_WIN32_or_hp700.h +INC_WIN32 = only_for_WIN32.h +INC_hp700 = -nil- # hp700 uses no special include +INC = file.h + +# -------------------------------------------------------------------- +# defining a library +# -------------------------------------------------------------------- +# +# Contents of a library are specified via LIBSRCS. +# From this the platform specific object names (.o, .obj, ...) +# are derived automatically. +# +# Platform specific objects: +# use LIBOBJS_$(ARCH_CLASS) or .._DEFAULT +# +# Platform specific files can also be put in +# separate os/ARCH_CLASS directories! +# +# For almost every file the seach order is: +# ./os/ARCH_CLASS +# ./os/generic +# . +# So usually only LIBSRCS should be sufficient! +# +LIBSRCS = file_for_lib.c another_file.cc +LIBSRCS_DEFAULT = posix.c +LIBSRCS_WIN32 = win32_special.c +LIBSRCS_BSD = -nil- + +# Library to build: +# lib$(LIBRARY).a or ..dll/..exp/..lib +# +# Currently you can build only one (1) lib per Makefile.Host! +# +LIBRARY=libname + +# if SHARED_LIBRARIES is YES then shared and archive libraries will +# both be built +#SHARED_LIBRARIES = YES +# +# Library version +SHRLIB_VERSION = +# On WIN32 results in /version:$(SHRLIB_VERSION) link option +# On Unix type hosts .$(SHRLIB_VERSION) is appended to library name + +# -------------------------------------------------------------------- +# defining products (executable programs) +# -------------------------------------------------------------------- +# +# if SRCS is undefined, it defaults to $(PROD).c +SRCS=a.c b.c c.c + +# SRCS that are only used for PROD a_file +# +a_file_SRCS = aa.c bb.c + +# EPICS libs needed to link PROD, TESTPROD and sharable library +# +# note that DLL_LIBS (the libraries needed to link a shareable +# library) is created by default from the PROD/SYS libraries specified +# below minus the name of the sharable library (LIBRARY) +# +# +# for all systems: +PROD_LIBS = Com Ca +# for most systems: +PROD_LIBS_DEFAULT = mathlib +PROD_LIBS_WIN32 = -nil- + +# system libs needed to link PROD, TESTPROD and sharable library +# +# for all systems: +SYS_PROD_LIBS = m +# for most systems: +SYS_PROD_LIBS_DEFAULT = foolib +SYS_PROD_LIBS_WIN32 = -nil- + +# other libs needed to link PROD, TESTPROD and sharable library +# +# for all systems: +USR_LIBS = Xm Xt X11 +Xm_DIR = $(MOTIF_LIB) +Xt_DIR = $(X11_LIB) +X11_DIR = $(X11_LIB) + +# for most systems: +USR_LIBS_DEFAULT = foolib +USR_LIBS_WIN32 = -nil- +foolib_DIR = $(FOO_LIB) + +# Product, +# may be caRepeater.o -> caRepeater +# or caRepeater.obj -> caRepeater.exe +PROD = prod +PROD_DEFAULT = product_for_rest +PROD_WIN32 = product_only_for_WIN32 +PROD_BSD = product_only_for_BSD +PROD_SYSV = product_only_for_SYSV + +# Product version +PROD_VERSION = +# On WIN32 results in /version:$(SHRLIB_VERSION) link option +# On Unix type hosts PROD_VERSION) is ignored + +# Scripts to install +# +# If there is both ../$(SCRIPT) and ../$(ARCH_CLASS)/$(SCRIPT), +# the latter, system specific version will be installed! +# +SCRIPTS_DEFAULT = script_for_rest +SCRIPTS_WIN32 = script_only_for_WIN32 +SCRIPTS_BSD = script_only_for_BSD +SCRIPTS = script + +# if you want to build products locally without installing: +# TESTPROD = test + +# put all definitions before the following include line +# put all rules after the following include line + +include $(TOP)/config/RULES.Host + +# EOF Makefile.Host diff --git a/config/tools/cp.pl b/config/tools/cp.pl new file mode 100755 index 000000000..6562d92f7 --- /dev/null +++ b/config/tools/cp.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# UNIX-cp in Perl + +use File::Copy; +use File::Basename; + +sub Usage +{ + my ($txt) = @_; + + print "Usage:\n"; + print "\tcp file1 file2\n"; + print "\tcp file [ file2 file3 ...] directory\n"; + print "\nError: $txt\n" if $txt; + + exit 2; +} + +# need at least two args: ARGV[0] and ARGV[1] +Usage("need more args") if $#ARGV < 1; + +$target=$ARGV[$#ARGV]; +@sources=@ARGV[0..$#ARGV-1]; + +if (-d $target) +{ + foreach $file ( @sources ) + { + $base=basename($file); + copy ($file, "$target/$base"); + } +} +else +{ + Usage("Cannot copy more than one source into a single target") + if ($#sources != 0); + copy ($sources[0], $target); +} + +# EOF cp.pl diff --git a/config/tools/findBase.pl b/config/tools/findBase.pl new file mode 100755 index 000000000..796dde3e1 --- /dev/null +++ b/config/tools/findBase.pl @@ -0,0 +1,22 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* + +use Cwd; + +$dir=cwd(); +# make sure $dir ends with '/' +# +$dir="$dir/" unless ($dir =~ m'/$'); + +if ($dir =~ m'(.*(/|\\)base)(/|\\)') +{ + print "$1"; +} diff --git a/config/tools/installEpics b/config/tools/installEpics new file mode 100755 index 000000000..edd4f3ccc --- /dev/null +++ b/config/tools/installEpics @@ -0,0 +1,106 @@ +#!/bin/sh +#************************************************************************* +# 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. +#************************************************************************* + +# InstallEpics +# +# InstallEpics is used within makefiles to copy new versions of +# files into a destination directory. +# +########################################################## +TOOL=`basename $0` +MODE=755 +CREATE_DIR=0 +USAGE="Usage: + $TOOL [ -m mode ] file ... directory + + -m mode Set the mode for the installed file (0755 by default) + file Name of file + directory Destination directory +" +# get command line options +while getopts m:g:o:csd OPT +do + + case $OPT in + m) MODE=$OPTARG;; + g | o) echo "$USAGE"; echo "$i $OPTARG not implemented";; + c | s) echo "$USAGE"; echo "$i not implemented";; + d) CREATE_DIR=1;; + --) break;; + esac +done +shift `expr $OPTIND - 1` + +# at least two args required +if [ $# -lt 2 ] +then + echo "Nothing to install" + exit +fi + +INSTALL_DIR= +FILELIST= +for i +do + FILELIST="${FILELIST} ${INSTALL_DIR}"; INSTALL_DIR=$i; shift; +done + +if [ ! -d "${INSTALL_DIR}" ] ;then + if [ ${CREATE_DIR} != "0" ] ;then + OLDIFS=${IFS} + IFS=/ + DIRNAME= + for DIR in ${INSTALL_DIR} + do + if [ "${DIR}" = "." ] || [ "${DIR}" = ".." ] ;then + if [ "${DIRNAME}" = "" ] ;then + DIRNAME=${DIR} + else + DIRNAME=${DIRNAME}/${DIR} + fi + else + DIRNAME=${DIRNAME}/${DIR} + if [ ! -d "${DIRNAME}" ] ;then + mkdir "${DIRNAME}" + fi + fi + done + IFS=${OLDIFS} + else + echo "$USAGE\n Can't find directory '${INSTALL_DIR}'" + exit 1 + fi +fi + +for FILE in ${FILELIST} +do + if [ ! -f ${FILE} ] ;then + echo "$USAGE\n Can't find file '${FILE}'" + exit 1 + fi + + TEST= + FILEBASENAME=`basename ${FILE}` + if [ -f ${INSTALL_DIR}/${FILEBASENAME} ] ; then + #Is ${INSTALL_DIR}/${FILEBASENAME} link timestamp newer than ${FILE} + TEST=`find ${INSTALL_DIR} -name "${FILEBASENAME}" -newer ${FILE} -print` + fi + if [ "${TEST}x" = "x" ] ; then + #echo "Installing ${FILEBASENAME}" + rm -f ${INSTALL_DIR}/${FILEBASENAME} + cp -p ${FILE} ${INSTALL_DIR}/${FILEBASENAME} + chmod ${MODE} ${INSTALL_DIR}/${FILEBASENAME} + else + echo "${INSTALL_DIR}/${FILEBASENAME} is up to date" + fi +done + +exit 0 diff --git a/config/tools/installEpics.pl b/config/tools/installEpics.pl new file mode 100755 index 000000000..248e662f7 --- /dev/null +++ b/config/tools/installEpics.pl @@ -0,0 +1,118 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# InstallEpics.pl +# +# InstallEpics is used within makefiles to copy new versions of +# files into a destination directory. +# Based on installEpics shell script. +# +# 2-4-97 -kuk- +# +########################################################## + +use Getopt::Std; +use File::Path; +use File::Copy; + +$tool=$0; +$tool=~ s'.*[/\\].+''; # basename +$mode=0755; + +# get command line options +getopt "m"; +$mode = oct ($opt_m) if ($opt_m); + +# Complain about obsolete options: +Usage("unknown option given") if ($opt_g or $opt_o or $opt_c or $opt_s); + +$num_files = $#ARGV; +# at least two args required +Usage ("Nothing to install") if ($num_files < 1); + +# split args in file1 ... fileN target_dir: +@files=@ARGV[0..$num_files-1]; +$install_dir=$ARGV[$num_files]; +$install_dir =~ s[\\][/]g; # maybe fix DOS-style path +$install_dir =~ s[/$][]; # remove trailing '/' +$install_dir =~ s[//][/]g; # replace '//' by '/' + +# Do we have to create the directory? +unless ( (-d $install_dir) || (-l $install_dir) ) +{ + # Create dir only if -d option given + Usage ("$install_dir does not exist") unless ($opt_d); + + # Create all the subdirs that lead to $install_dir + mkpath ($install_dir, 1, 0777); +} + +foreach $source ( @files ) +{ + Usage ("Can't find file '$source'") unless -f $source; + + $basename=$source; + $basename=~s'.*[/\\]''; + $target = "$install_dir/$basename"; + + # The Win32 filesystem seems to be 'slow', + # i.e. $target may look like 'up to date' + # unless you wait an hour. + # -> skip this test on WIN32 ? + #if (-f $target and $^O ne "MSWin32") + if (-f $target) + { + if (-M $target < -M $source and + -C $target < -C $source) + { + print "$target is up to date\n"; + next; + } + else + { + # remove old target, make sure it is deletable: + chmod 0777, $target; + unlink $target; + } + } + + # print "Installing $source into $install_dir\n"; + copy ($source, $target) or die "Copy failed"; + + # chmod 0555 DOES work on WIN32, but: + # Another chmod 0777 to make it write- and deletable + # will then fail. + # -> you have to use Win32::SetFileAttributes + # to get rid of those files from within Perl. + # Because the chmod is not really needed on WIN32, + # just skip it! + chmod $mode, $target unless ($^O eq "MSWin32"); +} + +sub Usage +{ + my ($txt) = @_; + + print "Usage:\n"; + print "\t$tool [ -m mode ] file ... directory\n"; + print "\n"; + print "\t-d Create non-existing directories\n"; + print "\t-m mode Set the mode for the installed file"; + print " (0755 by default)\n"; + print "\tfile Name of file\n"; + print "\tdirectory Destination directory\n"; + + print "$txt\n" if $txt; + + exit 2; +} + +# EOF installEpics.pl diff --git a/config/tools/makeMakefile.pl b/config/tools/makeMakefile.pl new file mode 100755 index 000000000..9c0b2fe93 --- /dev/null +++ b/config/tools/makeMakefile.pl @@ -0,0 +1,40 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# makeMakefile.pl +# +# called from RULES_ARCHS +# +# +# Usage: perl makeMakefile.pl O.*-dir Makefile-Type + +$dir = $ARGV[0]; +$type= $ARGV[1]; +$makefile="$dir/Makefile"; + +if ($dir =~ m'O.(.+)') +{ + $t_a = $1; +} +else +{ + die "Cannot extract T_A from $dir"; +} + +mkdir ($dir, 0777) unless -d $dir; + +open OUT, "> $makefile" or die "Cannot create $makefile"; +print OUT "T_A=$t_a\n"; +print OUT "BUILD_TYPE=$type\n"; +print OUT "include ../Makefile.$type\n"; +close OUT; + +# EOF makeMakefile.pl diff --git a/config/tools/mkdir.pl b/config/tools/mkdir.pl new file mode 100755 index 000000000..7d268ae07 --- /dev/null +++ b/config/tools/mkdir.pl @@ -0,0 +1,32 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# UNIX-mkdir in Perl +# +# -p option generates full path to given dir + +use File::Path; +use Getopt::Std; +getopt ""; + +foreach $dir ( @ARGV ) +{ + if ($opt_p) + { + mkpath ($dir) or die "Cannot make directory $dir"; + } + else + { + mkdir ($dir, 0777) or die "Cannot make directory $dir"; + } +} + +# EOF mkdir.pl diff --git a/config/tools/munch.pl b/config/tools/munch.pl new file mode 100755 index 000000000..031b98c54 --- /dev/null +++ b/config/tools/munch.pl @@ -0,0 +1,66 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # makeConfigAppInclude.pl +#************************************************************************* +# 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. +#************************************************************************* + +# Creates a ctdt.c file of c++ static constructors and destructors. +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +@ctorlist = (); +@dtorlist = (); + +while ($line = ) +{ + next if ($line =~ /__?GLOBAL_.F.+/); + next if ($line =~ /__?GLOBAL_.I._GLOBAL_.D.+/); + if ($line =~ /__?GLOBAL_.D.+/) { + ($adr,$type,$name) = split ' ',$line,3; + chop $name; + $name =~ s/^__/_/; + next if ( $name =~ /^__?GLOBAL_.D.*.\.cpp/ ); + next if ( $name =~ /^__?GLOBAL_.D.\.\./ ); + @dtorlist = (@dtorlist,$name); + }; + if ($line =~ /__?GLOBAL_.I.+/) { + ($adr,$type,$name) = split ' ',$line,3; + chop $name; + $name =~ s/^__/_/; + next if ( $name =~ /^__?GLOBAL_.I.*.\.cpp/ ); + next if ( $name =~ /^__?GLOBAL_.I.\.\./ ); + @ctorlist = (@ctorlist,$name); + }; +} + +foreach $ctor (@ctorlist) +{ + printf "void %s();\n",$ctor; +} + +print "extern void (*_ctors[])();\n"; +print "void (*_ctors[])() = {\n"; +foreach $ctor (@ctorlist) +{ + printf " %s,\n",$ctor; +} +print " 0};\n"; + + +foreach $dtor (@dtorlist) +{ + printf "void %s();\n",$dtor; +} + +print "extern void (*_ctors[])();\n"; +print "void (*_dtors[])() = {\n"; +foreach $dtor (@dtorlist) +{ + printf " %s,\n",$dtor; +} +print " 0};\n"; diff --git a/config/tools/mv.pl b/config/tools/mv.pl new file mode 100755 index 000000000..2ccf5105d --- /dev/null +++ b/config/tools/mv.pl @@ -0,0 +1,78 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# UNIX-mv in Perl + +use File::Copy; + +sub Usage +{ + my ($txt) = @_; + + print "Usage:\n"; + print "\tmv oldname newname\n"; + print "\tmv file [ file2 file3 ...] directory\n"; + print "\nError: $txt\n" if $txt; + + exit 2; +} + +sub Move +{ + my ($src, $dest) = @_; + + print "Move($src, $dest)\n"; + + copy ($src, $dest) or die "Cannot copy $src to $dest"; + unlink ($src) or die "Cannot remove $src"; +} + +# return filename.ext from Drive:/path/a/b/c/filename.ext +sub Filename +{ + my ($file) = @_; + + $file =~ s'.*[/\\]''; + + return $file; +} + +# need at least two args: ARGV[0] and ARGV[1] +Usage("need more args") if $#ARGV < 1; + +$target=$ARGV[$#ARGV]; +@sources=@ARGV[0..$#ARGV-1]; + +print "move @sources into $target\n"; + +# If target is (already existent) directory, +# move files into it: +if (-d $target) +{ + foreach $file ( @sources ) + { + Move ($file, "$target/" . Filename($file)); + } + exit 0; +} + +# Otherwise the target is a filename. +# Now 'mv' may be either a 'move' or a 'rename', +# in any case it requires exactly two args: old and new name. + +Usage("Need exactly one source") if $#sources != 0; +$source = @sources[0]; + +# Move only if a simple rename +# fails (e.g. across file systems): +Move ($source, $target) unless (rename $source, $target); + +# EOF mv.pl diff --git a/config/tools/rm.pl b/config/tools/rm.pl new file mode 100755 index 000000000..f5fa70ef7 --- /dev/null +++ b/config/tools/rm.pl @@ -0,0 +1,45 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# UNIX-rm in Perl + +use File::Path; +use File::Find; +use Getopt::Std; + +getopt ""; + +foreach $arg ( @ARGV ) +{ + next unless -e $arg; + + if (-d $arg) + { + if ($opt_r and $opt_f) + { + rmtree $arg; + } + else + { + rmdir ($arg) or die "Cannot delete $arg"; + } + if (-d $arg) + { + die "Failed to delete $arg"; + } + } + else + { + unlink ($arg) or die "Cannot delete $arg"; + } +} + +# EOF rm.pl diff --git a/config/tools/useManifestTool.pl b/config/tools/useManifestTool.pl new file mode 100755 index 000000000..8607e9c51 --- /dev/null +++ b/config/tools/useManifestTool.pl @@ -0,0 +1,31 @@ +eval 'exec perl -S -w $0 ${1+"$@"}' # -*- Mode: perl -*- + if 0; + +# +# Use MS Visual C++ compiler version number to determine if +# we want to use the Manifest Tool (status=1) or not (status=0) +# +# VC compiler versions >= 14.00 will have status=1 +# VC compiler versions 10.00 - 13.10 will have status=0 +# EPICS builds with older VC compilers is not supported +# + +my $versionString=`cl 2>&1`; + +if ($versionString =~ m/Version 14./) { + $status=1; +} elsif ($versionString =~ m/Version 13.10/){ + $status=0; +} elsif ($versionString =~ m/Version 13.0/){ + $status=0; +} elsif ($versionString =~ m/Version 12./){ + $status=0; +} elsif ($versionString =~ m/Version 11./){ + $status=0; +} elsif ($versionString =~ m/Version 10./){ + $status=0; +} else { + $status=1; +} +print "$status\n"; +exit; diff --git a/configure/CONFIG b/configure/CONFIG new file mode 100644 index 000000000..b62edb67a --- /dev/null +++ b/configure/CONFIG @@ -0,0 +1,140 @@ +#************************************************************************* +# 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-20101026195642-3p5qxsy19a8skgud +# +# The developer may edit this file. +# assume T_A is the host arch if not specified + +# +# Common build definitions +# + +ifneq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),) +EPICS_BASE = $(INSTALL_LOCATION) +CONFIG = $(TOP)/configure +BASE_TOP=YES +else +CONFIG ?= $(EPICS_BASE)/configure +endif + +# Provide a default if the user hasn't set EPICS_HOST_ARCH +ifeq ($(origin EPICS_HOST_ARCH), undefined) +# NB: Must use a simply expanded variable here for performance: +EPICS_HOST_ARCH := $(shell $(CONFIG)/../startup/EpicsHostArch.pl) +endif +# + +-include $(CONFIG)/os/CONFIG_COMPAT + +-include $(CONFIG)/RELEASE +-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH) +-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).Common +ifdef T_A +-include $(CONFIG)/RELEASE.Common.$(T_A) +-include $(CONFIG)/RELEASE.$(EPICS_HOST_ARCH).$(T_A) +endif + +include $(CONFIG)/CONFIG_COMMON +include $(CONFIG)/CONFIG_FILE_TYPE + +# Base-specific build options +# +include $(CONFIG)/CONFIG_BASE + +# Site-specific build options +# +include $(CONFIG)/CONFIG_SITE + +# Version numbering +# +include $(CONFIG)/CONFIG_BASE_VERSION + +# Host architecture specific definitions +# +include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).Common +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).Common + +ifdef T_A + +# Cross compile specific definitions +# +ifneq ($(EPICS_HOST_ARCH),$(T_A)) +include $(CONFIG)/CONFIG.CrossCommon +endif + +# Target architecture specific definitions +# +-include $(CONFIG)/os/CONFIG.Common.$(T_A) + +# Host-Target architecture specific definitions +# +-include $(CONFIG)/os/CONFIG.$(EPICS_HOST_ARCH).$(T_A) + +# RELEASE file specific definitions +# +ifneq ($(CONFIG),$(TOP)/configure) +-include $(CONFIG)/CONFIG_APP_INCLUDE +endif + +# Site specific target and host-target definitions +# +-include $(CONFIG)/os/CONFIG_SITE.Common.$(T_A) +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) + +endif + +# Include /cfg/CONFIG* definitions from tops defined in RELEASE* files +# +ifneq ($(CONFIG),$(TOP)/configure) +RELEASE_TOPS := $(shell $(CONVERTRELEASE) -T $(TOP) releaseTops) +RELEASE_CFG_CONFIGS = $(foreach top, $(RELEASE_TOPS), $(wildcard $($(top))/cfg/CONFIG*)) +ifneq ($(RELEASE_CFG_CONFIGS),) +include $(RELEASE_CFG_CONFIGS) +endif +endif + +# Include $(INSTALL_CFG)/CONFIG* definitions +# +ifndef T_A +TOP_CFG_CONFIGS = $(wildcard $(INSTALL_CFG)/CONFIG*) +ifneq ($(TOP_CFG_CONFIGS),) +include $(TOP_CFG_CONFIGS) +endif +endif + +# User specific definitions +# +-include $(HOME)/configure/CONFIG_USER +-include $(HOME)/configure/CONFIG_USER.$(EPICS_HOST_ARCH) +ifdef T_A +-include $(HOME)/configure/CONFIG_USER.Common.$(T_A) +-include $(HOME)/configure/CONFIG_USER.$(EPICS_HOST_ARCH).$(T_A) +endif + +# All options +# may be overridden here. +# +# EXAMPLES +# -------- +# Build client objects statically ? must be either YES or NO +#STATIC_BUILD=NO +# Host build optimization, must be either YES or NO +#HOST_OPT=YES +# Cross build optimization, must be either YES or NO +#CROSS_OPT=YES +# Generate Verbose Compiler Warnings for host build, must be either YES or NO +#HOST_WARN=YES +# Generate Verbose Compiler Warnings for cross compile builds, must be either YES or NO +#CROSS_WARN=YES +#etc. + +#CROSS_COMPILER_TARGET_ARCHS=vxWorks-68040 + diff --git a/configure/CONFIG.CrossCommon b/configure/CONFIG.CrossCommon new file mode 100644 index 000000000..167ba490f --- /dev/null +++ b/configure/CONFIG.CrossCommon @@ -0,0 +1,32 @@ +#************************************************************************* +# 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. +#************************************************************************* +# Cross compiler default definitions + +# Build class: either HOST or CROSS +# Used to determine OPT and WARN compiler flags +BUILD_CLASS = CROSS + +# Cross build: either defined or not +# Used in os/CONFIG.Common. files +# ifdef CROSS looks better than ifeq ($(BUILD_CLASS),CROSS) +CROSS = YES + +GNU_TARGET_INCLUDE_DIR = $(GNU_TARGET:%= $(GNU_DIR)/%/include) +GNU_TARGET_LIB_DIR = $(GNU_TARGET:%= $(GNU_DIR)/%/lib) + +CROSS_INCLUDES = $(addprefix -I,$(GNU_TARGET_INCLUDE_DIR)) +CROSS_LDFLAGS = $(addprefix -L,$(GNU_TARGET_LIB_DIR)) + +CMPLR_PREFIX_CROSS=$(addsuffix -,$(GNU_TARGET)) +CMPLR_PREFIX=$(CMPLR_PREFIX_$(BUILD_CLASS)) + +# All cross builds use the gnu compiler +include $(CONFIG)/CONFIG.gnuCommon + diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon new file mode 100644 index 000000000..f0548024e --- /dev/null +++ b/configure/CONFIG.gnuCommon @@ -0,0 +1,57 @@ +#************************************************************************* +# 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. +#************************************************************************* +#-------------------------------------------------- +# GNU compiler defaults + +GNU = YES + +GNU_BIN = $(GNU_DIR)/bin +GNU_LIB = $(GNU_DIR)/lib + +CC = $(GNU_BIN)/$(CMPLR_PREFIX)gcc$(CMPLR_SUFFIX) +CCC = $(GNU_BIN)/$(CMPLR_PREFIX)g++$(CMPLR_SUFFIX) +AR = $(GNU_BIN)/$(CMPLR_PREFIX)ar$(CMPLR_SUFFIX) -rc +LD = $(GNU_BIN)/$(CMPLR_PREFIX)ld$(CMPLR_SUFFIX) -r +CPP = $(CC) -x c -E +RANLIB = $(GNU_BIN)/$(CMPLR_PREFIX)ranlib$(CMPLR_SUFFIX) + +PROF_CFLAGS_YES = -p +GPROF_CFLAGS_YES = -pg +CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF)) +WARN_CFLAGS_YES = -Wall +WARN_CFLAGS_NO = -w +OPT_CFLAGS_YES = -O3 +OPT_CFLAGS_NO = -g + +PROF_CXXFLAGS_YES = -p +GPROF_CXXFLAGS_YES = -pg +CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) +WARN_CXXFLAGS_YES = -Wall +WARN_CXXFLAGS_NO = -w +OPT_CXXFLAGS_YES = -O3 +OPT_CXXFLAGS_NO = -g + +CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) + +PIPE_CFLAGS_YES_YES = -pipe +PIPE_CFLAGS = $(PIPE_CFLAGS_$(GCC_PIPE)_$(GNU)) +PIPE_CXXFLAGS = $(PIPE_CFLAGS) + +STATIC_LDFLAGS_YES = -static +STATIC_LDFLAGS_NO = + +SHRLIB_CFLAGS = -fPIC +SHRLIB_LDFLAGS = -shared -fPIC +LOADABLE_SHRLIB_LDFLAGS = -shared -fPIC + +GNU_LDLIBS_YES = -lgcc + +# Use GNU compiler flags to generate header dependancies files +HDEPENDS_METHOD = CFLAGS diff --git a/configure/CONFIG_ADDONS b/configure/CONFIG_ADDONS new file mode 100644 index 000000000..4a8ecfb6b --- /dev/null +++ b/configure/CONFIG_ADDONS @@ -0,0 +1,560 @@ +#************************************************************************* +# 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 is 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 +# +# check for add-on CFLAGS and CXXFLAGS +# +# Rules: +# 1) USR_CFLAGS is used +# 2) if there is a special USR_CFLAGS_$(OS_CLASS), it's +# appended to 1) +# 3) if there is no special defined, but a generic USR_CFLAGS_DEFAULT, +# this one is appended +# 4) if you have the special case that your USR_CFLAGS_$(OS_CLASS) is +# empty but you don't want 3), you have to define it as '-nil-', e.g.: +# USR_CFLAGS = +# USR_CFLAGS_WIN = -nil- +# USR_CFLAGS_DEFAULT = +# +# These rules apply to these Makefile-variables: +# USR_CFLAGS C flags +# USR_CXXFLAGS C++ flags +# USR_CPPFLAGS c preprocesser flags +# INC include-files to install +# SRCS source files for building libraries and prods +# USR_SRCS source files for building libraries and prods +# PROD_SRCS source files for building prods +# LIB_SRCS source files for building libraries +# LIBSRCS source files for building libraries (deprecated) +# PROD_OBJS object files for building prods +# LIB_OBJS object files for building libraries +# USR_LIBS libs needed by PROD and TESTPROD and LIBRARY +# PROD_LIBS libs needed by PROD and TESTPROD +# LIB_LIBS libs needed by shared LIBRARY +# SHRLIB_LIBS libs needed by shared LIBRARY +# USR_OBJLIBS R3.13 vxWorks object libs needed building libraries and prods +# PROD_OBJLIBS R3.13 vxWorks object libs needed for building prods +# LIB_OBJLIBS R3.13 vxWorks object libs needed for building libraries +# USR_SYS_LIBS system libs needed building libraries and prods +# PROD_SYS_LIBS system libs needed for building prods +# LIB_SYS_LIBS system libs needed for building libraries +# USR_LDFLAGS ld flags for building libraries and prods +# PROD_LDFLAGS ld flags for building prods +# LIB_LDFLAGS ld flags for building libraries +# PROD products to build and install +# PROD_HOST products to build and install +# PROD_IOC products to build and install +# TESTPROD products to build +# TESTPROD_HOST products to build +# TESTPROD_IOC products to build +# LIBRARY products to build and install +# LIBRARY_HOST products to build and install +# LIBRARY_IOC products to build and install +# LOADABLE_LIBRARY module to build and install +# LOADABLE_LIBRARY_HOST module to build and install +# SCRIPTS scripts and install +# SCRIPTS_HOST host system scripts to install +# SCRIPTS_IOC ioc system scripts to install +# TESTSCRIPTS scripts +# TESTSCRIPTS_HOST host system scripts +# TESTSCRIPTS_IOC ioc system scripts +# OBJS object files to build and install +# OBJS_HOST host system object files to build and install +# OBJS_IOC ioc system object files to build and install +# USR_INCLUDES include directories +# BIN_INSTALLS binaries to install +# LIB_INSTALLS library binaries to install +# RCS win32 resource files for building libraries and prods +# PROD_RCS win32 resource files for building prods +# LIB_RCS win32 resource files for building libraries +# +# Remark: +# If you define INC, e.g. INC = getopt.h, the source +# (getopt.h) must be in the source directory (..) and/or +# in one or more ../os/ directories. +# +# Additional target architecture, T_A, Rules for USR_CFLAGS, USR_CXXFLAGS, +# and USR_CPPFLAGS which are applied before the above os_class Rules: +# 1) USR_CFLAGS_$(OS_CLASS) is used +# 2) if there is a special $(USR_CFLAGS_$(T_A)), it's +# appended to 1) +# 3) if there is no special defined, but a generic USR_CFLAGS_$(OS_CLASS)_DEFAULT, +# this one is appended +# 4) if you have the special case that your $(USR_CFLAGS_$(T_A)) is +# empty but you don't want 3), you have to define it as '-nil-', e.g.: +# USR_CFLAGS_vxWorks = +# USR_CFLAGS_vxWorks-68040 = -nil- +# USR_CFLAGS_vxWorks_DEFAULT = +# +# + +ifneq ($(strip $(USR_CFLAGS_$(T_A))),) +USR_CFLAGS_$(OS_CLASS)+=$(subst -nil-,,$(USR_CFLAGS_$(T_A))) +else +ifdef USR_CFLAGS_$(OS_CLASS)_DEFAULT +USR_CFLAGS_$(OS_CLASS)+=$(USR_CFLAGS_$(OS_CLASS)_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_CFLAGS_$(OS_CLASS))),) +USR_CFLAGS+=$(subst -nil-,,$(USR_CFLAGS_$(OS_CLASS))) +else +ifdef USR_CFLAGS_DEFAULT +USR_CFLAGS+=$(USR_CFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_INCLUDES_$(OS_CLASS))),) +USR_INCLUDES+=$(subst -nil-,,$(USR_INCLUDES_$(OS_CLASS))) +else +ifdef USR_INCLUDES_DEFAULT +USR_INCLUDES+=$(USR_INCLUDES_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_CXXFLAGS_$(T_A))),) +USR_CXXFLAGS_$(OS_CLASS)+=$(subst -nil-,,$(USR_CXXFLAGS_$(T_A))) +else +ifdef USR_CXXFLAGS_$(OS_CLASS)_DEFAULT +USR_CXXFLAGS_$(OS_CLASS)+=$(USR_CXXFLAGS_$(OS_CLASS)_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_CXXFLAGS_$(OS_CLASS))),) +USR_CXXFLAGS+=$(subst -nil-,,$(USR_CXXFLAGS_$(OS_CLASS))) +else +ifdef USR_CXXFLAGS_DEFAULT +USR_CXXFLAGS+=$(USR_CXXFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_CPPFLAGS_$(T_A))),) +USR_CPPFLAGS_$(OS_CLASS)+=$(subst -nil-,,$(USR_CPPFLAGS_$(T_A))) +else +ifdef USR_CPPFLAGS_$(OS_CLASS)_DEFAULT +USR_CPPFLAGS_$(OS_CLASS)+=$(USR_CPPFLAGS_$(OS_CLASS)_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_CPPFLAGS_$(OS_CLASS))),) +USR_CPPFLAGS+=$(subst -nil-,,$(USR_CPPFLAGS_$(OS_CLASS))) +else +ifdef USR_CPPFLAGS_DEFAULT +USR_CPPFLAGS+=$(USR_CPPFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_LDFLAGS_$(OS_CLASS))),) +USR_LDFLAGS+=$(subst -nil-,,$(USR_LDFLAGS_$(OS_CLASS))) +else +ifdef USR_LDFLAGS_DEFAULT +USR_LDFLAGS+=$(USR_LDFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_LDFLAGS_$(OS_CLASS))),) +PROD_LDFLAGS+=$(subst -nil-,,$(PROD_LDFLAGS_$(OS_CLASS))) +else +ifdef PROD_LDFLAGS_DEFAULT +PROD_LDFLAGS+=$(PROD_LDFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_LDFLAGS_$(OS_CLASS))),) +LIB_LDFLAGS+=$(subst -nil-,,$(LIB_LDFLAGS_$(OS_CLASS))) +else +ifdef LIB_LDFLAGS_DEFAULT +LIB_LDFLAGS+=$(LIB_LDFLAGS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIBSRCS_$(OS_CLASS))),) +LIBSRCS += $(subst -nil-,,$(LIBSRCS_$(OS_CLASS))) +else +ifdef LIBSRCS_DEFAULT +LIBSRCS+=$(LIBSRCS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_SRCS_$(OS_CLASS))),) +LIB_SRCS += $(subst -nil-,,$(LIB_SRCS_$(OS_CLASS))) +else +ifdef LIB_SRCS_DEFAULT +LIB_SRCS+=$(LIB_SRCS_DEFAULT) +endif +endif + +ifneq ($(strip $(SRCS_$(OS_CLASS))),) +SRCS += $(subst -nil-,,$(SRCS_$(OS_CLASS))) +else +ifdef SRCS_DEFAULT +SRCS+=$(SRCS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_SRCS_$(OS_CLASS))),) +USR_SRCS += $(subst -nil-,,$(USR_SRCS_$(OS_CLASS))) +else +ifdef USR_SRCS_DEFAULT +USR_SRCS+=$(USR_SRCS_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_SRCS_$(OS_CLASS))),) +PROD_SRCS += $(subst -nil-,,$(PROD_SRCS_$(OS_CLASS))) +else +ifdef PROD_SRCS_DEFAULT +PROD_SRCS+=$(PROD_SRCS_DEFAULT) +endif +endif + +ifneq ($(strip $(BIN_INSTALLS_$(OS_CLASS))),) +BIN_INSTALLS+=$(subst -nil-,,$(BIN_INSTALLS_$(OS_CLASS))) +else +ifdef BIN_INSTALLS_DEFAULT +BIN_INSTALLS+=$(BIN_INSTALLS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_INSTALLS_$(OS_CLASS))),) +LIB_INSTALLS+=$(subst -nil-,,$(LIB_INSTALLS_$(OS_CLASS))) +else +ifdef LIB_INSTALLS_DEFAULT +LIB_INSTALLS+=$(LIB_INSTALLS_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_OBJS_$(OS_CLASS))),) +PROD_OBJS+=$(subst -nil-,,$(PROD_OBJS_$(OS_CLASS))) +else +ifneq (,$(strip $(PROD_OBJS_DEFAULT))) +PROD_OBJS+=$(PROD_OBJS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_OBJS_$(OS_CLASS))),) +USR_OBJS+=$(subst -nil-,,$(USR_OBJS_$(OS_CLASS))) +else +ifneq (,$(strip $(USR_OBJS_DEFAULT))) +USR_OBJS+=$(USR_OBJS_DEFAULT) +endif +endif + +ifneq ($(strip $(OBJS_$(OS_CLASS))),) +OBJS+=$(subst -nil-,,$(OBJS_$(OS_CLASS))) +else +ifneq (,$(strip $(OBJS_DEFAULT))) +OBJS+=$(OBJS_DEFAULT) +endif +endif + +ifneq ($(strip $(OBJS_IOC_$(OS_CLASS))),) +OBJS_IOC+=$(subst -nil-,,$(OBJS_IOC_$(OS_CLASS))) +else +ifneq (,$(strip $(OBJS_IOC_DEFAULT))) +OBJS_IOC+=$(OBJS_IOC_DEFAULT) +endif +endif + +ifneq ($(strip $(OBJS_HOST_$(OS_CLASS))),) +OBJS_HOST+=$(subst -nil-,,$(OBJS_HOST_$(OS_CLASS))) +else +ifneq (,$(strip $(OBJS_HOST_DEFAULT))) +OBJS_HOST+=$(OBJS_HOST_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_OBJLIBS_$(OS_CLASS))),) +PROD_OBJLIBS+=$(subst -nil-,,$(PROD_OBJLIBS_$(OS_CLASS))) +else +ifdef PROD_OBJLIBS_DEFAULT +PROD_OBJLIBS+=$(PROD_OBJLIBS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_OBJLIBS_$(OS_CLASS))),) +LIB_OBJLIBS+=$(subst -nil-,,$(LIB_OBJLIBS_$(OS_CLASS))) +else +ifdef LIB_OBJLIBS_DEFAULT +LIB_OBJLIBS+=$(LIB_OBJLIBS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_OBJLIBS_$(OS_CLASS))),) +USR_OBJLIBS+=$(subst -nil-,,$(USR_OBJLIBS_$(OS_CLASS))) +else +ifdef USR_OBJLIBS_DEFAULT +USR_OBJLIBS+=$(USR_OBJLIBS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_OBJS_$(OS_CLASS))),) +LIB_OBJS+=$(subst -nil-,,$(LIB_OBJS_$(OS_CLASS))) +else +ifneq (,$(strip $(LIB_OBJS_DEFAULT))) +LIB_OBJS+=$(LIB_OBJS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIBRARY_$(OS_CLASS))),) +LIBRARY+=$(subst -nil-,,$(LIBRARY_$(OS_CLASS))) +else +ifneq (,$(strip $(LIBRARY_DEFAULT))) +LIBRARY+=$(LIBRARY_DEFAULT) +endif +endif + +ifneq ($(strip $(LIBRARY_IOC_$(OS_CLASS))),) +LIBRARY_IOC+=$(subst -nil-,,$(LIBRARY_IOC_$(OS_CLASS))) +else +ifneq (,$(strip $(LIBRARY_IOC_DEFAULT))) +LIBRARY_IOC+=$(LIBRARY_IOC_DEFAULT) +endif +endif + +ifneq ($(strip $(LIBRARY_HOST_$(OS_CLASS))),) +LIBRARY_HOST+=$(subst -nil-,,$(LIBRARY_HOST_$(OS_CLASS))) +else +ifneq (,$(strip $(LIBRARY_HOST_DEFAULT))) +LIBRARY_HOST+=$(LIBRARY_HOST_DEFAULT) +endif +endif + +ifneq ($(strip $(LOADABLE_LIBRARY_$(OS_CLASS))),) +LOADABLE_LIBRARY+=$(subst -nil-,,$(LOADABLE_LIBRARY_$(OS_CLASS))) +else +ifneq (,$(strip $(LOADABLE_LIBRARY_DEFAULT))) +LOADABLE_LIBRARY+=$(LOADABLE_LIBRARY_DEFAULT) +endif +endif + +ifneq ($(strip $(LOADABLE_LIBRARY_HOST_$(OS_CLASS))),) +LOADABLE_LIBRARY_HOST+=$(subst -nil-,,$(LOADABLE_LIBRARY_HOST_$(OS_CLASS))) +else +ifneq (,$(strip $(LOADABLE_LIBRARY_HOST_DEFAULT))) +LOADABLE_LIBRARY_HOST+=$(LOADABLE_LIBRARY_HOST_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_LIBS_$(OS_CLASS))),) +PROD_LIBS += $(subst -nil-,,$(PROD_LIBS_$(OS_CLASS))) +else +ifdef PROD_LIBS_DEFAULT +PROD_LIBS += $(PROD_LIBS_DEFAULT) +endif +endif + +# SHRLIB_LIBS deprecated +ifneq ($(strip $(SHRLIB_LIBS_$(OS_CLASS))),) +SHRLIB_LIBS += $(subst -nil-,,$(SHRLIB_LIBS_$(OS_CLASS))) +else +ifdef SHRLIB_LIBS_DEFAULT +SHRLIB_LIBS += $(SHRLIB_LIBS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_LIBS_$(OS_CLASS))),) +LIB_LIBS += $(subst -nil-,,$(LIB_LIBS_$(OS_CLASS))) +else +ifdef LIB_LIBS_DEFAULT +LIB_LIBS += $(LIB_LIBS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_LIBS_$(OS_CLASS))),) +USR_LIBS += $(subst -nil-,,$(USR_LIBS_$(OS_CLASS))) +else +ifdef USR_LIBS_DEFAULT +USR_LIBS += $(USR_LIBS_DEFAULT) +endif +endif + +# +# concat specific include files +# +ifneq ($(strip $(INC_$(OS_CLASS))),) +INC += $(subst -nil-,,$(INC_$(OS_CLASS))) +else +ifdef INC_DEFAULT +INC+=$(INC_DEFAULT) +endif +endif + +# +# concat specific library contents (if defined) to SYS_PROD_LIBS +# +# SYS_PROD_LIBS deprecated +ifneq ($(strip $(SYS_PROD_LIBS_$(OS_CLASS))),) +SYS_PROD_LIBS += $(subst -nil-,,$(SYS_PROD_LIBS_$(OS_CLASS))) +else +ifdef SYS_PROD_LIBS_DEFAULT +SYS_PROD_LIBS += $(SYS_PROD_LIBS_DEFAULT) +endif +endif +PROD_SYS_LIBS+=$(SYS_PROD_LIBS) + +ifneq ($(strip $(PROD_SYS_LIBS_$(OS_CLASS))),) +PROD_SYS_LIBS += $(subst -nil-,,$(PROD_SYS_LIBS_$(OS_CLASS))) +else +ifdef PROD_SYS_LIBS_DEFAULT +PROD_SYS_LIBS += $(PROD_SYS_LIBS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_SYS_LIBS_$(OS_CLASS))),) +LIB_SYS_LIBS += $(subst -nil-,,$(LIB_SYS_LIBS_$(OS_CLASS))) +else +ifdef LIB_SYS_LIBS_DEFAULT +LIB_SYS_LIBS += $(LIB_SYS_LIBS_DEFAULT) +endif +endif + +ifneq ($(strip $(USR_SYS_LIBS_$(OS_CLASS))),) +USR_SYS_LIBS += $(subst -nil-,,$(USR_SYS_LIBS_$(OS_CLASS))) +else +ifdef USR_SYS_LIBS_DEFAULT +USR_SYS_LIBS += $(USR_SYS_LIBS_DEFAULT) +endif +endif + +# +# concat specific products +# +ifneq ($(strip $(PROD_$(OS_CLASS))),) +PROD+=$(subst -nil-,,$(PROD_$(OS_CLASS))) +else +ifneq (,$(strip $(PROD_DEFAULT))) +PROD+=$(PROD_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_IOC_$(OS_CLASS))),) +PROD_IOC+=$(subst -nil-,,$(PROD_IOC_$(OS_CLASS))) +else +ifneq (,$(strip $(PROD_IOC_DEFAULT))) +PROD_IOC+=$(PROD_IOC_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_HOST_$(OS_CLASS))),) +PROD_HOST+=$(subst -nil-,,$(PROD_HOST_$(OS_CLASS))) +else +ifneq (,$(strip $(PROD_HOST_DEFAULT))) +PROD_HOST+=$(PROD_HOST_DEFAULT) +endif +endif + +# +# concat specific products +# +ifneq ($(strip $(TESTPROD_$(OS_CLASS))),) +TESTPROD+=$(subst -nil-,,$(TESTPROD_$(OS_CLASS))) +else +ifneq (,$(strip $(TESTPROD_DEFAULT))) +TESTPROD+=$(TESTPROD_DEFAULT) +endif +endif + +ifneq ($(strip $(TESTPROD_IOC_$(OS_CLASS))),) +TESTPROD_IOC+=$(subst -nil-,,$(TESTPROD_IOC_$(OS_CLASS))) +else +ifneq (,$(strip $(TESTPROD_IOC_DEFAULT))) +TESTPROD_IOC+=$(TESTPROD_IOC_DEFAULT) +endif +endif + +ifneq ($(strip $(TESTPROD_HOST_$(OS_CLASS))),) +TESTPROD_HOST+=$(subst -nil-,,$(TESTPROD_HOST_$(OS_CLASS))) +else +ifneq (,$(strip $(TESTPROD_HOST_DEFAULT))) +TESTPROD_HOST+=$(TESTPROD_HOST_DEFAULT) +endif +endif + +# +# concat specific scripts +# +ifneq ($(strip $(SCRIPTS_$(OS_CLASS))),) +SCRIPTS += $(subst -nil-,,$(SCRIPTS_$(OS_CLASS))) +else +ifdef SCRIPTS_DEFAULT +SCRIPTS += $(SCRIPTS_DEFAULT) +endif +endif + +ifneq ($(strip $(SCRIPTS_IOC_$(OS_CLASS))),) +SCRIPTS_IOC+=$(subst -nil-,,$(SCRIPTS_IOC_$(OS_CLASS))) +else +ifneq (,$(strip $(SCRIPTS_IOC_DEFAULT))) +SCRIPTS_IOC+=$(SCRIPTS_IOC_DEFAULT) +endif +endif + +ifneq ($(strip $(SCRIPTS_HOST_$(OS_CLASS))),) +SCRIPTS_HOST+=$(subst -nil-,,$(SCRIPTS_HOST_$(OS_CLASS))) +else +ifneq (,$(strip $(SCRIPTS_HOST_DEFAULT))) +SCRIPTS_HOST+=$(SCRIPTS_HOST_DEFAULT) +endif +endif + +# +# concat specific scripts +# +ifneq ($(strip $(TESTSCRIPTS_$(OS_CLASS))),) +TESTSCRIPTS += $(subst -nil-,,$(TESTSCRIPTS_$(OS_CLASS))) +else +ifdef TESTSCRIPTS_DEFAULT +TESTSCRIPTS += $(TESTSCRIPTS_DEFAULT) +endif +endif + +ifneq ($(strip $(TESTSCRIPTS_IOC_$(OS_CLASS))),) +TESTSCRIPTS_IOC+=$(subst -nil-,,$(TESTSCRIPTS_IOC_$(OS_CLASS))) +else +ifneq (,$(strip $(TESTSCRIPTS_IOC_DEFAULT))) +TESTSCRIPTS_IOC+=$(TESTSCRIPTS_IOC_DEFAULT) +endif +endif + +ifneq ($(strip $(TESTSCRIPTS_HOST_$(OS_CLASS))),) +TESTSCRIPTS_HOST+=$(subst -nil-,,$(TESTSCRIPTS_HOST_$(OS_CLASS))) +else +ifneq (,$(strip $(TESTSCRIPTS_HOST_DEFAULT))) +TESTSCRIPTS_HOST+=$(TESTSCRIPTS_HOST_DEFAULT) +endif +endif + +# +# concat specific resource files +# +ifneq ($(strip $(RCS_$(OS_CLASS))),) +RCS += $(subst -nil-,,$(RCS_$(OS_CLASS))) +else +ifdef RCS_DEFAULT +RCS += $(RCS_DEFAULT) +endif +endif + +ifneq ($(strip $(PROD_RCS_$(OS_CLASS))),) +PROD_RCS += $(subst -nil-,,$(PROD_RCS_$(OS_CLASS))) +else +ifdef PROD_RCS_DEFAULT +PROD_RCS+=$(PROD_RCS_DEFAULT) +endif +endif + +ifneq ($(strip $(LIB_RCS_$(OS_CLASS))),) +LIB_RCS += $(subst -nil-,,$(LIB_RCS_$(OS_CLASS))) +else +ifdef LIB_RCS_DEFAULT +LIB_RCS+=$(LIB_RCS_DEFAULT) +endif +endif + diff --git a/configure/CONFIG_APP_INCLUDE b/configure/CONFIG_APP_INCLUDE new file mode 100644 index 000000000..3951ed282 --- /dev/null +++ b/configure/CONFIG_APP_INCLUDE @@ -0,0 +1,24 @@ +export TOP +export IOCAPPS + +RELEASE_TOPS := $(shell $(CONVERTRELEASE) -T $(TOP) releaseTops) + +ifneq ($(RELEASE_TOPS),) + +define RELEASE_FLAGS_template + export $(1) + $(1)_HOST_BIN = $$(strip $$($(1)))/bin/$$(EPICS_HOST_ARCH) + $(1)_HOST_LIB = $$(strip $$($(1)))/lib/$$(EPICS_HOST_ARCH) + $(1)_BIN = $$(wildcard $$(strip $$($(1)))/bin/$$(T_A)) + $(1)_LIB = $$(wildcard $$(strip $$($(1)))/lib/$$(T_A)) + SHRLIB_SEARCH_DIRS += $$($(1)_LIB) + RELEASE_INCLUDES += $$(addprefix -I,$$(wildcard $$(strip $$($(1)))/include/os/$$(OS_CLASS))) + RELEASE_INCLUDES += $$(addprefix -I,$$(wildcard $$(strip $$($(1)))/include)) + RELEASE_DBDFLAGS += $$(addprefix -I,$$(wildcard $$(strip $$($(1)))/dbd)) + RELEASE_DBFLAGS += $$(addprefix -I,$$(wildcard $$(strip $$($(1)))/db)) + RELEASE_PERL_MODULE_DIRS += $$(wildcard $$($(1)_LIB)/perl) +endef +$(foreach top, $(RELEASE_TOPS), $(eval $(call RELEASE_FLAGS_template,$(top)) )) + +endif + diff --git a/configure/CONFIG_BASE b/configure/CONFIG_BASE new file mode 100644 index 000000000..dd559536d --- /dev/null +++ b/configure/CONFIG_BASE @@ -0,0 +1,116 @@ +#************************************************************************* +# 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. +#************************************************************************* + +#--------------------------------------------------------------- +# Epics base directories + +EPICS_BASE_HOST_BIN = $(EPICS_BASE)/bin/$(EPICS_HOST_ARCH) +EPICS_BASE_HOST_LIB = $(EPICS_BASE)/lib/$(EPICS_HOST_ARCH) +ifdef T_A +EPICS_BASE_LIB = $(EPICS_BASE)/lib/$(T_A) +EPICS_BASE_BIN = $(EPICS_BASE)/bin/$(T_A) +endif + +#--------------------------------------------------------------- +# Epics base Ioc libraries + +EPICS_BASE_IOC_LIBS += recIoc softDevIoc +EPICS_BASE_IOC_LIBS += miscIoc rsrvIoc dbtoolsIoc asIoc +EPICS_BASE_IOC_LIBS += dbIoc registryIoc dbStaticIoc ca Com + +#--------------------------------------------------------------- +# Epics base Host libraries + +EPICS_BASE_HOST_LIBS += cas gdd asHost dbStaticHost registryIoc +EPICS_BASE_HOST_LIBS += ca Com + +#--------------------------------------------------------------- +# Version number for base shared libraries (and win32 products) + +ifdef BASE_TOP +SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION) +PROD_VERSION = $(EPICS_VERSION).$(EPICS_REVISION) +endif # BASE_TOP + +#--------------------------------------------------------------- +# Base c preprocessor flags + +BASE_CPPFLAGS = + +# osithread default stack +OSITHREAD_USE_DEFAULT_STACK = NO +OSITHREAD_DEFAULT_STACK_FLAGS_YES = -DOSITHREAD_USE_DEFAULT_STACK +OSITHREAD_DEFAULT_STACK_FLAGS_NO = +BASE_CPPFLAGS += $(OSITHREAD_DEFAULT_STACK_FLAGS_$(OSITHREAD_USE_DEFAULT_STACK)) + +#--------------------------------------------------------------- +# Where to find the installed build tools + +TOOLS = $(EPICS_BASE_HOST_BIN) + +#--------------------------------------------------------------- +# Epics base build tools and tool flags + +MAKEBPT = $(call PATH_FILTER, $(TOOLS)/makeBpt$(HOSTEXE)) +DBEXPAND = $(call PATH_FILTER, $(TOOLS)/dbExpand$(HOSTEXE)) +DBTORECORDTYPEH = $(call PATH_FILTER, $(TOOLS)/dbToRecordtypeH$(HOSTEXE)) +DBTOMENUH = $(call PATH_FILTER, $(TOOLS)/dbToMenuH$(HOSTEXE)) +REGISTERRECORDDEVICEDRIVER = $(PERL) $(TOOLS)/registerRecordDeviceDriver.pl +CONVERTRELEASE=$(PERL) $(TOOLS)/convertRelease.pl + +#------------------------------------------------------- +# tools for installing libraries and products +INSTALL = $(PERL) $(TOOLS)/installEpics.pl +INSTALL_PRODUCT = $(INSTALL) +INSTALL_LIBRARY = $(INSTALL) + +#--------------------------------------------------------------- +# tools for making header dependancies and variable replacement +MKMF = $(PERL) $(TOOLS)/mkmf.pl +REPLACEVAR = $(PERL) $(TOOLS)/replaceVAR.pl + +#--------------------------------------------------------------- +# private versions of lex/yacc from EPICS +EYACC = $(call PATH_FILTER, $(TOOLS)/antelope$(HOSTEXE)) +ELEX = $(call PATH_FILTER, $(TOOLS)/e_flex$(HOSTEXE)) -S$(EPICS_BASE)/include/flex.skel.static + +YACC = $(EYACC) +LEX = $(ELEX) + +#--------------------------------------------------------------- +# External tools and tool flags - must be in path or defined in application + +ifndef ADL2DL +ADL2DL = adl2dl +endif + +# sch2edif compiler and flags +SCH2EDIF = sch2edif +SCH2EDIF_PATH = +SCH2EDIF_SYSFLAGS = -n -ap -p.+..+$(SCH2EDIF_PATH)+$(CAPFAST_TEMPLATES)/sym+ +SCH2EDIF_FLAGS = + +# e2db and flags +# - again there is an assumption where edb.def is installed. +ifndef E2DB +E2DB = e2db +endif +E2DB_SYSFLAGS = -ate -d $(CAPFAST_TEMPLATES)/edb.def +E2DB_FLAGS = + +ifndef DBST +DBST = dbst +endif + +ifndef MSI +MSI = msi +endif + + diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION new file mode 100644 index 000000000..1dedd2b07 --- /dev/null +++ b/configure/CONFIG_BASE_VERSION @@ -0,0 +1,66 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# +# Revision-Id: jba@aps.anl.gov-20101124205038-hr4w7l8eki0rmwm0 +# +# EPICS Version information +# +# Only the person making an official EPICS release should make changes in +# this file. +# +# EPICS_SITE_VERSION is defined in CONFIG_SITE for sites that want a local +# version number to be included in the reported version string. + +BASE_3_14=YES + +# EPICS_VERSION must be a number >0 and <256 +EPICS_VERSION = 3 + +# EPICS_REVISION must be a number >=0 and <256 +EPICS_REVISION = 14 + +# EPICS_MODIFICATION must be a number >=0 and <256 +EPICS_MODIFICATION = 12 + +# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) +# Not included if zero +EPICS_PATCH_LEVEL = 0 + +# This will end in -DEV between official releases +#EPICS_DEV_SNAPSHOT=-DEV +#EPICS_DEV_SNAPSHOT=-pre1 +#EPICS_DEV_SNAPSHOT=-pre1-DEV +#EPICS_DEV_SNAPSHOT=-pre2 +#EPICS_DEV_SNAPSHOT=-pre2-DEV +#EPICS_DEV_SNAPSHOT=-rc1 +#EPICS_DEV_SNAPSHOT=-rc1-DEV +#EPICS_DEV_SNAPSHOT=-rc2 +#EPICS_DEV_SNAPSHOT=-rc2-DEV +EPICS_DEV_SNAPSHOT= + +# No changes should be needed below here + +ifneq ($(EPICS_PATCH_LEVEL),0) + EPICS_PATCH_VSTRING=.$(EPICS_PATCH_LEVEL) +endif + +ifneq ($(strip $(EPICS_SITE_VERSION)),) + EPICS_SITE_VSTRING=-$(EPICS_SITE_VERSION) +endif + +EPICS_SHORT_VERSION=$(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION)$(EPICS_PATCH_VSTRING) +EPICS_VERSION_NUMBER=$(EPICS_SHORT_VERSION)$(EPICS_DEV_SNAPSHOT)$(EPICS_SITE_VSTRING) +EPICS_VERSION_STRING="EPICS Version $(EPICS_VERSION_NUMBER)" + +COMMIT_DATE="\$Date: 2010/11/29 10:38:06 $" + +# Provide these in case anyone is still using the old names +EPICS_CVS_SNAPSHOT=$(EPICS_DEV_SNAPSHOT) +CVS_DATE=$(COMMIT_DATE) +CVS_TAG="-no-tags-" diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON new file mode 100644 index 000000000..d7c3d534a --- /dev/null +++ b/configure/CONFIG_COMMON @@ -0,0 +1,425 @@ +#************************************************************************* +# 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: jba@aps.anl.gov-20101111194353-td0hfhkjg56whjgn +# +# CONFIG_COMMON +# +# This file is to be maintained by the community. +# +# Common Configuration Information + +#------------------------------------------------------- +# POSIX is OS default +POSIX=YES + +#------------------------------------------------------- +# Divider symbol +DIVIDER = . + +#------------------------------------------------------- +# Build architectures + +# CROSS1 will be defined only when CROSS_COMPILER_HOST_ARCHS is NOT defined +CROSS1 = $(CROSS_COMPILER_TARGET_ARCHS$(word 1,$(CROSS_COMPILER_HOST_ARCHS))) + +# CROSS2 will be defined only when CROSS_COMPILER_HOST_ARCHS is defined +# and EPICS_HOST_ARCH is one of it's words +CROSS2 = $(CROSS_COMPILER_TARGET_ARCHS$(filter-out 1,$(words $(filter $(EPICS_HOST_ARCH),$(CROSS_COMPILER_HOST_ARCHS))))) + +BUILD_ARCHS = $(EPICS_HOST_ARCH) $(CROSS1) $(CROSS2) + +#------------------------------------------------------- +# Default for perl if it's on the PATH, +# otherwise override this in os/CONFIG_SITE..Common +PERL=perl + +#------------------------------------------------------- +# dbst based database optimization default +DB_OPT = NO + +#------------------------------------------------------- +# Check configure/RELEASE file for consistency +CHECK_RELEASE_YES = checkRelease +CHECK_RELEASE_NO = +CHECK_RELEASE_WARN = warnRelease + +#------------------------------------------------------- +# GNU directory +# GNU_DIR definition is here because it is used to find +# READLINE library even if GNU compiler is not used +GNU_DIR = /usr + +#------------------------------------------------------- +# Directories + +INSTALL_LOCATION = $(TOP) + +INSTALL_LOCATION_LIB = $(INSTALL_LOCATION)/lib +INSTALL_LOCATION_BIN = $(INSTALL_LOCATION)/bin + +INSTALL_HOST_BIN = $(INSTALL_LOCATION_BIN)/$(EPICS_HOST_ARCH) +INSTALL_HOST_LIB = $(INSTALL_LOCATION_LIB)/$(EPICS_HOST_ARCH) + +INSTALL_INCLUDE = $(INSTALL_LOCATION)/include +INSTALL_DOC = $(INSTALL_LOCATION)/doc +INSTALL_HTML = $(INSTALL_LOCATION)/html +INSTALL_TEMPLATES = $(INSTALL_LOCATION)/templates +INSTALL_DBD = $(INSTALL_LOCATION)/dbd +INSTALL_DB = $(INSTALL_LOCATION)/db +INSTALL_CONFIG = $(INSTALL_LOCATION)/configure +INSTALL_JAVA = $(INSTALL_LOCATION)/javalib + +#Directory for OS independant build created files +COMMON_DIR = ../O.Common + +#------------------------------------------------------- +# Make echo output - suppress echoing if make's '-s' flag is set +ECHO := $(if $(findstring s,$(MAKEFLAGS)),\#,@echo) + +#------------------------------------------------------- +ifdef T_A + +INSTALL_LIB = $(INSTALL_LOCATION_LIB)/$(T_A) +INSTALL_SHRLIB = $(INSTALL_LOCATION_LIB)/$(T_A) +INSTALL_TCLLIB = $(INSTALL_LOCATION_LIB)/$(T_A) +INSTALL_BIN = $(INSTALL_LOCATION_BIN)/$(T_A) + +#Directories for libraries +SHRLIB_SEARCH_DIRS = $(INSTALL_LIB) + +#------------------------------------------------------- +# Ext, app, and module configure dir targets +CONFIG_INSTALLS += ../RULES_BUILD ../RELEASE* + +#------------------------------------------------------- +# Cross compile default, HOST or CROSS, CONFIG.crossCommon will override +BUILD_CLASS = HOST + +#------------------------------------------------------- +# Build defaults, CONFIG_SITE, CONFIG, or os/CONFIG* will override +STATIC_BUILD=NO +SHARED_LIBRARIES=YES +HOST_OPT=YES +CROSS_OPT=YES +HOST_WARN=YES +CROSS_WARN=YES +GNU=NO + +#------------------------------------------------------- +# Run checkRelease in $(TOP)/configure/O.* +CONFIG_TARGETS += $(CHECK_RELEASE_$(CHECK_RELEASE)) + +#------------------------------------------------------- +# Prefix and suffix +DEP=.d +OBJ = . +CMPLR_SUFFIX= +CMPLR_PREFIX= +LIB_PREFIX= +SHRLIB_PREFIX= $(LIB_PREFIX) + +#-------------------------------------------------- +# vpath directories +POSIX_YES = os/posix +GENERIC_SRC_DIRS = .. $(SRC_DIRS) +OS_SRC_DIRS += . $(foreach dir, .. $(SRC_DIRS), \ + $(addprefix $(dir)/, os/$(OS_CLASS) $(POSIX_$(POSIX)) os/default )) +ALL_SRC_DIRS = $(OS_SRC_DIRS) $(GENERIC_SRC_DIRS) + +#-------------------------------------------------- +# compile line include directories +INSTALL_INCLUDES += \ + -I$(INSTALL_INCLUDE)/os/$(OS_CLASS) \ + -I$(INSTALL_INCLUDE) +SRC_INCLUDES = -I$(COMMON_DIR) $(addprefix -I, $(wildcard $(ALL_SRC_DIRS))) + +#-------------------------------------------------- +# Target filename definitions +OBJSNAME = $(addsuffix $(OBJ),$(basename $(OBJS))) +PRODNAME = $(addsuffix $(EXE),$(basename $(PROD))) +TESTPRODNAME = $(addsuffix $(EXE),$(basename $(TESTPROD))) + +SHRLIBNAME = $(SHRLIBNAME_$(SHARED_LIBRARIES)) + +JAVA = +JAR = + +#-------------------------------------------------- +# obj files + +TARGET_OBJS = $($*_OBJLIBS) $($*_LDOBJS) $(addsuffix $(OBJ),$(basename $($*_OBJS) $($*_SRCS))) + +PRODUCT_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(USR_OBJS) $(PROD_OBJS))) +PROD_LD_OBJS = $(USR_OBJLIBS) $(PROD_OBJLIBS) $(TARGET_OBJS) $(PRODUCT_OBJS) + +LIBRARY_OBJS = $(addsuffix $(OBJ),$(basename $(SRCS) $(USR_SRCS) $(LIB_SRCS) $(LIBSRCS) $(USR_OBJS) $(LIB_OBJS))) +LIBRARY_LD_OBJS = $(USR_OBJLIBS) $(LIB_OBJLIBS) $(TARGET_OBJS) $(LIBRARY_OBJS) + +#-------------------------------------------------- +# WIN95/NT resource files + +TARGET_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $($*_RCS))),) + +PROD_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(PROD_RCS))),) +PROD_LD_RESS = $(TARGET_RESS) $(PROD_RESS) + +LIBRARY_RESS = $(if $(RES),$(addsuffix $(RES),$(basename $(RCS) $(LIB_RCS) $(LIBRARY_RCS))),) +LIBRARY_LD_RESS = $(TARGET_RESS) $(LIBRARY_RESS) + +#-------------------------------------------------- +# WIN95/NT source browser +PROD_BAF = $(addsuffix $(BAF), $(basename $(PROD))) +LIB_BAF=$(addsuffix $(BAF),$(basename $(LIBRARY))) + +#-------------------------------------------------- +# C preprocessor, compiler, and linker flag defaults + +# Target architecture specific flags +ARCH_DEP_CPPFLAGS = +ARCH_DEP_CFLAGS = +ARCH_DEP_CXXFLAGS = $(ARCH_DEP_CFLAGS) +ARCH_DEP_LDFLAGS = +ARCH_DEP_LDLIBS = + +# Target operating system specific flags +OP_SYS_CPPFLAGS = +OP_SYS_CFLAGS = +OP_SYS_CXXFLAGS = $(OP_SYS_CFLAGS) +OP_SYS_LDFLAGS = +OP_SYS_INCLUDES = + +# Makefile specific flags +USR_INCLUDES = +USR_CFLAGS = +USR_CXXFLAGS = +USR_LDFLAGS = +USR_LIBS = +USR_CPPFLAGS = +USR_DBDFLAGS = +USR_ARFLAGS = + +# Debug specific options +DEBUG_CPPFLAGS = +DEBUG_CFLAGS = +DEBUG_CXXFLAGS = $(DEBUG_CFLAGS) +DEBUG_LDFLAGS = +DEBUG_LDLIBS = + +# Target specific options +TARGET_INCLUDES = $($(basename $@)_INCLUDES_$(T_A)) +TARGET_CFLAGS = $($(basename $@)_CFLAGS_$(T_A)) +TARGET_CXXFLAGS = $($(basename $@)_CXXFLAGS_$(T_A)) +TARGET_CPPFLAGS = $($(basename $@)_CPPFLAGS_$(T_A)) + +TARGET_INCLUDES += $($(basename $@)_INCLUDES_$(OS_CLASS)) $($(basename $@)_INCLUDES) +TARGET_CFLAGS += $($(basename $@)_CFLAGS_$(OS_CLASS)) $($(basename $@)_CFLAGS) +TARGET_CXXFLAGS += $($(basename $@)_CXXFLAGS_$(OS_CLASS)) $($(basename $@)_CXXFLAGS) +TARGET_CPPFLAGS += $($(basename $@)_CPPFLAGS_$(OS_CLASS)) $($(basename $@)_CPPFLAGS) + +TARGET_LDFLAGS = $($*_LDFLAGS) + +# Warnings flags +WARN_CPPFLAGS = $(WARN_CPPFLAGS_$($(BUILD_CLASS)_WARN)) +WARN_CFLAGS = $(WARN_CFLAGS_$($(BUILD_CLASS)_WARN)) +WARN_CXXFLAGS = $(WARN_CXXFLAGS_$($(BUILD_CLASS)_WARN)) + +# Optimization flags +OPT_CPPFLAGS = $(OPT_CPPFLAGS_$($(BUILD_CLASS)_OPT)) +OPT_CFLAGS = $(OPT_CFLAGS_$($(BUILD_CLASS)_OPT)) +OPT_CXXFLAGS = $(OPT_CXXFLAGS_$($(BUILD_CLASS)_OPT)) + +# Static build flags +STATIC_CFLAGS = $(STATIC_CFLAGS_$(STATIC_BUILD)) +STATIC_CXXCFLAGS = $(STATIC_CXXFLAGS_$(STATIC_BUILD)) +STATIC_LDFLAGS = $(STATIC_LDFLAGS_$(STATIC_BUILD)) +STATIC_LDLIBS = $(STATIC_LDLIBS_$(STATIC_BUILD)) + +#-------------------------------------------------- +# cflags for shared library src files (from SHRLIB_CFLAGS) +LIBRARY_SRCS=$(basename $(foreach lib,$(LIBRARY) $(LOADABLE_LIBRARY),$($(lib)_OBJSNAME) $(LIBRARY_OBJS))) +LIBRARY_SRC_CFLAGS=$($(patsubst $*,SHRLIB,$(findstring $*,$(LIBRARY_SRCS)))_CFLAGS) + +#-------------------------------------------------- +# prefix, suffix, and ldflags for loadable shared libraries +TARGET_LIB_LDFLAGS=$($(patsubst $*,LOADABLE_,$(findstring $*,$(LOADABLE_LIBRARY)))SHRLIB_LDFLAGS) +LOADABLE_SHRLIB_PREFIX=$(SHRLIB_PREFIX) +LOADABLE_SHRLIB_SUFFIX=$(SHRLIB_SUFFIX) + +#-------------------------------------------------- +# Command-line input support default +COMMANDLINE_LIBRARY = EPICS +OP_SYS_LDLIBS += $(LDLIBS_$(COMMANDLINE_LIBRARY)) +OP_SYS_LDFLAGS += $(LDFLAGS_$(COMMANDLINE_LIBRARY)) +RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(COMMANDLINE_LIBRARY)) + +#-------------------------------------------------- +# Flags + +INCLUDES = -I. $(SRC_INCLUDES) $(INSTALL_INCLUDES) $(RELEASE_INCLUDES)\ + $(TARGET_INCLUDES) $(USR_INCLUDES) $(OP_SYS_INCLUDES) $($(BUILD_CLASS)_INCLUDES) + +CFLAGS = $($(BUILD_CLASS)_CFLAGS) $(POSIX_CFLAGS) $(OPT_CFLAGS) $(DEBUG_CFLAGS)\ + $(PIPE_CFLAGS) $(WARN_CFLAGS) $(TARGET_CFLAGS) $(USR_CFLAGS) $(ARCH_DEP_CFLAGS)\ + $(CODE_CFLAGS) $(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS) $(HDEPENDS_CFLAGS) + +CXXFLAGS = $($(BUILD_CLASS)_CXXFLAGS) $(POSIX_CXXFLAGS) $(OPT_CXXFLAGS) $(DEBUG_CXXFLAGS)\ + $(PIPE_CFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS) $(USR_CXXFLAGS) $(ARCH_DEP_CXXFLAGS)\ + $(CODE_CXXFLAGS) $(STATIC_CXXCFLAGS) $(OP_SYS_CXXFLAGS) $(LIBRARY_SRC_CFLAGS) $(HDEPENDS_CFLAGS) + +LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(POSIX_LDFLAGS) \ + $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS) $($(BUILD_CLASS)_LDFLAGS)\ + $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS) + +LDLIBS = \ + $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS) $(GNU_LDLIBS_$(GNU)) + +CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS)\ + $(OPT_CPPFLAGS) $(DEBUG_CPPFLAGS) $(WARN_CPPFLAGS)\ + $(BASE_CPPFLAGS) $(TARGET_CPPFLAGS) $(USR_CPPFLAGS) $(ARCH_DEP_CPPFLAGS)\ + $(OP_SYS_CPPFLAGS) $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) + +#-------------------------------------------------- +# ar definition default +ARFLAGS = +ARCMD = $(AR) $(ARFLAGS) $(USR_ARFLAGS) $@ $(LIBRARY_LD_OBJS) + +#-------------------------------------------------- +# 'Munch' link-edit +MUNCH_CMD = $(LD) $(MUNCH_LDFLAGS) -o $@ $^ + +#-------------------------------------------------- +# LEX default options +# +# Allow 8-bit characters +LEXOPT += -8 +# Generate an "interactive" scanner, solves problems at EOF. +LEXOPT += -I + +#-------------------------------------------------- +# Build compile line here + +PATH_FILTER = $(1) +COMPILE.c = $(CC) -c $(CPPFLAGS) $(CFLAGS) $(call PATH_FILTER,$(INCLUDES)) +COMPILE.cpp = $(CCC) -c $(CPPFLAGS) $(CXXFLAGS) $(call PATH_FILTER,$(INCLUDES)) + +#-------------------------------------------------- +# C preprocessor command +PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(INCLUDES) $< > $@ + +#-------------------------------------------------- +# Header dependency file generation + +HDEPENDS = YES +HDEPENDS_METHOD = CMD + +HDEPENDS_INCLUDES = $(subst -I,,$(INCLUDES)) +HDEPENDSFLAGS = -m $*$(DEP) $(HDEPENDS_INCLUDES) $@ $< +HDEPENDSCMD = -$(MKMF) $(HDEPENDS_FLAGS) $(HDEPENDSFLAGS) + +HDEPENDS_CMD_NO = $(ECHO) "" +HDEPENDS_CMD_YES = $(if $(filter CMD,$(HDEPENDS_METHOD)),$(HDEPENDSCMD),$(HDEPENDS_CMD_NO)) +HDEPENDS_CMD = $(HDEPENDS_CMD_$(HDEPENDS)) + +HDEPENDSCFLAGS = -MMD +HDEPENDS_CFLAGS_YES = $(if $(filter CFLAGS,$(HDEPENDS_METHOD)),$(HDEPENDSCFLAGS)) +HDEPENDS_CFLAGS = $(HDEPENDS_CFLAGS_$(HDEPENDS)) + +#-------------------------------------------------- +# depends definition + +TARGET_SRCS = $(foreach name, $(TESTPROD) $(PROD) $(LIBRARY), $($(name)_SRCS)) +SRC_FILES = $(LIB_SRCS) $(LIBSRCS) $(SRCS) $(USR_SRCS) $(PROD_SRCS) $(TARGET_SRCS) +HDEPENDS_FILES_YES = $(addsuffix $(DEP),$(notdir $(basename $(SRC_FILES)))) +HDEPENDS_FILES = $(if $(filter NO,$(HDEPENDS)),,$(HDEPENDS_FILES_YES)) + +#--------------------------------------------------------------- +# Names of installed items +# +# each list starts with the destination directory name(s) +# to make sure it's there + +INSTALL_PROD= $(PRODNAME:%= $(INSTALL_BIN)/%) +INSTALL_LIBS= $(LIBNAME:%=$(INSTALL_LIB)/%) +INSTALL_MUNCHS= $(MUNCHNAME:%=$(INSTALL_BIN)/%) +INSTALL_SHRLIBS= $(SHRLIBNAME:%=$(INSTALL_SHRLIB)/%) +INSTALL_LOADABLE_SHRLIBS= $(LOADABLE_SHRLIBNAME:%=$(INSTALL_SHRLIB)/%) +INSTALL_DLL_LINK_LIBS=$(DLL_LINK_LIBNAME:%=$(INSTALL_LIB)/%) +INSTALL_TCLLIBS=$(TCLLIBNAME:%=$(INSTALL_TCLLIB)/%) +INSTALL_TCLINDEX=$(TCLINDEX:%=$(INSTALL_TCLLIB)/%) +INSTALL_SCRIPTS = $(SCRIPTS:%= $(INSTALL_BIN)/%) +INSTALL_OBJS = $(OBJSNAME:%= $(INSTALL_BIN)/%) + +INSTALL_DOCS = $(DOCS:%= $(INSTALL_DOC)/%) +INSTALL_HTMLS = $(HTMLS:%= $(INSTALL_HTML)/$(HTMLS_DIR)/%) + +INSTALL_TEMPLATE = $(addprefix $(INSTALL_TEMPLATES_SUBDIR)/, \ + $(subst $(CONFIG),top/configure,$(TEMPLATES))) +INSTALL_CONFIGS = $(CONFIGS:%= $(INSTALL_CONFIG)/%) + +INSTALL_BIN_INSTALLS = $(addprefix $(INSTALL_BIN)/,$(notdir $(BIN_INSTALLS))) +INSTALL_LIB_INSTALLS = $(addprefix $(INSTALL_LIB)/,$(notdir $(LIB_INSTALLS))) + +#--------------------------------------------------------------- +# Installed file permissions +BIN_PERMISSIONS = 555 +LIB_PERMISSIONS = 444 +INSTALL_PERMISSIONS = 444 + +#--------------------------------------------------------------- +# +# auto determine the directory paths that things are installed to +# RULES: +# 1) found in any one of several os specific area +# => install to $(INSTALL_INCLUDE)/os/$(OS_CLASS) +# 2) not foundin (1) and found in generic area +# => install to $(INSTALL_INCLUDE) +# 3) not found in (1) or (2) then may be (not yet) computer generated +# => install into $(INSTALL_INCLUDE)/os/$(OS_CLASS) and let +# build rules work on vpath +# +# These rules guarantee that the users include from +# no more than two directories +# +INSTALL_INC += $(foreach inc, $(INC), \ + $(firstword \ + $(OS_INSTALL_INC) \ + $(GENERIC_INSTALL_INC) \ + $(GENERATED_INSTALL_INC) ) ) +INSTALL_INC += $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INC_$(OS_CLASS)) ) + +# +# Rule 1 +# +OS_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/os/$(OS_CLASS)/, $(INSTALL_INC_ggg) ) +INSTALL_INC_ggg = $(foreach dir, $(OS_SRC_DIRS), $(INSTALL_INC_fff) ) +INSTALL_INC_fff = $(subst $(dir)/, , $(INSTALL_INC_eee) ) +INSTALL_INC_eee = $(wildcard $(addsuffix /$(inc), $(dir)) ) + +# +# Rule 2 +# +GENERIC_INSTALL_INC = $(addprefix $(INSTALL_INCLUDE)/, $(INSTALL_INC_ccc) ) +INSTALL_INC_ccc = $(foreach dir, .. $(SRC_DIRS), $(INSTALL_INC_bbb) ) +INSTALL_INC_bbb = $(subst $(dir)/, , $(INSTALL_INC_aaa) ) +INSTALL_INC_aaa = $(wildcard $(addsuffix /$(inc), $(dir)) ) + +# +# Rule 3 +# +GENERATED_INSTALL_INC = $(INSTALL_INCLUDE)/$(inc) + +COMMON_INC += $(filter $(COMMON_DIR)/%, $(foreach file, $(INC), \ + $(firstword $(SOURCE_INC) $(COMMON_DIR)/$(file) ) ) ) +SOURCE_INC = $(wildcard $(file) $(SOURCE_INC_bbb) ) +SOURCE_INC_bbb = $(foreach dir, $(ALL_SRC_DIRS), $(SOURCE_INC_aaa) ) +SOURCE_INC_aaa = $(addsuffix /$(file), $(dir) ) + +endif diff --git a/configure/CONFIG_ENV b/configure/CONFIG_ENV new file mode 100644 index 000000000..f381ed41a --- /dev/null +++ b/configure/CONFIG_ENV @@ -0,0 +1,57 @@ +#************************************************************************* +# 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: Andrew Johnson +# Date: 20 April 1995 +# +# Experimental Physics and Industrial Control System (EPICS) +# +# CONFIG_ENV - EPICS Environment Parameter configuration file +# +# This file is read by the script base/src/libCom/env/bldEnvdata.pl +# Variable definitions must take the form +# VAR = VALUE +# or +# VAR = "Value containing spaces" +# with each one on its own line. +# Enclosing spaces and "" will be trimmed. +# + +# Default environment settings + +# Channel Access: +# Details are in the CA reference manual +# (see CAref.html in this distribution) +EPICS_CA_ADDR_LIST="" +EPICS_CA_AUTO_ADDR_LIST=YES +EPICS_CA_NAME_SERVERS="" +EPICS_CA_CONN_TMO=30.0 +EPICS_CA_REPEATER_PORT=5065 +EPICS_CA_SERVER_PORT=5064 +EPICS_CA_MAX_ARRAY_BYTES=16384 +EPICS_CA_BEACON_PERIOD=15.0 +EPICS_CA_MAX_SEARCH_PERIOD=300.0 +EPICS_CAS_BEACON_PERIOD= +EPICS_CAS_BEACON_PORT= +EPICS_CAS_AUTO_BEACON_ADDR_LIST="" +EPICS_CAS_BEACON_ADDR_LIST="" +EPICS_CAS_SERVER_PORT= +EPICS_CAS_INTF_ADDR_LIST="" +EPICS_CAS_IGNORE_ADDR_LIST="" + +# Log Server: +# EPICS_IOC_LOG_PORT Log server port number etc. +EPICS_IOC_LOG_PORT=7004 + +# Other services: + +EPICS_CMD_PROTO_PORT= +EPICS_AR_PORT=7002 + diff --git a/configure/CONFIG_FILE_TYPE b/configure/CONFIG_FILE_TYPE new file mode 100644 index 000000000..11fe4c696 --- /dev/null +++ b/configure/CONFIG_FILE_TYPE @@ -0,0 +1,64 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Macros and rules to create a new installation file type +# + +# -------------------------------------------------------------- +# Module developers can now define a new type of file, e.g. ABC, +# so that files of type ABC will be installed into a directory +# defined by INSTALL_ABC. This is done by creating a new CONFIG +# file, e.g. CONFIG_ABC, with the following lines: +# +# FILE_TYPE += ABC +# INSTALL_ABC = $(INSTALL_LOCATION)/abc +# +# The INSTALL_ABC directory should be be a subdirectory of +# $(INSTALL_LOCATION). The file type ABC should be target +# architecture independent (alh files, medm files, edm files). +# +# Optional rules necessary for files of type ABC should be put in +# a RULES_ABC file. +# +# The module developer installs new CONFIG* or RULES* files +# into the directory $(INSTALL_LOCATION)/cfg by including the +# following Makefile line: +# +# CFG += CONFIG_ABC RULES_ABC +# +# Files of type ABC are installed into INSTALL_ABC directory +# by adding a line like the following to a Makefile. +# +# ABC += +# +# Files in $(INSTALL_LOCATION)/cfg directory are now included by +# the base config files so the definitions and rules are available +# for use by later src directory Makefiles in the same module or +# by other modules with a RELEASE line pointing to the TOP of +# the module with RULES_ABC. + +FILE_TYPE += ADL +INSTALL_ADL = $(INSTALL_LOCATION)/adl + +FILE_TYPE += ALH +INSTALL_ALH = $(INSTALL_LOCATION)/alh + +FILE_TYPE += CFG +INSTALL_CFG = $(INSTALL_LOCATION)/cfg + +FILE_TYPE += EDL +INSTALL_EDL = $(INSTALL_LOCATION)/edl + +FILE_TYPE += PERL_MODULES +INSTALL_PERL_MODULES = $(INSTALL_LOCATION_LIB)/perl + +INSTALLS_CFG= $(CFG:%= $(INSTALL_CFG)/%) +DIRECTORY_TARGETS += $(foreach type, $(FILE_TYPE),$(INSTALL_$(type))) + diff --git a/configure/CONFIG_SITE b/configure/CONFIG_SITE new file mode 100644 index 000000000..88d383331 --- /dev/null +++ b/configure/CONFIG_SITE @@ -0,0 +1,186 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# +# Revision-Id: jba@aps.anl.gov-20101026200048-yl4pqc4jl040iqea +# +# CONFIG_SITE - Global site configuration file +# + +# The host architecture performing the build, in the form +# -[-] +# +# Currently Supporting: +# aix-ppc (IBM compiler used for host builds) +# aix-ppc-gnu (GNU compiler used for host builds) +# cygwin-x86 (cygwin compiler used for host builds) +# darwin-ppc (PowerPC based Apple running OSX) +# darwin-ppcx86 (Universal binaries for both CPUs) +# darwin-x86 (Intel based Apple running OSX) +# freebsd-x86 (GNU compiler used for host builds) +# freebsd-x86_64 (GNU compiler used for host builds) +# linux-ppc (GNU compiler used for host builds) +# linux-ppc64 (GNU compiler used for host builds) +# linux-x86 (GNU compiler used for host builds) +# linux-x86_64 (GNU compiler used for host builds) +# linux-x86-borland (Borland C++ compiler used for host builds) +# solaris-sparc (Sun compiler used for host builds) +# solaris-sparc-gnu (GNU compiler used for host builds) +# solaris-sparc64 (Sun compiler used for host builds) +# solaris-sparc64-gnu (GNU compiler used for host builds) +# solaris-x86 (Sun compiler used for host builds) +# solaris-x86-gnu (GNU compiler used for host builds) +# solaris-x86_64 (Sun compiler used for host builds) +# solaris-x86_64-gnu (GNU compiler used for host builds) +# win32-x86 (MS Visual C++ compiler used for host builds) +# win32-x86-cygwin (WIN32 API with cygwin GNU compiler used for host builds) +# win32-x86-mingw (MinGW compiler used for host builds) +# windows-x64 (MS Visual C++ compiler used for host builds) + +# Debugging builds +# linux-x86-debug (GNU compiler with -g option for host builds) +# linux-x86_64-debug (GNU compiler with -g option for host builds) +# solaris-sparc-debug (sun compiler no optimization,-g for debugging info) +# win32-x86-debug (MS Visual C++ compiler with debug option for host builds) +# windows-x64-debug (MS Visual C++ compiler with debug option for host builds) + + +# EPICS_HOST_ARCH is a required environment variable +# Do not set EPICS_HOST_ARCH in this file. +# Use base/startup files to set EPICS_HOST_ARCH or +# provide EPICS_HOST_ARCH on the GNU make command line. + +# The cross-compiler architectures to build EPICS for +# +# Currently Supporting: + +# ios-arm +# ios-386 +# linux-386 (linux-x86 host) +# linux-486 (linux-x86 host) +# linux-586 (linux-x86 host) +# linux-686 (linux-x86 host) +# linux-arm (linux-x86 host) +# linux-arm_eb (linux-x86 host) +# linux-arm_el (linux-x86 host) +# linux-athlon (linux-x86 host) +# linux-cris (Axis GNU crosscompiler on linux-x86 host) +# linux-cris_v10 (Axis GNU crosscompiler on linux-x86 host) +# linux-cris_v32 (Axis GNU crosscompiler on linux-x86 host) +# linux-xscale_be +# vxWorks-486 +# vxWorks-68040 +# vxWorks-68040lc +# vxWorks-68060 +# vxWorks-pentium +# vxWorks-ppc603 +# vxWorks-ppc603_long +# vxWorks-ppc604 +# vxWorks-ppc604_long +# vxWorks-ppc604_altivec +# vxWorks-mpc8540 +# RTEMS-at91rm9200ek +# RTEMS-beatnik +# RTEMS-gen68360 +# RTEMS-mcp750 +# RTEMS-mvme167 +# RTEMS-mvme2100 +# RTEMS-mvme2700 +# RTEMS-mvme3100 +# RTEMS-mvme5500 +# RTEMS-pc386 +# RTEMS-psim +# RTEMS-uC5282 +# + +# Definitions of CROSS_COMPILER_TARGET_ARCHS in +# configure/os/CONFIG_SITE..Common files will +# override +# +CROSS_COMPILER_TARGET_ARCHS= +#CROSS_COMPILER_TARGET_ARCHS=vxWorks-68040 + +# If only a subset of the host architectures perform +# the build for the CROSS_COMPILER_TARGET_ARCHS +# uncomment the following line and specify them. +# +CROSS_COMPILER_HOST_ARCHS= + +# Build shared libraries? +# must be either YES or NO +# NOTE: os/CONFIG.$(EPICS_HOST_ARCH).$(EPICS_HOST_ARCH) files and +# os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(EPICS_HOST_ARCH) files may override +# +# NOTE WIN32: YES results in a DLL. Valid settings are +# SHARED_LIBRARIES=YES and STATIC_BUILD=NO +# SHARED_LIBRARIES=NO and STATIC_BUILD=YES +# +SHARED_LIBRARIES=YES + +# Build client objects statically ? +# must be either YES or NO +# +STATIC_BUILD=NO + +# Should header dependancy files be automatically generated +# for each C/C++ created object file? +# must be either YES or NO +HDEPENDS=YES + +# Host build optimization +# must be either YES or NO +HOST_OPT=YES + +# Cross build optimization +# must be either YES or NO +CROSS_OPT=YES + +# Generate Verbose Compiler Warnings for Host builds +# must be either YES or NO +HOST_WARN=YES + +# Generate Verbose Compiler Warnings for cross compile builds +# must be either YES or NO +CROSS_WARN=YES + +# Create and/or install perl build tools for R3.13 extension builds? +# must be either YES or NO +# +# NOTE: Set to YES only if you have existing R3.13 extensions to be +# built with this base +# +#COMPAT_TOOLS_313=YES + +# Create and/or install files for R3.13 ioc application and extension builds? +# must be either YES or NO +# +# NOTE: Set to YES only if you have existing R3.13 ioc applications +# and extensions to be built with this base +# +COMPAT_313=NO + +# Installation directory +# If you don't want to install into $(TOP) dir then +# define INSTALL_LOCATION here +#INSTALL_LOCATION= + +# Use POSIX thread priority scheduling (YES or NO) +USE_POSIX_THREAD_PRIORITY_SCHEDULING = NO + +# Site version number, if set will append '-' and this string to the +# EPICS version number string that is reported by many tools +EPICS_SITE_VERSION = + +# For GNU compiler, use pipes rather than temporary files for communication +# between the various stages of compilation. +GCC_PIPE = NO + +# Include RPATH when linking executables and libraries. +# must be either YES or NO +LINKER_USE_RPATH=YES + diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV new file mode 100644 index 000000000..484f14026 --- /dev/null +++ b/configure/CONFIG_SITE_ENV @@ -0,0 +1,79 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +# Revision-Id: anj@aps.anl.gov-20101020200106-6li5onfc43xb807y +# Author: Andrew Johnson +# Date: 1 May 1995 +# +# Experimental Physics and Industrial Control System (EPICS) +# +# CONFIG_SITE_ENV - EPICS Environment Parameter Site configuration file +# +# This file is read by the script base/src/libCom/env/bldEnvdata.pl +# Variable definitions must take the form +# VAR = VALUE +# or +# VAR = "Value containing spaces" +# with each one on its own line. +# Enclosing spaces and "" will be trimmed. +# + +# Site-specific environment settings + +# Time service: +# EPICS_TIMEZONE +# local timezone info for vxWorks and RTEMS IOCs. The format is +# :::: +# where the start and end are mmddhh - that is month,day,hour +# e.g. for ANL in 2010: EPICS_TIMEZONE=CUS::360:031402:110702 +# +# DST for 2011 US: Mar 13 - Nov 06 +# EU: Mar 27 - Oct 30 +# DST for 2012 US: Mar 11 - Nov 04 +# EU: Mar 25 - Oct 28 +# DST for 2013 US: Mar 10 - Nov 03 +# EU: Mar 31 - Oct 27 +# DST for 2014 US: Mar 09 - Nov 02 +# EU: Mar 30 - Oct 26 +# DST for 2015 US: Mar 08 - Nov 01 +# EU: Mar 29 - Oct 25 +# (see: http://www.worldtimezone.org/daylight.html) +# +# These values are for 2011: +EPICS_TIMEZONE=CUS::360:031302:110602 +#EPICS_TIMEZONE=MET::-60:032702:103002 + +# EPICS_TS_NTP_INET +# NTP time server ip address. Uses boot host if not set. +EPICS_TS_NTP_INET= + +# IOC Shell: +# IOCSH_PS1 +# Prompt string +# IOCSH_HISTSIZE +# Number of lines of command history to keep. +IOCSH_PS1="epics> " +IOCSH_HISTSIZE=50 + +# Log Server: +# EPICS_IOC_LOG_INET +# Log server ip addr. +# EPICS_IOC_LOG_FILE_NAME +# pathname to the log file. +# EPICS_IOC_LOG_FILE_LIMIT +# maximum log file size. +# EPICS_IOC_LOG_FILE_COMMAND +# A shell command string used to obtain a new +# path name in response to SIGHUP - the new path name will +# replace any path name supplied in EPICS_IOC_LOG_FILE_NAME + +EPICS_IOC_LOG_INET= +EPICS_IOC_LOG_FILE_NAME= +EPICS_IOC_LOG_FILE_COMMAND= +EPICS_IOC_LOG_FILE_LIMIT=1000000 + diff --git a/configure/Makefile b/configure/Makefile new file mode 100644 index 000000000..809433b70 --- /dev/null +++ b/configure/Makefile @@ -0,0 +1,30 @@ +#************************************************************************* +# 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 +# + +TOP=.. + +include $(TOP)/configure/CONFIG + +# Bootstrap resolution: tools not installed yet +TOOLS = $(TOP)/src/tools + +CONFIGS += $(subst ../,,$(wildcard ../CONFIG*)) +CONFIGS += $(subst ../,,$(wildcard ../os/CONFIG*)) + +CONFIGS += $(subst ../,,$(wildcard ../RELEASE*)) +CONFIGS += $(subst ../,,$(wildcard ../RULES*)) + +CONFIGS += $(subst ../,,$(wildcard ../tools/*.pl)) + +include $(TOP)/configure/RULES + diff --git a/configure/RELEASE b/configure/RELEASE new file mode 100644 index 000000000..01375a05f --- /dev/null +++ b/configure/RELEASE @@ -0,0 +1,21 @@ +#************************************************************************* +# 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. +#************************************************************************* +# RELEASE: Define location of external EPICS products + +# Define INSTALL_LOCATION in CONFIG_SITE + +# VX_DIR definition now in os/CONFIG_SITE.Common.vxWorksCommon +# RTEMS_BASE (and RTEMS_VERSION) now in os/CONFIG_SITE.Common.RTEMS + +# NB: Settings in RELEASE files can be overridden in files named +# RELEASE.$(EPICS_HOST_ARCH).Common +# RELEASE.Common.$(T_A) +# RELEASE.$(EPICS_HOST_ARCH).$(T_A) + diff --git a/configure/RULES b/configure/RULES new file mode 100644 index 000000000..67cd4150a --- /dev/null +++ b/configure/RULES @@ -0,0 +1,21 @@ +#************************************************************************* +# 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 + +ifndef T_A + +include $(CONFIG)/RULES_ARCHS + +else #T_A + +include $(CONFIG)/RULES_BUILD + +endif # T_A defined + diff --git a/configure/RULES.Db b/configure/RULES.Db new file mode 100644 index 000000000..ed6e10978 --- /dev/null +++ b/configure/RULES.Db @@ -0,0 +1,382 @@ +#************************************************************************* +# 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. +#************************************************************************* +#RULES.Db + +##################################################### vpath + +vpath %.dbd $(USR_VPATH) $(GENERIC_SRC_DIRS) $(dir $(DBD)) +vpath %.db $(USR_VPATH) $(GENERIC_SRC_DIRS) $(dir $(DB)) +vpath %.vdb $(USR_VPATH) $(GENERIC_SRC_DIRS) $(dir $(DB)) +vpath %.substitutions $(USR_VPATH) $(GENERIC_SRC_DIRS) $(COMMON_DIR) +vpath %.template $(USR_VPATH) $(GENERIC_SRC_DIRS) $(COMMON_DIR) +vpath bpt%.data $(USR_VPATH) $(GENERIC_SRC_DIRS) $(COMMON_DIR) +vpath %.acf $(USR_VPATH) $(GENERIC_SRC_DIRS) $(COMMON_DIR) +vpath %.acs $(USR_VPATH) $(GENERIC_SRC_DIRS) $(COMMON_DIR) + +##################################################### dbdflags + +# dbExpand +INSTALL_DBDFLAGS += -I $(INSTALL_LOCATION)/dbd +INSTALL_DBFLAGS += -I $(INSTALL_LOCATION)/db +DBDFLAGS = $(USR_DBDFLAGS) -I . -I .. $(INSTALL_DBDFLAGS) $(RELEASE_DBDFLAGS) +DBFLAGS = $($*_DBFLAGS) $(USR_DBFLAGS) -I. -I.. $(INSTALL_DBFLAGS) $(RELEASE_DBFLAGS) + +##################################################### +# To allow os specific dbd files AND have the -j option work properly, +# add the following lines to your ioc application Makefile +# +# # These lines may be committed to RULES.Db for a future base release. +# CROSS_TARGET_OS_TYPES = $(sort $(foreach target, \ +# $(CROSS_COMPILER_TARGET_ARCHS),$(firstword $(subst -, ,$(target))))) +# DBD += $(foreach type, $(CROSS_TARGET_OS_TYPES), $(DBD_$(type))) +# +# and then add something like the following os specific lines +# +# DBD_vxWorks += abcVx.dbd +# DBD_RTEMS += abcRTEMS.dbd +# DBD_solaris += abcSolaris.dbd +# +##################################################### Targets + +# Following line added for backward compatibilty +DBD += $(DBDNAME) + +DBDINC_NAME = $(patsubst %.h,%,$(patsubst %.db,%,$(DBDINC))) +DBD += $(addsuffix .dbd,$(DBDINC_NAME)) +INC += $(addsuffix .h,$(DBDINC_NAME)) + +INSTALL_DBDS += $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD))) + +COMMON_DBDS += $(filter $(COMMON_DIR)/%, $(foreach file, $(DBD), \ + $(firstword $(SOURCE_DBD) $(COMMON_DIR)/$(file) ) ) ) +SOURCE_DBD = $(wildcard $(file) $(SOURCE_DBD_bbb) ) +SOURCE_DBD_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DBD_aaa) ) +SOURCE_DBD_aaa = $(addsuffix /$(file), $(dir) ) + +INSTALL_DBS += $(addprefix $(INSTALL_DB)/,$(notdir $(DB))) + +COMMON_DBS += $(filter $(COMMON_DIR)/%, $(foreach file, $(DB), \ + $(firstword $(SOURCE_DB) $(COMMON_DIR)/$(file) ) ) ) +SOURCE_DB = $(wildcard $(file) $(SOURCE_DB_bbb) ) +SOURCE_DB_bbb = $(foreach dir, $(GENERIC_SRC_DIRS), $(SOURCE_DB_aaa) ) +SOURCE_DB_aaa = $(addsuffix /$(file), $(dir) ) + +COMMONS = $(COMMON_DIR)/*.dbd $(COMMON_DIR)/*.db $(COMMON_DIR)/*.h \ + $(COMMON_DIR)/*.substitutions $(COMMON_DIR)/*.template + +# Remove trailing numbers (to 99) on stem +TEMPLATE1=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \ + $(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \ + $*)))))))))) +TEMPLATE2=$(patsubst %0,%,$(patsubst %1,%,$(patsubst %2,%,$(patsubst %3,%,$(patsubst %4,%, \ + $(patsubst %5,%,$(patsubst %6,%,$(patsubst %7,%,$(patsubst %8,%,$(patsubst %9,%, \ + $(TEMPLATE1))))))))))) +TEMPLATE3=$(addsuffix .template,$(addprefix ../,$(TEMPLATE2))) +TEMPLATE_FILENAME=$(firstword $(wildcard $($*_TEMPLATE) $(addprefix ../,$($*_TEMPLATE)) ../$*.template $(TEMPLATE3) ../template)) + +# dbst based database optimization +ifeq '$(DB_OPT)' 'YES' +RAW=.raw +DBS = $(filter %.db,$(DB)) $(addsuffix $(RAW),$(filter %.db,$(DB))) +COMMON_DBS = $(addprefix $(COMMON_DIR)/,$(DBS)) +endif + +INSTALL_DB_INSTALLS = $(addprefix $(INSTALL_DB)/,$(notdir $(DB_INSTALLS))) +INSTALL_DBD_INSTALLS = $(addprefix $(INSTALL_DBD)/,$(notdir $(DBD_INSTALLS))) + +##################################################### acf files +# An access security configuration file, *.acf, can be created from +# an *.acs file (has format of acf file plus #include "filename" lines) + +# flags for GNU compiler +ACF_CPPFLAGS_YES = -undef -nostdinc +ACF_CPPFLAGS = $(ACF_CPPFLAGS_$(GNU)) + +ACF_INCLUDES = -I. $(TARGET_INCLUDES) $(USR_INCLUDES)\ + $(SRC_INCLUDES) -I$(INSTALL_DB) +ACFDEPENDS_CMD = -$(MKMF) -m $(notdir $@)$(DEP) $(subst -I,,$(ACF_INCLUDES)) $@ $< +ACF_CMD = $(CPP) $(ACF_CPPFLAGS) $(ACF_INCLUDES) $< > $@ + +##################################################### dependancies + +HINC += $(addsuffix .h,$(DBDINC_NAME)) +COMMON_DBDINC += $(addprefix $(COMMON_DIR)/,$(HINC)) + +DBDDEPENDS_FILES += $(addsuffix $(DEP),$(HINC) $(DBS) \ + $(patsubst $(COMMON_DIR)/%,%,$(COMMON_DBDS))) + +DBDDEPENDS_FLAGS = $(subst -I,,$(filter-out -S%,$(DBDFLAGS))) +DBDDEPENDS_CMD = -$(MKMF) -m $(notdir $@)$(DEP) $(DBDDEPENDS_FLAGS) $@ $< + +MAKEDBDEPENDS = $(PERL) $(TOOLS)/makeDbDepends.pl + +##################################################### + +ifndef T_A + +ECHO := $(if $(findstring s,$(MAKEFLAGS)),\#,@echo) +COMMON_DIR = . +INSTALL_DBDS = +INSTALL_DBS = +COMMON_DBDS = $(DBD) +COMMON_DBS = $(DB) +COMMONS = $(DBD) $(DB) + +ACTIONS = inc +ACTIONS += build +ACTIONS += install +ACTIONS += buildInstall +ACTIONS += browse +ACTIONS += runtests + +actionArchTargets = $(foreach x, $(ACTIONS),\ $(foreach arch,$(BUILD_ARCHS), $(x)$(DIVIDER)$(arch))) + +cleanArchTargets = $(foreach arch,$(BUILD_ARCHS), clean$(DIVIDER)$(arch)) +-include $(TOP)/configure/CONFIG_APP_INCLUDE + +all: install + +install: buildInstall + +buildInstall : build + +rebuild: clean install + +.PHONY: all inc build install clean rebuild buildInstall + +$(actionArchTargets) $(BUILD_ARCHS):install +$(cleanArchTargets):clean + +.PHONY: $(BUILD_ARCHS) $(actionArchTargets) $(cleanArchTargets) + +endif # T_A defined + +ifneq (,$(strip $(DBDDEPENDS_FILES))) +-include $(DBDDEPENDS_FILES) +endif + +$(DBDDEPENDS_FILES): + +##################################################### build dependancies, clean rule + +inc : $(COMMON_INC) $(INSTALL_INC) + +build : $(COMMON_DBDS) $(COMMON_DBS) \ + $(INSTALL_DBDS) $(INSTALL_DBS) \ + $(DBDDEPENDS_FILES) $(TARGETS) \ + $(INSTALL_DB_INSTALLS) $(INSTALL_DBD_INSTALLS) + +clean:: + @$(RM) $(COMMONS) $(DBDDEPENDS_FILES) + @$(RM) *_registerRecordDeviceDriver.cpp + @$(RM) $(TARGETS) + +realclean:: clean + +##################################################### CapFast filter + +$(COMMON_DIR)/%.edf: ../%.sch $(DEPSCHS) + @$(RM) $@ + @if [ ! -f cad.rc -a -r ../cad.rc ] ; then ln -s ../cad.rc ; fi + $(SCH2EDIF) $(SCH2EDIF_SYSFLAGS) $(SCH2EDIF_FLAGS) -o $@ $< + +##################################################### Substitution files + +# WARNING: CREATESUBSTITUTIONS script needs output dir on command line + +ifdef CREATESUBSTITUTIONS +$(COMMON_DIR)/%.substitutions: + $(ECHO) "Create substitutions" + @$(RM) $@ + $(CREATESUBSTITUTIONS) $@ +endif + +$(INSTALL_DB)/%.substitutions: %.substitutions + $(ECHO) "Installing db file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +.PRECIOUS: $(COMMON_DIR)/%.substitutions + +##################################################### Template files + +$(COMMON_DIR)/%.template: $(COMMON_DIR)/%.edf + @$(RM) $@ + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $@.VAR $< + @$(REPLACEVAR) < $@.VAR > $@ + @$(RM) $@.VAR + +$(INSTALL_DB)/%.template: %.template + $(ECHO) "Installing db file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +.PRECIOUS: $(COMMON_DIR)/%.template + +##################################################### INC files + +$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd + @$(RM) $(notdir $@)$(DEP) + @$(DBDDEPENDS_CMD) + $(ECHO) "$<:../Makefile" >> $(notdir $@)$(DEP) + @$(RM) $@ + $(DBTORECORDTYPEH) $(DBDFLAGS) $< $@ + +$(COMMON_DIR)/%Record.h: %Record.dbd + @$(RM) $(notdir $@)$(DEP) + @$(DBDDEPENDS_CMD) + $(ECHO) "$<:../Makefile" >> $(notdir $@)$(DEP) + @$(RM) $@ + $(DBTORECORDTYPEH) $(DBDFLAGS) $< $@ + +$(COMMON_DIR)/menu%.h: $(COMMON_DIR)/menu%.dbd + @$(RM) $(notdir $@)$(DEP) + @$(DBDDEPENDS_CMD) + $(ECHO) "$<:../Makefile" >> $(notdir $@)$(DEP) + @$(RM) $@ + $(DBTOMENUH) $(DBDFLAGS) $< $@ + +$(COMMON_DIR)/menu%.h: menu%.dbd + @$(RM) $(notdir $@)$(DEP) + @$(DBDDEPENDS_CMD) + $(ECHO) "$<:../Makefile" >> $(notdir $@)$(DEP) + @$(RM) $@ + $(DBTOMENUH) $(DBDFLAGS) $< $@ + +.PRECIOUS: $(COMMON_DIR)/%.h + +##################################################### DBD files + +$(COMMON_DIR)/bpt%.dbd: bpt%.data + @$(RM) $@ + $(MAKEBPT) $< $@ + +$(COMMON_DIR)/%.dbd: $(COMMON_DIR)/%Include.dbd + @$(RM) $(notdir $@)$(DEP) + @$(DBDDEPENDS_CMD) + $(ECHO) "$<:../Makefile" >> $(notdir $@)$(DEP) + $(ECHO) "Expanding dbd" + @$(RM) $@ + @$(DBEXPAND) $(DBDFLAGS) -o $@ $< + +$(COMMON_DIR)/%.dbd: %Include.dbd + @$(RM) $(notdir $@)$(DEP) + @$(DBDDEPENDS_CMD) + $(ECHO) "$<:../Makefile" >> $(notdir $@)$(DEP) + $(ECHO) "Expanding dbd" + @$(RM) $@ + $(DBEXPAND) $(DBDFLAGS) -o $@ $< + +$(COMMON_DIR)/%Include.dbd: + @$(RM) $@ + $(PERL) $(TOOLS)/makeIncludeDbd.pl $($*_DBD) $@ + +$(INSTALL_DBD)/%: $(COMMON_DIR)/% + $(ECHO) "Installing created dbd file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +$(INSTALL_DBD)/%: % + $(ECHO) "Installing dbd file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +define DBD_INSTALLS_template +$$(INSTALL_DBD)/$$(notdir $(1)) : $(1) + $(ECHO) "Installing $$@" + @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$^ $$(INSTALL_DBD) +endef +$(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file)))) + +.PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%Include.dbd + +##################################################### DB files + +$(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf + $(E2DB) $(E2DB_SYSFLAGS) $(E2DB_FLAGS) -n $*.VAR $< + @$(REPLACEVAR) < $*.VAR > $@ + @$(RM) $*.VAR + +$(COMMON_DIR)/%.db$(RAW): %.substitutions + @$(RM) $(notdir $@)$(DEP) + $(MAKEDBDEPENDS) $@ $< $(TEMPLATE_FILENAME) >> $(notdir $@)$(DEP) + $(ECHO) "$@:$(TEMPLATE_FILENAME)" >> $(notdir $@)$(DEP) + $(ECHO) "Inflating database from $< $(TEMPLATE_FILENAME)" + @$(RM) $@ $*.tmp + $(MSI) $(DBFLAGS) -S$< $(TEMPLATE_FILENAME) > $*.tmp + $(MV) $*.tmp $@ + +$(COMMON_DIR)/%.db$(RAW): %.template + @$(RM) $(notdir $@)$(DEP) + @$(MAKEDBDEPENDS) $@ $^ >> $(notdir $@)$(DEP) + $(ECHO) "Inflating database from $<" + @$(RM) $@ $*.tmp + $(MSI) $(DBFLAGS) $< > $*.tmp + $(MV) $*.tmp $@ + +$(COMMON_DIR)/%.acf: %.acs + @$(RM) $(notdir $@)$(DEP) + @$(ACFDEPENDS_CMD) + $(ECHO) "Creating acf file $@" + @$(RM) $@ + $(ACF_CMD) + +.PRECIOUS: $(COMMON_DIR)/%.acf + +# dbst based database optimization +ifeq '$(DB_OPT)' 'YES' + +$(COMMON_DIR)/%.db$(RAW): ../%.db + @$(RM) $@ + $(CP) $< $@ + +$(COMMON_DIR)/%.db: $(COMMON_DIR)/%.db$(RAW) + $(ECHO) "Optimizing database $@" + @$(RM) $@ + $(DBST) . $< -d > $@ + +.PRECIOUS: $(COMMON_DIR)/%.db +.PRECIOUS: $(DB:%=$(COMMON_DIR)/%$(RAW)) +else + +$(INSTALL_DB)/%: % + $(ECHO) "Installing db file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) +endif + +$(INSTALL_DB)/%.db: $(COMMON_DIR)/%.db + $(ECHO) "Installing created db file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +define DB_INSTALLS_template +$$(INSTALL_DB)/$$(notdir $(1)) : $(1) + $(ECHO) "Installing $$@" + @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$^ $$(INSTALL_DB) +endef +$(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file)))) + +.PRECIOUS: $(COMMON_DIR)/%.edf +.PRECIOUS: $(COMMON_DBS) + +##################################################### register record,device,driver support + +IOC_INST_TOP := $(firstword $(IOCS_APPL_TOP) \ + $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION) ) ) + +%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd + @$(RM) $@ $*.tmp + $(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp + $(MV) $*.tmp $@ + +%_registerRecordDeviceDriver.cpp: %.dbd + @$(RM) $@ $*.tmp + $(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp + $(MV) $*.tmp $@ + +.PRECIOUS: %_registerRecordDeviceDriver.cpp +.PRECIOUS: %$(DEP) + +##################################################### END OF FILE diff --git a/configure/RULES.ioc b/configure/RULES.ioc new file mode 100644 index 000000000..c661d54d5 --- /dev/null +++ b/configure/RULES.ioc @@ -0,0 +1,38 @@ +#************************************************************************* +# 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. +#************************************************************************* +#RULES.ioc +include $(CONFIG)/RULES_DIRS + +build$(DIVIDER)$(ARCH) build: buildInstall +install$(DIVIDER)$(ARCH) install: buildInstall +$(ARCH): buildInstall + +ifeq ($(filter $(ARCH),$(BUILD_ARCHS)),$(ARCH)) +buildInstall$(DIVIDER)$(ARCH) buildInstall: $(TARGETS) + +clean$(DIVIDER)$(ARCH) clean: + $(RM) cdCommands envPaths dllPath.bat + +else +buildInstall$(DIVIDER)$(ARCH) buildInstall: +clean$(DIVIDER)$(ARCH) clean: +endif + +cdCommands envPaths dllPath.bat: $(wildcard $(TOP)/configure/RELEASE*) \ + $(TOP)/configure/CONFIG $(INSTALL_BIN) +ifeq ($(IOCS_APPL_TOP),) + $(PERL) $(TOOLS)/convertRelease.pl -a $(ARCH) $@ +else + $(PERL) $(TOOLS)/convertRelease.pl -a $(ARCH) -t $(IOCS_APPL_TOP) $@ +endif + +realclean: + $(RM) cdCommands envPaths dllPath.bat + diff --git a/configure/RULES_ARCHS b/configure/RULES_ARCHS new file mode 100644 index 000000000..d64d53679 --- /dev/null +++ b/configure/RULES_ARCHS @@ -0,0 +1,94 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# + +all: install + +ACTIONS = inc +ACTIONS += build +ACTIONS += install +ACTIONS += buildInstall +ACTIONS += browse +ACTIONS += runtests +#ACTIONS += rebuild + +actionPart = $(word 1, $(subst $(DIVIDER), ,$@)) +archPart = $(word 2, $(subst $(DIVIDER), ,$@)) + +actionArchTargets = $(foreach x, $(ACTIONS),\ + $(foreach arch,$(BUILD_ARCHS), $(x)$(DIVIDER)$(arch))) + +cleanArchTargets = $(foreach arch,$(BUILD_ARCHS), clean$(DIVIDER)$(arch)) + +buildDirs = $(addprefix O.,$(BUILD_ARCHS)) + +#************************************************************************* +# Create epics_host_arch dependancies for GNU make -j option +# Only works with GNU make 3.81 or later (uses eval function) +# Needed in dirs where EPICS_HOST_ARCH build creates a tool used in +# cross arch builds + +CROSS_ARCHS += $(CROSS1) $(CROSS2) + +# j is NOT found in MAKEFLAGS when using make 3.81 +# Hope to uncomment this for GNU make 3.82 +#ifeq ($(findstring j,$(MAKEFLAGS)),j) + +define DEP_template +$(2): $$(EPICS_HOST_ARCH) +$(1)$$(DIVIDER)$(2): $(1)$$(DIVIDER)$$(EPICS_HOST_ARCH) O.$(2) +endef + +$(foreach action, $(ACTIONS), $(foreach arch,\ + $(CROSS_ARCHS),$(eval $(call DEP_template,$(action),$(arch))))) + +#endif +#************************************************************************* + +# Allows rebuild to work with parallel builds option, -j. +ifeq (rebuild,$(filter rebuild,$(MAKECMDGOALS))) +$(buildDirs) O.Common : clean +rebuild: install +endif + +$(actionArchTargets) : $(buildDirs) O.Common + $(MAKE) -C O.$(archPart) -f ../Makefile TOP=$(TOP)/.. T_A=$(archPart) $(actionPart) + +$(BUILD_ARCHS) : % : O.% O.Common + $(MAKE) -C O.$@ -f ../Makefile TOP=$(TOP)/.. T_A=$@ + +$(ACTIONS):%: $(foreach arch, $(BUILD_ARCHS), %$(DIVIDER)$(arch)) + +$(buildDirs): + $(PERL) $(TOOLS)/makeMakefile.pl $@ $(TOP)/.. + +O.Common: + $(MKDIR) O.Common + +# +# special clean rule +# +clean :: + $(RMDIR) $(addprefix O.,$(BUILD_ARCHS)) O.Common + +archclean :: + $(RMDIR) $(addprefix O.,$(BUILD_ARCHS)) + +$(cleanArchTargets) :: + $(RMDIR) O.$(archPart) + +realclean :: + $(RMDIR) O.* + +.PHONY : $(actionArchTargets) +.PHONY : $(cleanArchTargets) +.PHONY : $(BUILD_ARCHS) rebuild +.PHONY : $(ACTIONS) clean realclean archclean all diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD new file mode 100644 index 000000000..9b0591bd7 --- /dev/null +++ b/configure/RULES_BUILD @@ -0,0 +1,469 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Rules for making things specified in Makefile +# +# we are in O.$(OS_CLASS), but most sources are one dir above: +# + +ifndef BASE_RULES_BUILD +BASE_RULES_BUILD=1 + +vpath %.c $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.cc $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.cpp $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.C $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.rc $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.h $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.skel.static $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.y $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.l $(USR_VPATH) $(ALL_SRC_DIRS) + +#--------------------------------------------------------------- + +include $(CONFIG)/CONFIG_ADDONS + +#--------------------------------------------------------------- +# Set PROD, TESTPROD, OBJS, and LIBRARY + +SCRIPTS_HOST += $(PERL_SCRIPTS) +# PERL_SCRIPTS are installed into existing $(INSTALL_BIN) for Host systems + +ifeq ($(findstring Host,$(VALID_BUILDS)),Host) +LIBRARY += $(LIBRARY_HOST) +LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_HOST) +OBJS += $(OBJS_HOST) +PROD += $(PROD_HOST) +SCRIPTS += $(SCRIPTS_HOST) +TESTSCRIPTS += $(TESTSCRIPTS_HOST) +TESTPROD += $(TESTPROD_HOST) +endif + +ifeq ($(findstring Ioc,$(VALID_BUILDS)),Ioc) +LIBRARY += $(LIBRARY_IOC) +OBJS += $(OBJS_IOC) +PROD += $(PROD_IOC) +SCRIPTS += $(SCRIPTS_IOC) +TESTSCRIPTS += $(TESTSCRIPTS_IOC) +TESTPROD += $(TESTPROD_IOC) +endif + +#--------------------------------------------------------------- + +ifdef TEMPLATES_DIR +INSTALL_TEMPLATES_SUBDIR = $(INSTALL_TEMPLATES)/$(TEMPLATES_DIR) +else +INSTALL_TEMPLATES_SUBDIR = $(INSTALL_TEMPLATES) +endif + +#--------------------------------------------------------------- +# First target + +all: install + +-include $(CONFIG)/RULES.Db + +-include $(CONFIG)/RULES_JAVA + +ifneq (,$(strip $(HDEPENDS_FILES))) +-include $(HDEPENDS_FILES) +endif + +#--------------------------------------------------------------- +# Included defines and rules for prod,testprod, and library targets + +#ifneq (,$(strip $(PROD) $(TESTPROD) $(LIBRARY) $(LOADABLE_LIBRARY))) +include $(CONFIG)/RULES_TARGET +#endif + +#--------------------------------------------------------------- +# Products and Object libraries +# +PRODTARGETS += $(PRODNAME) $(MUNCHNAME) $(CTDT_SRCS) $(CTDT_OBJS) $(NMS) + +#--------------------------------------------------------------- +# Generate a test specification if any tests are defined. +# +ifneq (,$(strip $(TESTS))) +TARGETS += testspec +endif + +#--------------------------------------------------------------- +# Libraries +# + +LIBTARGETS += $(LIBNAME) $(INSTALL_LIBS) \ + $(SHRLIBNAME) $(INSTALL_SHRLIBS) \ + $(DLL_LINK_LIBNAME) $(INSTALL_DLL_LINK_LIBS) \ + $(LOADABLE_SHRLIBNAME) $(INSTALL_LOADABLE_SHRLIBS) + +$(INSTALL_LIBS): $(INSTALL_SHRLIBS) $(INSTALL_DLL_LINK_LIBS) $(INSTALL_LOADABLE_SHRLIBS) + + +-include $(CONFIG)/RULES_FILE_TYPE + +# Main targets + +install: buildInstall + +buildInstall : build + +# Allows rebuild to work with parallel builds option, -j. +install: $(patsubst rebuild,clean,$(filter rebuild,$(MAKECMDGOALS))) + +rebuild: clean install + +build : inc + +build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODNAME) \ + $(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS) + +inc : $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) + +buildInstall : \ + $(INSTALL_SCRIPTS) $(INSTALL_PROD) $(INSTALL_MUNCHS) \ + $(INSTALL_TCLLIBS) $(INSTALL_TCLINDEX) \ + $(INSTALL_OBJS) \ + $(INSTALL_DOCS) \ + $(INSTALL_HTMLS) \ + $(INSTALL_TEMPLATE) \ + $(INSTALL_BIN_INSTALLS) + +clean:: + $(ECHO) "Cleaning" + @$(RM) *.i *$(OBJ) *.a $(TESTPRODNAME) $(LIBNAME) $(SHRLIBNAME) \ + $(INC) $(TARGETS) $(DLL_LINK_LIBNAME) $(TDS) \ + *.out MakefileInclude $(LOADABLE_SHRLIBNAME) *.manifest *.exp \ + $(COMMON_INC) $(HDEPENDS_FILES) $(PRODTARGETS) $(TESTSCRIPTS) +ifdef RES + @$(RM) *$(RES) +endif +ifdef BAF + @$(RM) $(PROD_BAF) $(LIB_BAF) +endif +ifdef BOF + @$(RM) *$(BOF) +endif + +# WIN95/NT source browser +#ifdef BAF +browse: $(LIB_BAF) $(PROD_BAF) +#endif + +$(DIRECTORY_TARGETS) : + $(MKDIR) -p $@ + +$(PRODNAME): $(INSTALL_LIB_INSTALLS) + +# RELEASE file consistency checking +checkRelease: + $(CONVERTRELEASE) checkRelease +warnRelease: + -$(CONVERTRELEASE) checkRelease + +#--------------------------------------------------------------- +# The order of the following rules is +# VERY IMPORTANT !!!! + +$(TESTPRODNAME) $(PRODNAME): $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS) + +$(TESTPRODNAME) $(PRODNAME):%$(EXE): + @$(RM) $@ + $(DEBUGCMD) $(LINK.cpp) + $(MT_EXE_COMMAND) + +# object libs for R3.13 vxWorks compatibility only +$(OBJLIBNAME): $(OBJLIB_LD_OBJS) + +$(OBJLIBNAME):%$(OBJ): + @$(RM) $@ + $(OBJLIB_LINK.cpp) + +%_ctdt$(OBJ) : %_ctdt.c + @$(RM) $@ + $(COMPILE.ctdt) $< + +%$(OBJ): %.c + @$(HDEPENDS_CMD) + @$(RM) $@ + $(COMPILE.c) $(call PATH_FILTER,$<) $(COMPILE_FILTER.c) + +%$(OBJ): %.cc + @$(HDEPENDS_CMD) + @$(RM) $@ + $(COMPILE.cpp) $(call PATH_FILTER,$<) $(COMPILE_FILTER.cpp) + +%$(OBJ): %.cpp + @$(HDEPENDS_CMD) + @$(RM) $@ + $(COMPILE.cpp) $(call PATH_FILTER,$<) $(COMPILE_FILTER.cpp) + +%$(OBJ): %.C + @$(HDEPENDS_CMD) + @$(RM) $@ + $(COMPILE.cpp) $(call PATH_FILTER,$<) $(COMPILE_FILTER.cpp) + +# WIN95/NT resource compiler +%$(RES): %.rc + @$(RM) $@ + $(RCCMD) + +# WIN95/NT source browser +%.bsc: %.sbr + $(ECHO) "building source browser archive $@" + @$(RM) $@ + $(BAFCMD) $< + +# +# rename the y.tab.h file only if we +# are creating it +# +ifeq ($(findstring -d, $(YACCOPT)),-d) +%.h %.c: %.y + $(RM) $*.c y.tab.c + $(RM) $*.h y.tab.h + $(YACC) $(YACCOPT) $< + $(MV) y.tab.c $*.c + $(MV) y.tab.h $*.h +else +%.c: %.y + $(RM) $*.c y.tab.c + $(YACC) $(YACCOPT) $< + $(MV) y.tab.c $*.c +endif + +%.c: %.l + @$(RM) lex.yy.c + $(LEX) $(LEXOPT) $< + @$(RM) $@ + $(MV) lex.yy.c $@ + +# Adl2dl rule +%.dl : ../%.adl + @$(RM) $@ + -$(ADL2DL) $< $@ + +# Mangen Rule: +%.1:% + $(MANGEN) -s $< + $(MV) $( $@ + +%.nm : %$(OBJ) + @$(RM) $@ + $(NM) $< > $@ + +%_ctdt.c : %.nm + @$(RM) $@ + $(PERL) $(TOOLS)/munch.pl -o $@ $< + +$(MUNCHNAME):%$(MUNCH_SUFFIX) : $(MUNCH_DEPENDS) %$(EXE) + @$(RM) $@ + $(MUNCH_CMD) + +$(OBJLIB_MUNCHNAME):%.munch : %_ctdt$(OBJ) %$(OBJ) + @$(RM) $@ + $(MUNCH_CMD) + +runtests: $(TESTSCRIPTS_$(BUILD_CLASS)) + -$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^ + +testspec: $(TESTSCRIPTS_$(BUILD_CLASS)) + @$(RM) $@ + @echo OS-class: $(OS_CLASS) > $@ + @echo Target-arch: $(T_A) >> $@ + $(if $^, @echo Tests: $^ >> $@) + $(if $(TESTSPEC_$(OS_CLASS)), @echo "Harness: $(TESTSPEC_$(OS_CLASS))" >> $@) + +# If there's a perl test script (.plt) available, use it +%.t: ../%.plt + @$(RM) $@ + @$(CP) $< $@ + +# Some versions of Test::Harness expect test programs in perl only. +# Generate a perl program to exec the real test binary. +%.t: %$(EXE) + @$(RM) $@ + @$(PERL) $(TOOLS)/makeTestfile.pl $@ $< + +#--------------------------------------------------------------- +## Install rules for BIN_INSTALLS and LIB_INSTALLS +define BIN_INSTALLS_template +$$(INSTALL_BIN)/$$(notdir $(1)) : $(1) + $(ECHO) "Installing $$(/configure/RULES_EXPAND + +ifeq ($(findstring Host,$(VALID_BUILDS)),Host) + +# Default settings +EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl + +EXPANDFLAGS += -t $(INSTALL_LOCATION) -a $(T_A) +EXPANDFLAGS += $(addprefix -D ,$(EXPAND_VARS)) + +EXPANDED = $(EXPAND:%@=%) + +buildInstall: $(EXPANDED) + +$(EXPANDED): %: ../%@ + $(ECHO) "Expanding $< to $@" + @$(RM) $@ + @$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@ + +clean:: + @$(RM) $(EXPANDED) + +endif diff --git a/configure/RULES_FILE_TYPE b/configure/RULES_FILE_TYPE new file mode 100644 index 000000000..0ccc71cd9 --- /dev/null +++ b/configure/RULES_FILE_TYPE @@ -0,0 +1,73 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# + +#--------------------------------------------------------------- +# Include /configure/RULES_BUILD definitions from tops defined in RELEASE* files +# +RELEASE_RULES_BUILDS = $(foreach top, $(RELEASE_TOPS), $(wildcard $($(top))/configure/RULES_BUILD)) +ifneq ($(RELEASE_RULES_BUILDS),) + include $(RELEASE_RULES_BUILDS) +endif + +#--------------------------------------------------------------- +# Include /cfg/RULES* definitions from tops defined in RELEASE* files +# +RELEASE_CFG_RULES = $(foreach top, $(RELEASE_TOPS), $(wildcard $($(top))/cfg/RULES*)) +ifneq ($(RELEASE_CFG_RULES),) + include $(RELEASE_CFG_RULES) +endif + +#--------------------------------------------------------------- +# If this is not BASE then include TOP/configure/RULES_BUILD definitions +# +ifeq ($(wildcard $(TOP)/configure/CONFIG_BASE_VERSION),) +TOP_RULES_BUILDS = $(wildcard $(TOP)/configure/RULES_BUILD) +ifneq ($(TOP_RULES_BUILDS),) + include $(TOP_RULES_BUILDS) +endif +endif + +#--------------------------------------------- +# Include existing and new $(INSTALL_CFG)/* definitions +# +TOP_CFG_FILES = $(sort $(wildcard $(INSTALL_CFG)/RULES*) \ + $(wildcard $(INSTALL_CFG)/CONFIG*) \ + $(addprefix $(INSTALL_CFG)/,$(CFG))) +ifneq ($(TOP_CFG_FILES),) + include $(TOP_CFG_FILES) +endif + +#--------------------------------------------------------------- + +define FILE_TYPE_template + +$(1) += $$(if $$(strip $$($(1)_$$(OS_CLASS))),$$(subst -nil-,,$$($(1)_$$(OS_CLASS))), $$($(1)_DEFAULT)) + +INSTALLS_$(1) = $$($(1):%=$$(INSTALL_$(1))/%) + +$$(INSTALL_$(1))/%: ../% + $(ECHO) "Installing $(1) file $$@" + @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$< $$(dir $$@) + +$$(INSTALL_$(1))/%: % + $(ECHO) "Installing $(1) file $$@" + @$$(INSTALL) -d -m $$(INSTALL_PERMISSIONS) $$< $$(dir $$@) + +buildInstall : $$(INSTALLS_$(1)) + +endef + +$(foreach type, $(FILE_TYPE),$(eval $(call FILE_TYPE_template,$(strip $(type))))) +#--------------------------------------------- + +clean:: + @$(RM) $(foreach type, $(FILE_TYPE), $($(type))) + diff --git a/configure/RULES_JAVA b/configure/RULES_JAVA new file mode 100644 index 000000000..f45289e73 --- /dev/null +++ b/configure/RULES_JAVA @@ -0,0 +1,157 @@ +#************************************************************************* +# 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 +# + + +ifeq ($(BUILD_CLASS),HOST) + +#------------------------------------------------------- +# java jdk1.1.5 definitions + +JAVA_INC = $(JAVA_DIR)/include +JAVA_BIN = $(JAVA_DIR)/bin +JAVA_INCLUDES += -I$(JAVA_INC) -I$(JAVA_INC)/$(word 1, $(subst -, ,$(T_A))) -I$(COMMON_DIR) + +JAVACCMD = $(subst \,/,$(JAVA_BIN)/javac$(EXE) $(CLASSPATH) $(SOURCEPATH) $(JAVACFLAGS)) +JAVAHCMD = $(subst \,/,$(JAVA_BIN)/javah$(EXE) -d $(COMMON_DIR) -force $(CLASSPATH) $(JAVAHFLAGS)) +JARCMD = $(subst \,/,$(JAVA_BIN)/jar$(EXE) $(JAR_OPTIONS) $@ $(JARINPUT) $(JARPACKAGES)) + +#------------------------------------------------------- +vpath %.java .. +vpath %.jar .. $(COMMON_DIR) + +empty:= +space:= $(empty) $(empty) +CLASSPATH = -classpath $(subst $(space),:,$(strip $(USR_PRECLASSPATH) $(INSTALL_JAVA) $(USR_CLASSPATH))) +SOURCEPATH = -sourcepath .:..:../.. + +#------------------------------------------------------- +# Java directory + +ifdef JAVA +JAVA_DIRECTORY_TARGETS += $(INSTALL_JAVA) +endif + +#------------------------------------------------------- +# Java native method C header files + +JAVAHFLAGS += $(USR_JAVAHFLAGS) +JAVAINC_CLASSFILES += $(addprefix $(INSTALL_JAVA)/,$(subst _,/,$(subst .h,.class,$(JAVAINC)))) +JAVAINC_CLASSNAMES += $(subst _,.,$(subst .h,,$(JAVAINC))) +COMMON_JAVAINC += $(addprefix $(COMMON_DIR)/,$(JAVAINC)) + +#------------------------------------------------------- +# Java class files + +JAVACFLAGS += $(USR_JAVACFLAGS) +CLASSES += $(subst .java,.class,$(JAVA)) +INSTALL_CLASSES = $(addprefix $(INSTALL_JAVA)/,$(CLASSES)) + +TESTCLASSES += $(subst .java,.class,$(notdir $(TESTJAVA))) +COMMON_TESTCLASSES += $(addprefix $(COMMON_DIR)/,$(TESTCLASSES)) +DEPTESTJAVA += $(subst .class,.java,$(TESTCLASSES)) + +#------------------------------------------------------- +# Java jar file + +INSTALL_JAR =$(addprefix $(INSTALL_JAVA)/,$(JAR)) +JARMANIFEST += $(firstword $(MANIFEST) $(JAR_MANIFEST)) +JARDEPFILES += $(addprefix $(INSTALL_JAVA)/,$(subst .java,.class,$(JAVA)) $(JAR_INPUT)) +JARINPUT += $(foreach inp,$(JAR_INPUT),-C $(INSTALL_JAVA) $(subst .java,.class,$(inp))) +JARPACKAGES += $(foreach pkg,$(JAR_PACKAGES),-C $(INSTALL_JAVA) $(subst .,/,$(pkg))) +PACKAGEDIRS += $(foreach pkg,$(JAR_PACKAGES),$(addprefix $(INSTALL_JAVA)/,$(subst .,/,$(pkg)))) + +ifneq ($(JARMANIFEST),) +JAR_OPTIONS = cvmf $(JARMANIFEST) +else +JAR_OPTIONS = cvf +endif + +#------------------------------------------------------- +# Java doc definitions +ifdef JAVADOC +JAVADOCFLAGS += $(USR_JAVADOCFLAGS) +JAVADOCCMD = $(subst \,/,$(JAVA_BIN)/javadoc$(EXE) $(CLASSPATH) $(SOURCEPATH) $(JAVADOCFLAGS)) +INSTALL_JAVADOC = $(addprefix $(INSTALL_HTML)/,$(JAVADOC))/index.html +endif + +#------------------------------------------------------- +# Java rules + +all: install + +install: buildInstall + +buildInstall : build + +rebuild: clean install + +inc: $(JAVA_DIRECTORY_TARGETS) $(INSTALL_CLASSES) $(COMMON_JAVAINC) + +build: inc + +build: $(COMMON_TESTCLASSES) + +buildInstall : $(INSTALL_JAR) $(INSTALL_JAVADOC) + +#This clean works only from O.* dirs. +clean:: + @$(RMDIR) $(INSTALL_CLASSES) $(PACKAGEDIRS) + @$(RM) $(INSTALL_JAR) $(INSTALL_JAVADOC) + @$(RM) $(COMMON_TESTCLASSES) $(COMMON_JAVAINC) + +ifdef JAVA_DIRECTORY_TARGETS +$(JAVA_DIRECTORY_TARGETS): + $(MKDIR) -p $@ +endif + +$(COMMON_JAVAINC):$(JAVAINC_CLASSFILES) + $(ECHO) Creating header files $(COMMON_JAVAINC) + @$(RM) $@ + $(JAVAHCMD) $(JAVAINC_CLASSNAMES) + +$(COMMON_TESTCLASSES): $(addprefix ../,$(DEPTESTJAVA)) + $(ECHO) Creating test java class files $(COMMON_TESTCLASSES) + @$(RM) $@ + $(JAVACCMD) -d $(COMMON_DIR) $^ + +$(INSTALL_CLASSES): $(addprefix ../,$(JAVA)) + $(ECHO) Creating java class files + @$(RM) $@ + $(JAVACCMD) -d $(INSTALL_JAVA) $^ + +$(INSTALL_JAVADOC): $(addprefix ../,$(JAVA)) + $(ECHO) Creating java doc files + @$(RM) $@ + $(JAVADOCCMD) -d $(addprefix $(INSTALL_HTML)/,$(JAVADOC)) $^ + +$(COMMON_DIR)/%.jar: $(JARDEPFILES) + $(ECHO) Creating java jar file $@ + @$(RM) $@ + $(JARCMD) + +$(INSTALL_JAVA)/%.jar: $(COMMON_DIR)/%.jar + $(ECHO) "Installing java jar file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +$(INSTALL_JAVA)/%.jar: %.jar + $(ECHO) "Installing java jar file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +$(addprefix $(INSTALL_JAVA)/, $(JARINPUT)): $(INSTALL_JAVA)/%: ../% + $(ECHO) "Installing jar input file $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + +.PHONY: all install buildInstall rebuild clean build inc + +.PRECIOUS: $(COMMON_JAVAINC) + +endif diff --git a/configure/RULES_OCTAVE b/configure/RULES_OCTAVE new file mode 100644 index 000000000..353f84a0e --- /dev/null +++ b/configure/RULES_OCTAVE @@ -0,0 +1,52 @@ +#************************************************************************* +# 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 +# +#------------------------------------------------------- +# Octave definitions and rules + +ifeq ($(findstring Host,$(VALID_BUILDS)),Host) + +FILE_TYPE += OCTAVE +INSTALL_OCTAVE = $(INSTALL_LOCATION_LIB)/octave +DIRECTORY_TARGETS += $(INSTALL_OCTAVE) + +ifdef T_A + +MKOCTFILE_FLAGS += --mex --verbose -DOCTAVE +MKOCTFILE_FLAGS += $(RELEASE_INCLUDES) $(addprefix -L,$(SHRLIB_SEARCH_DIRS)) + +vpath %.mex $(USR_VPATH) $(ALL_SRC_DIRS) +vpath %.m $(USR_VPATH) $(ALL_SRC_DIRS) + +define OCTAVES_template +$(1) : $$($(1)_SRCS) +endef +$(foreach file, $(OCTAVES),$(eval $(call OCTAVES_template,$(strip $(file))))) + +#This clean works from O.* dirs. +clean:: + @$(RM) *.mex *.m + +.PRECIOUS: *.m *.mex + +%.mex: + mkoctfile $(MKOCTFILE_FLAGS) $($*_LIBS:%=-l%) $($*_SRCS) + +endif +endif + + +# Makefile usage: +# OCTAVES += abc.mex def.mex +# abc_SRCS = a1.c a2.c +# abc_LIBS = ca Com +# def_SRCS = a3.c a4.c +# def_LIBS = ca Com diff --git a/configure/RULES_TARGET b/configure/RULES_TARGET new file mode 100755 index 000000000..0637a782b --- /dev/null +++ b/configure/RULES_TARGET @@ -0,0 +1,111 @@ +#************************************************************************* +# 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 +# +# RULES_TARGET +# +# This file is to be maintained by the community. +# +#----------------------------------------------------------------------- + +define TARGET_template +$(1)_$(2) += $$(if $$(strip $$($(1)_$(2)_$$(OS_CLASS))), \ + $$(subst -nil-,,$$($(1)_$(2)_$$(OS_CLASS))), \ + $$($(1)_$(2)_DEFAULT)) +endef + +$(foreach type, SRCS RCS OBJS LDFLAGS OBJLIBS LDOBJS SYS_LIBS , \ +$(foreach target, $(PROD) $(TESTPROD) $(LIBRARY) $(LOADABLE_LIBRARY) , \ + $(eval $(call TARGET_template,$(strip $(target)),$(type))))) + +#----------------------------------------------------------------------- + +# This define block requires GNU make 3.81 +define PROD_template +ifeq ($$(strip $$($(1)_OBJS) $$($(1)_SRCS) $$(PRODUCT_OBJS)),) +$(1)_OBJS = $(1)$$(OBJ) +endif +endef + +$(foreach target, $(PROD) $(TESTPROD), \ + $(eval $(call PROD_template,$(strip $(target))))) + +#----------------------------------------------------------------------- + +define TARGET2_template +$(1)_LDLIBS += $$($(1)_LIBS) +$(1)_LDLIBS += $$(if $$(strip $$($(1)_LIBS_$$(OS_CLASS))), \ + $$(subst -nil-,,$$($(1)_LIBS_$$(OS_CLASS))), \ + $$($(1)_LIBS_DEFAULT)) + +$(1)_RESS = $$(if $$(RES),$$(addsuffix $$(RES),$$(basename $$($(1)_RCS))),) +$(1)_OBJSNAME = $$(addsuffix $$(OBJ),$$(basename $$($(1)_OBJS) $$($(1)_SRCS) )) +$(1)_DEPLIBS = $$(foreach lib, $$($(1)_LDLIBS),\ + $$(firstword $$(wildcard $$(addsuffix /$$(LIB_PREFIX)$$(lib).*,\ + $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)))\ + $$(addsuffix /$$(LIB_PREFIX)$$(lib)$$(LIB_SUFFIX),\ + $$(firstword $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS))))) + +endef + +$(foreach target, $(PROD) $(TESTPROD) $(LIBRARY) $(LOADABLE_LIBRARY) , \ + $(eval $(call TARGET2_template,$(strip $(target))))) + +#----------------------------------------------------------------------- + +define PROD2_template + +$(1)$$(EXE): $$($(1)_OBJSNAME) $$($(1)_RESS) $$($(1)_DEPLIBS) +endef + +$(foreach target, $(PROD) $(TESTPROD), \ + $(eval $(call PROD2_template,$(strip $(target))))) + +#----------------------------------------------------------------------- + +define LIBRARY_template +BUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),) + +$(1)_DLL_DEPLIBS=$$(foreach lib, $$($(1)_DLL_LIBS),\ + $$(firstword $$(wildcard $$(addsuffix /$$(LIB_PREFIX)$$(lib).*,\ + $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)))\ + $$(addsuffix /$$(LIB_PREFIX)$$(lib)$$(LIB_SUFFIX),\ + $$(firstword $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS))))) + +$$(LIB_PREFIX)$(1)$$(LIB_SUFFIX):$$($(1)_OBJSNAME) $$($(1)_RESS) +$$(LIB_PREFIX)$(1)$$(LIB_SUFFIX):$$($(1)_DEPLIBS) +$$(SHRLIB_PREFIX)$(1)$$(SHRLIB_SUFFIX):$$($(1)_OBJSNAME) $$($(1)_RESS) +$$(SHRLIB_PREFIX)$(1)$$(SHRLIB_SUFFIX):$$($(1)_DEPLIBS) +$$(SHRLIB_PREFIX)$(1)$$(SHRLIB_SUFFIX):$$($(1)_DLL_DEPLIBS) +endef + +$(foreach target, $(LIBRARY), \ + $(eval $(call LIBRARY_template,$(strip $(target))))) + +#----------------------------------------------------------------------- + +define LOADABLE_LIBRARY_template +LOADABLE_BUILD_LIBRARY += $$(if $$(strip $$($(1)_OBJSNAME) $$(LIBRARY_OBJS)),$(1),) + +$(1)_DLL_DEPLIBS=$$(foreach lib, $$($(1)_DLL_LIBS),\ + $$(firstword $$(wildcard $$(addsuffix /$$(LIB_PREFIX)$$(lib).\*,\ + $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS)))\ + $$(addsuffix /$$(LIB_PREFIX)$$(lib)$$(LIB_SUFFIX),\ + $$(firstword $$($$(lib)_DIR) $$(SHRLIB_SEARCH_DIRS))))) + +$$(LOADABLE_SHRLIB_PREFIX)$(1)$$(LOADABLE_SHRLIB_SUFFIX):$$($(1)_OBJSNAME) $$($(1)_RESS) +$$(LOADABLE_SHRLIB_PREFIX)$(1)$$(LOADABLE_SHRLIB_SUFFIX):$$($(1)_DEPLIBS) +$$(LOADABLE_SHRLIB_PREFIX)$(1)$$(LOADABLE_SHRLIB_SUFFIX):$$($(1)_DLL_DEPLIBS) +endef + +$(foreach target, $(LOADABLE_LIBRARY), \ + $(eval $(call LOADABLE_LIBRARY_template,$(strip $(target))))) + diff --git a/configure/RULES_TOP b/configure/RULES_TOP new file mode 100644 index 000000000..2ebde5ea9 --- /dev/null +++ b/configure/RULES_TOP @@ -0,0 +1,85 @@ +#************************************************************************* +# 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 +# + +include $(CONFIG)/RULES_DIRS + +UNINSTALL_DIRS += $(INSTALL_DBD) $(INSTALL_INCLUDE) $(INSTALL_DOC)\ + $(INSTALL_HTML) $(INSTALL_JAVA) $(INSTALL_TEMPLATES) \ + $(INSTALL_DB) +UNINSTALL_DIRS += $(DIRECTORY_TARGETS) + +uninstallArchTargets = $(foreach arch,$(BUILD_ARCHS), uninstall$(DIVIDER)$(arch)) +archPart = $(word 2, $(subst $(DIVIDER), ,$@)) + +$(uninstallArchTargets): uninstallDirs + @$(RMDIR) $(INSTALL_LOCATION_BIN)/$(archPart) $(INSTALL_LOCATION_LIB)/$(archPart) + +cleandirs: +ifeq ($(wildcard $(INSTALL_LOCATION_BIN)/*),) + @$(RMDIR) $(INSTALL_LOCATION_BIN) +endif +ifeq ($(wildcard $(INSTALL_LOCATION_LIB)/*),) + @$(RMDIR) $(INSTALL_LOCATION_LIB) +endif + @echo +# The echo above stops a "nothing to be done for cleandirs" message + +distclean: realclean realuninstall + +CVSCLEAN=$(firstword $(wildcard $(TOOLS)/cvsclean.pl $(TOP)/src/tools/cvsclean.pl)) + +cvsclean: + @$(PERL) $(CVSCLEAN) + +realuninstall: + @$(RMDIR) $(INSTALL_LOCATION_BIN) $(INSTALL_LOCATION_LIB) + @$(RMDIR) $(UNINSTALL_DIRS) + +uninstall: $(addprefix uninstall$(DIVIDER),$(BUILD_ARCHS)) + @$(MAKE) -f Makefile cleandirs + +uninstallDirs: + @$(RMDIR) $(UNINSTALL_DIRS) + +help: + @echo "Usage: gnumake [options] [target] ..." + @echo "Targets supported by all Makefiles:" + @echo " install - Builds and installs all targets (default rule)" + @echo " all - Same as install" + @echo " buildInstall - Same as install" + @echo " clean - Removes the O. dirs created by running make" + @echo " In O. dir, clean removes build created files" + @echo " realclean - Removes ALL O. dirs" + @echo " Cannot be used within an O. dir" + @echo " rebuild - Same as clean install" + @echo " inc - Installs header files" + @echo " build - Builds all targets" + @echo " archclean - Removes O. dirs but not O.Common dir" + @echo "\"Partial\" build targets supported by Makefiles:" + @echo " inc. - Installs only header files." + @echo " install. - Builds and installs only." + @echo " clean. - Cleans binaries in O. dirs only." + @echo " build. - Builds only." + @echo "Targets supported by top level Makefile:" + @echo " uninstall - Cleans directories created by the install." + @echo " realuninstall - Removes ALL install dirs" + @echo " distclean - Same as realclean realuninstall." + @echo " cvsclean - Removes cvs .#* files in all dirs of directory tree" + @echo " help - Prints this list of valid make targets " + @echo "Indiv. object targets are supported by O. level Makefile .e.g" + @echo " xxxRecord.o" + +.PHONY : $(uninstallArchTargets) +.PHONY : uninstall help cleandirs distclean uninstallDirs realuninstall +.PHONY : cvsclean + diff --git a/configure/Sample.Makefile b/configure/Sample.Makefile new file mode 100755 index 000000000..a84ccbb45 --- /dev/null +++ b/configure/Sample.Makefile @@ -0,0 +1,216 @@ +#************************************************************************* +# 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. +#************************************************************************* +# Makefile for base/src/sample +# +# +# Sample Makefile showing some possible entries +# that are allowed using RULES_BUILD. +# + +TOP = ../../.. +include $(TOP)/configure/CONFIG + +# Add-on CPPFLAGS that are needed by this Makefile. +# (If possible, all system specific flags should be +# defined in configure/os/CONFIG.. +# +# These CPPFLAGS rules also apply to these Makefile-variables: +# CPPFLAGS C preprocessor flags +# CFLAGS C flags +# CXXFLAGS C++ flags +# LDFLAGS link flags +# +# This is used on all systems: +USR_CPPFLAGS = -DVAR=value -Ddefine_for_all_systems +# ..only for WIN32: +USR_CPPFLAGS_WIN32 = -DVERSION='WIN32 port' +# +# -nil- is special: +# if USR_CPPFLAGS_WIN32 was undefined or empty, .._DEFAULT would have +# been used. +# To indicate +# "yes, there is a special USR_CPPFLAGS for WIN32, but it's empty" +# you have to set it to -nil-: +USR_CPPFLAGS_WIN32 = -nil- +# .. for all other arch classes: +USR_CPPFLAGS_DEFAULT = -DVERSION='generic Unix' + +# CPPFLAGS that are only used to compile a_file.c or a_file.cpp: +# +a_file_CPPFLAGS = -DIN_A_FILE +a_file_CPPFLAGS_WIN32 = -DVERSION='WIN32 port' + +# --------------------------------------------------------- +# general rule for all .c .cpp .h .hh files and scripts: +# +# In here you supply just the filename without '../' etc. +# While building in an O.xxx subdir, the +# sources are extracted from the +# ../os/$(OS_CLASS) directory if it exists, or +# ../os/default directory if it exists, or +# .. directory +# --------------------------------------------------------- + +# includes to install from this Makefile +# +# again: if INC_$(OS_CLASS) is defined, it is added to INC, +# otherwise INC_DEFAULT (if defined) is added: +# +INC_DEFAULT = for_all_but_WIN32_or_vxWorks.h +INC_WIN32 = only_for_WIN32.h +INC_vxWorks = -nil- # vxWorks uses no special include +INC = file.h + +# -------------------------------------------------------------------- +# defining a library +# -------------------------------------------------------------------- +# +# Contents of a library are specified via SRCS, LIB_SRCS, or .._SRCS. +# From this the platform specific object names (.o, .obj, ...) +# are derived automatically. +# +# Platform specific objects: +# use .._OBJS_$(OS_CLASS) or .._OBJS_DEFAULT +# +# Platform specific files can also be put in +# separate os/OS_CLASS directories! +# +# For almost every file the seach order is: +# ./os/OS_CLASS +# ./os/generic +# . +# So usually only LIB_SRCS should be sufficient! + +# SRCS files will be used for both LIBRARY and PROD +SRCS = file_for_lib.c another_file.cpp +SRCS_DEFAULT = posix.c +SRCS_WIN32 = win32_special.c +SRCS_Linux = -nil- +# +libname_SRCS = file_for_lib.c another_file.cpp +libname_SRCS_DEFAULT = posix.c +libname_SRCS_WIN32 = win32_special.c +libname_SRCS_Linux = -nil- +# +# SRCS that are used for all libraries +LIB_SRCS = file_for_lib.c another_file.cpp +LIB_SRCS_DEFAULT = posix.c +LIB_SRCS_WIN32 = win32_special.c +LIB_SRCS_Linux = -nil- + +# Library to build: +# lib$(LIBRARY).a or ..dll/..exp/..lib +# +LIBRARY=libname +# +# Host or Ioc platform specific library to build: +# +LIBRARY_IOC=libnameIoc +LIBRARY_HOST=libnameHost + +# Library version +SHRLIB_VERSION = +# On WIN32 results in /version:$(SHRLIB_VERSION) link option +# On Unix type hosts .$(SHRLIB_VERSION) is appended to library name + +# -------------------------------------------------------------------- +# defining products (executable programs) +# -------------------------------------------------------------------- +# +# if SRCS is undefined, it defaults to $(PROD).c +SRCS=a.c b.c c.c + +# SRCS that are used for all PRODs +# +PROD_SRCS = ppp.c qqq.c + +# SRCS that are only used for PROD a_file +# +a_file_SRCS = aa.c bb.c + +# +# EPICS libs needed to link PROD, TESTPROD and sharable library +# +# note that DLL_LIBS (the libraries needed to link a shareable +# library) is created by default from the PROD/SYS libraries specified +# below minus the name of the sharable library (LIBRARY) +# +# +# ---------- libraries for a specific product pppp +# for all systems +pppp_LIBS = Com Ca +# for most systems: +pppp_LIBS_DEFAULT = mathlib +pppp_LIBS_WIN32 = -nil- + +# ---------- libraries for all products +# for all systems +PROD_LIBS = Com Ca +# for most systems: +PROD_LIBS_DEFAULT = mathlib +PROD_LIBS_WIN32 = -nil- + +# ---------- Libraries for all products and all libraries: +# for all systems +USR_LIBS = Xm Xt X11 +Xm_DIR = $(MOTIF_LIB) +Xt_DIR = $(X11_LIB) +X11_DIR = $(X11_LIB) + +# for most systems +USR_LIBS_DEFAULT = foolib +USR_LIBS_WIN32 = -nil- +foolib_DIR = $(FOO_LIB) + +# system libs needed to link PROD, TESTPROD and sharable library +# +# ---------- system libraries for all products +# for all systems: +PROD_SYS_LIBS = m +# for most systems: +PROD_SYS_LIBS_DEFAULT = foolib +PROD_SYS_LIBS_WIN32 = -nil- + +# Product, +# may be caRepeater.o -> caRepeater +# or caRepeater.obj -> caRepeater.exe +PROD = prod +PROD_DEFAULT = product_for_rest +PROD_WIN32 = product_only_for_WIN32 +PROD_Linux = product_only_for_Linux +PROD_solaris = product_only_for_solaris + +PROD_HOST = product_only_for_host_type_systems +PROD_IOC = product_only_for_ioc_type_systems + +# Product version +PROD_VERSION = +# On WIN32 results in /version:$(SHRLIB_VERSION) link option +# On Unix type hosts PROD_VERSION) is ignored + +# Scripts to install +# +# If there is both ../$(SCRIPTS) and ../$(OS_CLASS)/$(SCRIPTS), +# the latter, system specific version will be installed! +# +SCRIPTS_DEFAULT = script_for_rest +SCRIPTS_WIN32 = script_only_for_WIN32 +SCRIPTS_Linux = script_only_for_Linux +SCRIPTS = script + +# if you want to build products locally without installing: +# TESTPROD = test + +# put all definitions before the following include line +# put all rules after the following include line + +include $(TOP)/configure/RULES + +# EOF Makefile diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS new file mode 100644 index 000000000..e2d29b343 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS @@ -0,0 +1,117 @@ +# +# This file contains definitions for RTEMS builds +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# University of Saskatchewan +# eric.norum@usask.ca +# +# Contains definitions common to all RTEMS targets +# +# This file is maintained by the build community. +# Sites may override definitions in os/CONFIG_SITE.Common.RTEMS +#------------------------------------------------------- +# + +#------------------------------------------------------- +# RTEMS tools are similar to UNIX tools +-include $(CONFIG)/os/CONFIG.Common.UnixCommon + +GNU_TARGET_INCLUDE_DIR = +unexport GCC_EXEC_PREFIX + +#-------------------------------------------------- +# Get RTEMS_BASE definition +-include $(CONFIG)/os/CONFIG_SITE.Common.RTEMS + +ifneq ($(CONFIG),$(TOP)/configure) +-include $(TOP)/configure/CONFIG_SITE.Common.RTEMS +endif + +#------------------------------------------------------- +# Pick up the RTEMS tool/path definitions from the RTEMS BSP directory. +include $(RTEMS_BASE)/$(RTEMS_TARGET_CPU)-rtems$(RTEMS_VERSION)/$(subst RTEMS-,,$(T_A))/Makefile.inc +include $(RTEMS_CUSTOM) +include $(CONFIG.CC) + +#------------------------------------------------------- +# RTEMS cross-development tools +CC = $(RTEMS_TOOLS)/bin/$(CC_FOR_TARGET) $(GCCSPECS) -fasm +CCC = $(RTEMS_TOOLS)/bin/$(CXX) +CPP = $(RTEMS_TOOLS)/bin/$(CC_FOR_TARGET) -x c -E +AR = $(RTEMS_TOOLS)/bin/$(AR_FOR_TARGET) +LD = $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -r + +RANLIB := $(RTEMS_TOOLS)/bin/$(RANLIB) + +#------------------------------------------------------- +# Build types +VALID_BUILDS = Ioc + +#-------------------------------------------------- +# The RTEMS Makefiles redefine several macros, so we have to go +# through the following contortions to get the EPICS flags back. +CFLAGS = $(CROSS_CFLAGS) $(OPT_CFLAGS) $(DEBUG_CFLAGS)\ + $(WARN_CFLAGS) $(TARGET_CFLAGS) $(USR_CFLAGS) $(ARCH_DEP_CFLAGS)\ + $(CODE_CFLAGS) $(STATIC_CFLAGS) $(OP_SYS_CFLAGS) $(LIBRARY_SRC_CFLAGS)\ + $(HDEPENDS_CFLAGS) + +CXXFLAGS = $(CROSS_CXXFLAGS) $(OPT_CXXFLAGS)\ + $(DEBUG_CXXFLAGS) $(WARN_CXXFLAGS) $(TARGET_CXXFLAGS) $(USR_CXXFLAGS)\ + $(ARCH_DEP_CXXFLAGS) $(CODE_CXXFLAGS) $(STATIC_CXXCFLAGS) $(OP_SYS_CXXFLAGS)\ + $(LIBRARY_SRC_CFLAGS) $(HDEPENDS_CFLAGS) + +LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) \ + $(ARCH_DEP_LDFLAGS) $(DEBUG_LDFLAGS) $(OP_SYS_LDFLAGS) $(CROSS_LDFLAGS)\ + $(RUNTIME_LDFLAGS) $(CODE_LDFLAGS) + +LDLIBS = \ + $(POSIX_LDLIBS) $(ARCH_DEP_LDLIBS) $(DEBUG_LDLIBS) $(OP_SYS_LDLIBS) + +CPPFLAGS += $(CROSS_CPPFLAGS) $(POSIX_CPPFLAGS)\ +$(BASE_CPPFLAGS) $(TARGET_CPPFLAGS) $(USR_CPPFLAGS) $(ARCH_DEP_CPPFLAGS)\ + $(OP_SYS_CPPFLAGS) $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) + +#-------------------------------------------------- +# Although RTEMS uses gcc, it wants to use gcc its own way +CROSS_CPPFLAGS = +CROSS_LDFLAGS = +SHRLIB_CFLAGS = +OPT_CFLAGS_YES = $(CFLAGS_OPTIMIZE_V) +OPT_CXXFLAGS_YES = $(CFLAGS_OPTIMIZE_V) +OPT_CFLAGS_NO = $(CFLAGS_DEBUG_V) +OPT_CXXFLAGS_NO = $(CFLAGS_DEBUG_V) + +#-------------------------------------------------- +# operating system class (include/os/) +OS_CLASS = RTEMS + +#-------------------------------------------------- +# Operating system flags +OP_SYS_LDLIBS += -lrtemsCom -lc -lrtemscpu -lCom -lnfs -lm +OP_SYS_LDFLAGS += $(CPU_CFLAGS) -u Init \ + $(PROJECT_RELEASE)/lib/no-dpmem.rel \ + $(PROJECT_RELEASE)/lib/no-mp.rel \ + $(PROJECT_RELEASE)/lib/no-part.rel \ + $(PROJECT_RELEASE)/lib/no-signal.rel \ + $(PROJECT_RELEASE)/lib/no-rtmon.rel + +#-------------------------------------------------- +# RTEMS has neither shared libraries nor dynamic loading +STATIC_BUILD=YES +SHARED_LIBRARIES=NO +CODE_CFLAGS = +CODE_CXXFLAGS = + +#-------------------------------------------------- +# Override the usual RTEMS verbosity from ar +ARFLAGS = rc + +#-------------------------------------------------- +# Command-line input support +LDLIBS_LIBTECLA = -ltecla_r -lncurses +LDLIBS_READLINE = -lreadline -lncurses + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-at91rm9200ek b/configure/os/CONFIG.Common.RTEMS-at91rm9200ek new file mode 100644 index 000000000..6f5f97732 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-at91rm9200ek @@ -0,0 +1,13 @@ +# +# CONFIG.Common.RTEMS-at91rm9200ek +# Author: Ralf Hartmann +# BESSY +# Ralf.Hartmann@bessy.de +# +# Atmel AT91RM9200-EK evaluation kit +# using the AT91RM9200 ARM9-based 32-bit RISC microcontroller +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU=arm +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-beatnik b/configure/os/CONFIG.Common.RTEMS-beatnik new file mode 100644 index 000000000..f3260fc5f --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-beatnik @@ -0,0 +1,38 @@ +# +# CONFIG.Common.RTEMS-beatnik +# A migration from Eric Norum's CONFIG.Common.RTEMS-mvme5500 +# Author: Dayle Kotturi +# SLAC +# dayle@slac.stanford.edu +# +# All RTEMS targets use the same Makefile fragment +# +override EXE=.elf +RTEMS_TARGET_CPU = powerpc +GNU_TARGET=powerpc-rtems +ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL +ARCH_DEP_CFLAGS += -DHAVE_MOTLOAD + +MUNCH_SUFFIX = .boot +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +define MUNCH_CMD + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ +endef + +CROSS_COMPILER_TARGET_ARCHS=RTEMS-beatnik + +include $(CONFIG)/os/CONFIG.Common.RTEMS +RTEMSSYMS=$(PRODNAME:%$(EXE)=%.sym) +RTEMSIMGS=$(PRODNAME:%$(EXE)=%.bin) +INSTALL_RTEMSSYMS=$(RTEMSSYMS:%=$(INSTALL_BIN)/%) +INSTALL_RTEMSIMGS=$(RTEMSIMGS:%=$(INSTALL_BIN)/%) + +OP_SYS_LDLIBS += -lbspExt + +%.sym: %$(EXE) + $(XSYMS) $^ $@ + +%.bin: %$(EXE) + $(OBJCOPY) -Obinary $^ $@ + +#PRODTARGETS+=$(INSTALL_RTEMSSYMS) $(INSTALL_RTEMSIMGS) diff --git a/configure/os/CONFIG.Common.RTEMS-gen68360 b/configure/os/CONFIG.Common.RTEMS-gen68360 new file mode 100644 index 000000000..936928fdf --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-gen68360 @@ -0,0 +1,10 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# Canadian Light Source +# eric@cls.usask.ca +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU=m68k +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mcp750 b/configure/os/CONFIG.Common.RTEMS-mcp750 new file mode 100644 index 000000000..ea768f144 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-mcp750 @@ -0,0 +1,10 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# Canadian Light Source +# eric@cls.usask.ca +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU=ppc +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mvme167 b/configure/os/CONFIG.Common.RTEMS-mvme167 new file mode 100644 index 000000000..936928fdf --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-mvme167 @@ -0,0 +1,10 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# Canadian Light Source +# eric@cls.usask.ca +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU=m68k +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2100 b/configure/os/CONFIG.Common.RTEMS-mvme2100 new file mode 100644 index 000000000..064f2a01b --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-mvme2100 @@ -0,0 +1,29 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# Canadian Light Source +# eric@cls.usask.ca +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU = powerpc +ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL +ARCH_DEP_CFLAGS += -DHAVE_PPCBUG + +MUNCH_SUFFIX = .boot +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +define MUNCH_CMD + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems + gzip -f9 rtems + $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@ \ + $(PROJECT_RELEASE)/lib/bootloader.o \ + --just-symbols=$< \ + -b binary rtems.gz \ + -T $(PROJECT_RELEASE)/lib/ppcboot.lds \ + -Map $<.map + rm -f rtems.gz +endef + +OP_SYS_LDLIBS += -lbspExt + +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700 new file mode 100644 index 000000000..f45e321c4 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-mvme2700 @@ -0,0 +1,25 @@ +# +# Author: Matt Rippa +# +RTEMS_TARGET_CPU = powerpc +ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL +ARCH_DEP_CFLAGS += -DHAVE_PPCBUG +ARCH_DEP_CFLAGS += -DNVRAM_INDIRECT + +MUNCH_SUFFIX = .boot +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +define MUNCH_CMD + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems + gzip -f9 rtems + $(RTEMS_TOOLS)/bin/$(LD_FOR_TARGET) -o $@ \ + $(PROJECT_RELEASE)/lib/bootloader.o \ + --just-symbols=$< \ + -b binary rtems.gz \ + -T $(PROJECT_RELEASE)/lib/ppcboot.lds \ + -Map $<.map + rm -f rtems.gz +endef + +OP_SYS_LDLIBS += -lbspExt + +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-mvme3100 b/configure/os/CONFIG.Common.RTEMS-mvme3100 new file mode 100644 index 000000000..bea023374 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-mvme3100 @@ -0,0 +1,32 @@ +# +# All RTEMS targets use the same Makefile fragment +# +override EXE=.elf +RTEMS_TARGET_CPU = powerpc +GNU_TARGET=powerpc-rtems +ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL +ARCH_DEP_CFLAGS += -DHAVE_MOTLOAD + +MUNCH_SUFFIX = .boot +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +define MUNCH_CMD + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ +endef + +CROSS_COMPILER_TARGET_ARCHS=RTEMS-beatnik + +include $(CONFIG)/os/CONFIG.Common.RTEMS +RTEMSSYMS=$(PRODNAME:%$(EXE)=%.sym) +RTEMSIMGS=$(PRODNAME:%$(EXE)=%.bin) +INSTALL_RTEMSSYMS=$(RTEMSSYMS:%=$(INSTALL_BIN)/%) +INSTALL_RTEMSIMGS=$(RTEMSIMGS:%=$(INSTALL_BIN)/%) + +OP_SYS_LDLIBS += -lbspExt + +%.sym: %$(EXE) + $(XSYMS) $^ $@ + +%.bin: %$(EXE) + $(OBJCOPY) -Obinary $^ $@ + +#PRODTARGETS+=$(INSTALL_RTEMSSYMS) $(INSTALL_RTEMSIMGS) diff --git a/configure/os/CONFIG.Common.RTEMS-mvme5500 b/configure/os/CONFIG.Common.RTEMS-mvme5500 new file mode 100644 index 000000000..d28829987 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-mvme5500 @@ -0,0 +1,20 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# Advanced Photon Source +# norume@aps.anl.gov +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU = powerpc +ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL +ARCH_DEP_CFLAGS += -DHAVE_MOTLOAD +ARCH_DEP_CFLAGS += -DBSP_NVRAM_BASE_ADDR=0xf1110000 + +MUNCH_SUFFIX = .boot +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +define MUNCH_CMD + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ +endef + +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-pc386 b/configure/os/CONFIG.Common.RTEMS-pc386 new file mode 100644 index 000000000..a81237888 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-pc386 @@ -0,0 +1,25 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# Canadian Light Source +# eric@cls.usask.ca +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU=i386 + +MUNCH_SUFFIX = .boot +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +define MUNCH_CMD + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< temp.bin + $(PROJECT_RELEASE)/build-tools/bin2boot $@ 0x00097E00 \ + $(PROJECT_RELEASE)/lib/start16.bin 0x00097C00 0 temp.bin 0x00100000 0 + rm -f temp.bin +endef + +include $(CONFIG)/os/CONFIG.Common.RTEMS + +# +# Put text segment where it will work with etherboot +# +OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 diff --git a/configure/os/CONFIG.Common.RTEMS-psim b/configure/os/CONFIG.Common.RTEMS-psim new file mode 100644 index 000000000..7a8343d72 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-psim @@ -0,0 +1,10 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# University of Saskatchewan +# eric.norum@usask.ca +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU=ppc +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-uC5282 b/configure/os/CONFIG.Common.RTEMS-uC5282 new file mode 100644 index 000000000..cb31bfcc2 --- /dev/null +++ b/configure/os/CONFIG.Common.RTEMS-uC5282 @@ -0,0 +1,18 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Author: W. Eric Norum +# Canadian Light Source +# eric@cls.usask.ca +# +# All RTEMS targets use the same Makefile fragment +# +RTEMS_TARGET_CPU = m68k +ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL + +MUNCH_SUFFIX = .boot +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +define MUNCH_CMD + $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $@ +endef + +include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.UnixCommon b/configure/os/CONFIG.Common.UnixCommon new file mode 100644 index 000000000..c929e08e0 --- /dev/null +++ b/configure/os/CONFIG.Common.UnixCommon @@ -0,0 +1,94 @@ +# CONFIG.Common.UnixCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Contains definitions common to all Unix target archs +# +# This file is maintained by the build community. +# Sites may override definitions in CONFIG_SITE.Common.UnixCommon +# or CONFIG_SITE..UnixCommon +#------------------------------------------------------- + +# Unix valid build types +VALID_BUILDS = Host Ioc + +#------------------------------------------------------- +# Unix prefix and suffix definitions +EXE = +OBJ = .o +#Library prefix and suffixes +LIB_PREFIX = lib +LIB_SUFFIX = .a +SHRLIB_SUFFIX = .so$(addprefix .,$(SHRLIB_VERSION)) +LOADABLE_SHRLIB_SUFFIX = .so$(addprefix .,$(LOADABLE_SHRLIB_VERSION)) +LOADABLE_SHRLIB_PREFIX = lib + +#------------------------------------------------------- +# names of libraries to build +# -> lib.a +LIBNAME = $(BUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) +# -> lib.so. +SHRLIBNAME_YES = $(BUILD_LIBRARY:%=$(SHRLIB_PREFIX)%$(SHRLIB_SUFFIX)) +LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=$(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX)) + +#------------------------------------------------------- +# shrlib: SHRLIB_DEPLIBS, SHRLIB_LDLIBS and SHRLIBDIR_LDFLAGS definitions + +# SHRLIB_LIBS deprecated +LIB_LIBS += $(SHRLIB_LIBS) + +SHRLIB_DEPLIBS=$(foreach lib, $(LIB_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib).*, \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +SHRLIB_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(LIB_LIBS) $(USR_LIBS))\ + $(STATIC_LDLIBS) \ + $(addprefix -l, $($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS)) \ + $(LDLIBS) + +SHRLIB_DEPLIB_DIRS = $(foreach word,$(sort $(dir $($*_DEPLIBS) $(SHRLIB_DEPLIBS))), \ + $(shell $(PERL) $(TOOLS)/fullPathName.pl $(word))) + +SHRLIBDIR_LDFLAGS += $(SHRLIB_DEPLIB_DIRS:%=-L%) + +#------------------------------------------------------- +# Prod: PROD_DEPLIBS, PROD_LDLIBS and PRODDIR_LDFLAGS definitions + +PROD_DEPLIBS=$(foreach lib,$(PROD_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib).*, \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +PROD_LDLIBS = $(addprefix -l, $($*_LDLIBS) $(PROD_LIBS) $(USR_LIBS)) \ + $(STATIC_LDLIBS) \ + $(addprefix -l, $($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS)) + +LDLIBS_STATIC_YES = LDLIBS +LDLIBS_SHARED_NO = LDLIBS +PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \ + $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) + +PROD_DEPLIB_DIRS = $(foreach word,$(sort $(dir $($*_DEPLIBS) $(PROD_DEPLIBS))), \ + $(shell $(PERL) $(TOOLS)/fullPathName.pl $(word))) + +PRODDIR_LDFLAGS += $(PROD_DEPLIB_DIRS:%=-L%) + +#-------------------------------------------------- +# Link definitions +LINK.cpp = $(CCC) -o $@ $(STATIC_LDFLAGS) $(PRODDIR_LDFLAGS) $(LDFLAGS) +LINK.cpp += $(PROD_LDFLAGS) $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS) +LINK.shrlib = $(CCC) -o $@ $(TARGET_LIB_LDFLAGS) $(SHRLIBDIR_LDFLAGS) $(LDFLAGS) +LINK.shrlib += $(LIB_LDFLAGS) $(LIBRARY_LD_OBJS) $(LIBRARY_LD_RESS) $(SHRLIB_LDLIBS) + +#-------------------------------------------------- +# Operating system definitions +OP_SYS_CPPFLAGS += -DUNIX +OP_SYS_LDLIBS += -lm + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.Common.UnixCommon +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).UnixCommon diff --git a/configure/os/CONFIG.Common.aix-ppc b/configure/os/CONFIG.Common.aix-ppc new file mode 100644 index 000000000..683eb8c15 --- /dev/null +++ b/configure/os/CONFIG.Common.aix-ppc @@ -0,0 +1,24 @@ +# CONFIG.Common.aix-ppc +# +# This file is maintained by the build community. +# +# Definitions for aix-ppc target builds +# Sites may override these definitions in CONFIG_SITE.Common.aix-ppc +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +# +# Set OS-specific information +# +OS_CLASS = AIX +ARCH_CLASS = ppc + +CODE_CPPFLAGS = -D_REENTRANT + +POSIX_CPPFLAGS = -D_POSIX_C_SOURCE=199506L -D_POSIX_THREADS -D_XOPEN_SOURCE=500 -D_ALL_SOURCE +POSIX_LDLIBS = -lpthread + +#SHARED_LIBRARIES=NO + diff --git a/configure/os/CONFIG.Common.aix-ppc-gnu b/configure/os/CONFIG.Common.aix-ppc-gnu new file mode 100644 index 000000000..38488c9b3 --- /dev/null +++ b/configure/os/CONFIG.Common.aix-ppc-gnu @@ -0,0 +1,11 @@ +# CONFIG.Common.aix-ppc-gnu +# +# This file is maintained by the build community. +# +# Definitions for aix-ppc target builds +# Sites may override these definitions in CONFIG_SITE.Common.aix-ppc +#------------------------------------------------------- + +# Include definitions common to all aix-ppc target archs +include $(CONFIG)/os/CONFIG.Common.aix-ppc + diff --git a/configure/os/CONFIG.Common.cygwin-x86 b/configure/os/CONFIG.Common.cygwin-x86 new file mode 100644 index 000000000..4c2598620 --- /dev/null +++ b/configure/os/CONFIG.Common.cygwin-x86 @@ -0,0 +1,77 @@ +# CONFIG.Common.cygwin-x86 +# +# Revision-Id: jba@aps.anl.gov-20101022143344-u0445dydxhg80mru +# This file is maintained by the build community. +# +# Definitions for cygwin-x86 target builds +# Sites may override these definitions in CONFIG_SITE.Common.cygwin-x86 +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = cygwin32 +ARCH_CLASS = x86 + +# Definitions used when COMMANDLINE_LIBRARY is READLINE +LDLIBS_READLINE = -lreadline -lcurses + +POSIX_CPPFLAGS = -D_POSIX_THREADS -D_POSIX_TIMERS +#POSIX_CPPFLAGS += -D_POSIX_SOURCE +POSIX_LDLIBS += -lpthread + +ARCH_DEP_CFLAGS += -m32 +ARCH_DEP_LDFLAGS += -m32 + +# Compiler defines _X86_ 1 +# Compiler defines __MSVCRT__ 1 +# Compiler defines __CYGWIN__ 1 +# Compiler defines __CYGWIN32__ 1 +# Compiler defines __unix__ 1 +# Compiler defines __unix 1 +# Compiler defines unix 1 + +OP_SYS_CPPFLAGS += -DCYGWIN32 + +EXE=.exe + +VISC_DLL_NO = -DEPICS_DLL_NO +VISC_DLL_YES = +VISC_DLL = $(VISC_DLL_$(SHARED_LIBRARIES)) +STATIC_CFLAGS_YES= $(VISC_DLL) +STATIC_CFLAGS_NO= $(VISC_DLL) -D_DLL +STATIC_CXXFLAGS_YES= $(VISC_DLL) +STATIC_CXXFLAGS_NO= $(VISC_DLL) -D_DLL + +# adjust names of libraries to build +# +# But: if there are no objects LIBRARY_LD_OBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +SHRLIB_PREFIX= +SHRLIB_SUFFIX=.dll +SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) +LOADABLE_SHRLIB_PREFIX= +LOADABLE_SHRLIB_SUFFIX=.dll +LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(LOADABLE_SHRLIB_SUFFIX)) + +# +# When SHARED_LIBRARIES is YES we are building a DLL link library +# and when SHARED_LIBRARIES is NO we are building an object library +# +LIB_PREFIX= +LIB_SUFFIX=.lib +DLL_LINK_LIBNAME_YES = $(BUILD_LIBRARY:%=%$(LIB_SUFFIX)) +DLL_LINK_LIBNAME = $(DLL_LINK_LIBNAME_$(SHARED_LIBRARIES)) +LIBNAME_NO = $(BUILD_LIBRARY:%=%$(LIB_SUFFIX)) +LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) + +# dll install location +INSTALL_SHRLIB = $(INSTALL_BIN) + + +# Cygwin supports the sunrpc package in versions before 1.7. +# Cygwin supports the tirpc (Transport Independent RPC) package in versions 1.7 and later. +# uname -r return a string like "1.76(0230/5/3)" +CYGWIN_RPC_LIB= $(if $(findstring 1.5,$(shell uname -r)),rpc,tirpc) + diff --git a/configure/os/CONFIG.Common.darwin-ppc b/configure/os/CONFIG.Common.darwin-ppc new file mode 100644 index 000000000..f604672d8 --- /dev/null +++ b/configure/os/CONFIG.Common.darwin-ppc @@ -0,0 +1,15 @@ +# CONFIG.Common.darwin-ppc +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for darwin-ppc target builds +# Sites may override these definitions in CONFIG_SITE.Common.darwin-ppc +#------------------------------------------------------- + +# +# To build universal binaries, configure ARCH_CLASS +# in the file CONFIG_SITE.Common.darwin-ppc + +# Include definitions common to all Darwin targets +include $(CONFIG)/os/CONFIG.darwinCommon.darwinCommon diff --git a/configure/os/CONFIG.Common.darwin-ppcx86 b/configure/os/CONFIG.Common.darwin-ppcx86 new file mode 100644 index 000000000..6fc1d3d88 --- /dev/null +++ b/configure/os/CONFIG.Common.darwin-ppcx86 @@ -0,0 +1,15 @@ +# CONFIG.Common.darwin-ppcx86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for Darwin universal PowerPC + x86 target builds +# Sites may override these definitions in CONFIG_SITE.Common.darwin-ppcx86 +#------------------------------------------------------- + +# +# To build universal binaries, configure ARCH_CLASS +# in the file CONFIG_SITE.Common.darwin-ppcx86 + +# Include definitions common to all Darwin targets +include $(CONFIG)/os/CONFIG.darwinCommon.darwinCommon diff --git a/configure/os/CONFIG.Common.darwin-x86 b/configure/os/CONFIG.Common.darwin-x86 new file mode 100644 index 000000000..1b14f0369 --- /dev/null +++ b/configure/os/CONFIG.Common.darwin-x86 @@ -0,0 +1,15 @@ +# CONFIG.Common.darwin-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for darwin-x86 target builds +# Sites may override these definitions in CONFIG_SITE.Common.darwin-x86 +#------------------------------------------------------- + +# +# To build universal binaries, configure ARCH_CLASS +# in the file CONFIG_SITE.Common.darwin-x86 + +# Include definitions common to all Darwin targets +include $(CONFIG)/os/CONFIG.darwinCommon.darwinCommon diff --git a/configure/os/CONFIG.Common.freebsd-x86 b/configure/os/CONFIG.Common.freebsd-x86 new file mode 100644 index 000000000..04f4d6ceb --- /dev/null +++ b/configure/os/CONFIG.Common.freebsd-x86 @@ -0,0 +1,34 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file is maintained by the build community. +# +# Definitions for freebsd-x86 target builds +# Sites may override these definitions in CONFIG_SITE.Common.freebsd-x86 +#------------------------------------------------------- + +# Include definitions common to all freebsd targets +include $(CONFIG)/os/CONFIG.Common.freebsdCommon + +ARCH_CLASS = x86 + +ARCH_DEP_CPPFLAGS += -D_X86_ + +ifeq ($(BUILD_CLASS),CROSS) +ifeq ($(EPICS_HOST_ARCH),freebsd-x86) + # Added for 386,486,... cross builds + CMPLR_PREFIX= + CROSS_INCLUDES= + CROSS_LDFLAGS= + # Use -w not -Wall + #WARN_CFLAGS_YES = -w + #WARN_CXXFLAGS_YES = -w +-include $(CONFIG)/os/CONFIG_SITE.Common.freebsd-x86 +-include $(CONFIG)/os/CONFIG.freebsd-x86.freebsd-x86 +-include $(CONFIG)/os/CONFIG_SITE.freebsd-x86.freebsd-x86 +else + GNU_TARGET=i586-pc-freebsd-gnu + CMPLR_SUFFIX= + CMPLR_PREFIX=$(addsuffix -,$(GNU_TARGET)) +endif +endif + diff --git a/configure/os/CONFIG.Common.freebsd-x86_64 b/configure/os/CONFIG.Common.freebsd-x86_64 new file mode 100644 index 000000000..2b88e126b --- /dev/null +++ b/configure/os/CONFIG.Common.freebsd-x86_64 @@ -0,0 +1,34 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file is maintained by the build community. +# +# Definitions for freebsd-x86_64 target builds +# Sites may override these definitions in CONFIG_SITE.Common.freebsd-x86_64 +#------------------------------------------------------- + +# Include definitions common to all freebsd targets +include $(CONFIG)/os/CONFIG.Common.freebsdCommon + +ARCH_CLASS = x86_64 + +ARCH_DEP_CPPFLAGS += -D_X86_64_ + +ifeq ($(BUILD_CLASS),CROSS) +ifeq ($(EPICS_HOST_ARCH),freebsd-x86_64) + # Added for 386,486,... cross builds + CMPLR_PREFIX= + CROSS_INCLUDES= + CROSS_LDFLAGS= + # Use -w not -Wall + #WARN_CFLAGS_YES = -w + #WARN_CXXFLAGS_YES = -w +-include $(CONFIG)/os/CONFIG_SITE.Common.freebsd-x86_64 +-include $(CONFIG)/os/CONFIG.freebsd-x86_64.freebsd-x86_64 +-include $(CONFIG)/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 +else + GNU_TARGET=i586-pc-freebsd-gnu + CMPLR_SUFFIX= + CMPLR_PREFIX=$(addsuffix -,$(GNU_TARGET)) +endif +endif + diff --git a/configure/os/CONFIG.Common.freebsdCommon b/configure/os/CONFIG.Common.freebsdCommon new file mode 100644 index 000000000..10da6e80d --- /dev/null +++ b/configure/os/CONFIG.Common.freebsdCommon @@ -0,0 +1,39 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file is maintained by the build community. +# +# Definitions for freebsd target builds +# Sites may override these definitions in CONFIG_SITE.Common.freebsdCommon +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = freebsd + +CODE_CPPFLAGS = -D_REENTRANT + +POSIX_CPPFLAGS = -D_POSIX_THREADS +POSIX_LDLIBS = -lpthread + +# -D_BSD_SOURCE for gethostname() in unistd.h as needed by cacChannelIO.cpp. +OP_SYS_CPPFLAGS += -D_BSD_SOURCE +OP_SYS_CPPFLAGS += -Dfreebsd + +# Set runtime path for shared libraries +SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%) +SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +# Set runtime path for products +PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%) +PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +# Definitions used when COMMANDLINE_LIBRARY is READLINE +LDLIBS_READLINE = -lreadline -lcurses + +GNU_LDLIBS_YES = -lgcc_pic + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.Common.freebsdCommon +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).freebsdCommon diff --git a/configure/os/CONFIG.Common.ios-arm b/configure/os/CONFIG.Common.ios-arm new file mode 100644 index 000000000..b6f1a57b8 --- /dev/null +++ b/configure/os/CONFIG.Common.ios-arm @@ -0,0 +1,22 @@ +# CONFIG.Common.ios-arm +# +# This file is maintained by the build community. +# +# Definitions for ios-arm target builds +#------------------------------------------------------- + +IOS_PLATFORM = iPhoneOS + +# +# Architecture-specific information +# +ARCH_CLASS = armv6 + +OP_SYS_CFLAGS += -fno-inline-functions + +# iOS optimization flags for arm architecture +OPT_CFLAGS_YES = -O2 +OPT_CXXFLAGS_YES = -O2 + +# Include definitions common to all iphone targets +include $(CONFIG)/os/CONFIG.Common.iosCommon diff --git a/configure/os/CONFIG.Common.ios-x86 b/configure/os/CONFIG.Common.ios-x86 new file mode 100644 index 000000000..c236e606c --- /dev/null +++ b/configure/os/CONFIG.Common.ios-x86 @@ -0,0 +1,16 @@ +# CONFIG.Common.ios-x86 +# +# This file is maintained by the build community. +# +# Definitions for ios-x86 target builds +#------------------------------------------------------- + +IOS_PLATFORM = iPhoneSimulator + +# +# Architecture-specific information +# +ARCH_CLASS = i386 + +# Include definitions common to all iOS targets +include $(CONFIG)/os/CONFIG.Common.iosCommon diff --git a/configure/os/CONFIG.Common.iosCommon b/configure/os/CONFIG.Common.iosCommon new file mode 100644 index 000000000..6df8410a8 --- /dev/null +++ b/configure/os/CONFIG.Common.iosCommon @@ -0,0 +1,86 @@ +# CONFIG.Common.iosCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for iOS target archs +# Sites may override these definitions in CONFIG_SITE.Common.iosCommon +# or CONFIG_SITE..iosCommon +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +#------------------------------------------------------- +# Valid build types +VALID_BUILDS = Ioc + +#------------------------------------------------------- +# operating system class (include/os/) +OS_CLASS = iOS + +#-------------------------------------------------- +# GNU and SDK directories +GNU_DIR = $(PLATFORM_DIR)/Developer/usr +SDK_DIR = $(PLATFORM_DIR)/Developer/SDKs/$(IOS_PLATFORM)$(IOS_VERSION).sdk + +#------------------------------------------------------- +# Build architecture flags +ARCH_DEP_CFLAGS += -arch $(ARCH_CLASS) +ARCH_DEP_LDFLAGS += -arch $(ARCH_CLASS) + +#-------------------------------------------------- +# Operating system flags +OP_SYS_CFLAGS += -isysroot $(SDK_DIR) -D__IPHONE_OS_VERSION_MIN_REQUIRED=30200 + +#-------------------------------------------------- +# Don't try to use precompiled headers when converting sequencer files +CPPSNCFLAGS += -no-cpp-precomp + +#-------------------------------------------------- +# Always compile in debugging symbol table information +# +OPT_CFLAGS_YES += -g +OPT_CXXFLAGS_YES += -g + +#------------------------------------------------------- +# Compiler definitions: +# Use clang instead of gcc +# Must use g++ still +CC = $(GNU_BIN)/clang +CCC = $(GNU_BIN)/c++ + +#------------------------------------------------------- +# Linker flags +OP_SYS_LDFLAGS += -dynamic -Z -L$(SDK_DIR)/usr/lib + +#------------------------------------------------------- +# Shared libraries +SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION) +SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \ + -install_name $(shell perl $(TOOLS)/fullPathName.pl $(INSTALL_LIB))/$@ \ + -compatibility_version $(EPICS_VERSION).$(EPICS_REVISION) \ + -current_version $(SHRLIB_VERSION) +SHRLIB_SUFFIX = .$(SHRLIB_VERSION).dylib + +LOADABLE_SHRLIB_LDFLAGS = -bundle -flat_namespace -undefined suppress + +#-------------------------------------------------- +# code flags +CODE_CFLAGS = -fno-common -Wno-unused-value +CODE_CXXFLAGS = -fno-common + +# +# Add support for Objective-C source +# +vpath %.m $(USR_VPATH) $(ALL_SRC_DIRS) +%.o: %.m + $(COMPILE.c) -c $< + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.Common.iosCommon +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).iosCommon diff --git a/configure/os/CONFIG.Common.linux-386 b/configure/os/CONFIG.Common.linux-386 new file mode 100644 index 000000000..dc3825f22 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-386 @@ -0,0 +1,22 @@ +# CONFIG.Common.linux-386 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-386 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-386 +#------------------------------------------------------- + +# Include definitions common to all linux x86 targets +include $(CONFIG)/os/CONFIG.Common.linux-x86 + +ARCH_DEP_CFLAGS += -march=i386 + +ifeq ($(BUILD_CLASS),CROSS) + VALID_BUILDS = Ioc +endif + +# If your crosscompiler name has a GNU target prefix like -gcc, +# e.g. i386-pc-linux-gnu-gcc, put a GNU_TARGET definition in +# CONFIG_SITE..linux-386 file, e.g. GNU_TARGET=i386-pc-linux-gnu + diff --git a/configure/os/CONFIG.Common.linux-486 b/configure/os/CONFIG.Common.linux-486 new file mode 100644 index 000000000..6a39e52db --- /dev/null +++ b/configure/os/CONFIG.Common.linux-486 @@ -0,0 +1,22 @@ +# CONFIG.Common.linux-486 +# +# $Id +# This file is maintained by the build community. +# +# Definitions for linux-486 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-486 +#------------------------------------------------------- + +# Include definitions common to all linux x86 targets +include $(CONFIG)/os/CONFIG.Common.linux-x86 + +ARCH_DEP_CFLAGS += -march=i486 + +ifeq ($(BUILD_CLASS),CROSS) + VALID_BUILDS = Ioc +endif + +# If your crosscompiler name has a GNU target prefix like -gcc, +# e.g. i486-pc-linux-gnu-gcc, put a GNU_TARGET definition in +# CONFIG_SITE..linux-486 file, e.g. GNU_TARGET=i486-pc-linux-gnu + diff --git a/configure/os/CONFIG.Common.linux-586 b/configure/os/CONFIG.Common.linux-586 new file mode 100644 index 000000000..8b0538743 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-586 @@ -0,0 +1,24 @@ +# CONFIG.Common.linux-586 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-586 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-586 +#------------------------------------------------------- + +# Include definitions common to all linux x86 targets +include $(CONFIG)/os/CONFIG.Common.linux-x86 + +# i586 is equivalent to pentium +ARCH_DEP_CFLAGS += -march=i586 + +ifeq ($(BUILD_CLASS),CROSS) + VALID_BUILDS = Ioc +endif + +# If your crosscompiler name has a GNU target prefix like -gcc, +# e.g. i586-pc-linux-gnu-gcc, put a GNU_TARGET definition in +# CONFIG_SITE..linux-586 file, e.g. GNU_TARGET=i586-pc-linux-gnu + + diff --git a/configure/os/CONFIG.Common.linux-686 b/configure/os/CONFIG.Common.linux-686 new file mode 100644 index 000000000..2145fc3b8 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-686 @@ -0,0 +1,23 @@ +# CONFIG.Common.linux-686 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-686 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-686 +#------------------------------------------------------- + +# Include definitions common to all linux x86 targets +include $(CONFIG)/os/CONFIG.Common.linux-x86 + +# i686 is euivalent to pentiumpro +ARCH_DEP_CFLAGS += -march=i686 + +ifeq ($(BUILD_CLASS),CROSS) + VALID_BUILDS = Ioc +endif + +# If your crosscompiler name has a GNU target prefix like -gcc, +# e.g. i686-pc-linux-gnu-gcc, put a GNU_TARGET definition in +# CONFIG_SITE..linux-686 file, e.g. GNU_TARGET=i686-pc-linux-gnu + diff --git a/configure/os/CONFIG.Common.linux-arm b/configure/os/CONFIG.Common.linux-arm new file mode 100644 index 000000000..8b0a8c2a4 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-arm @@ -0,0 +1,39 @@ +# CONFIG.Common.linux-arm +# +# This file is maintained by the build community. +# +# Definitions for linux-arm target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-arm +#------------------------------------------------------- + +# Include definitions common to all Linux targets +include $(CONFIG)/os/CONFIG.Common.linuxCommon + +ARCH_CLASS = arm + +# Set a special definition for network order of Netwinder ARM floating point +ARCH_DEP_CPPFLAGS += -D_ARM_NWFP_ + +ifeq ($(BUILD_CLASS),CROSS) + VALID_BUILDS = Ioc + GNU_TARGET = arm-linux + + # prefix of compiler tools + CMPLR_SUFFIX = + CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) + + # Provide a link-time path for shared libraries + SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath-link,%) + SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + + # Provide a link-time path for products + PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath-link,%) + PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + + # Provide a link-time path for readline + RUNTIME_LDFLAGS_READLINE_YES = -Wl,-rpath-link,$(GNU_DIR)/lib + RUNTIME_LDFLAGS_READLINE = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) + RUNTIME_LDFLAGS_READLINE_CURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) + RUNTIME_LDFLAGS_READLINE_NCURSES = $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) + +endif diff --git a/configure/os/CONFIG.Common.linux-arm_eb b/configure/os/CONFIG.Common.linux-arm_eb new file mode 100644 index 000000000..c02f6392e --- /dev/null +++ b/configure/os/CONFIG.Common.linux-arm_eb @@ -0,0 +1,11 @@ +# CONFIG.Common.linux-arm_eb +# +# This file is maintained by the build community. +# +# Definitions for linux-arm_eb (big endian) target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-arm_eb +#------------------------------------------------------- + +# Include definitions common to all Linux-arm targets +include $(CONFIG)/os/CONFIG.Common.linux-arm + diff --git a/configure/os/CONFIG.Common.linux-arm_el b/configure/os/CONFIG.Common.linux-arm_el new file mode 100644 index 000000000..e076f92ad --- /dev/null +++ b/configure/os/CONFIG.Common.linux-arm_el @@ -0,0 +1,11 @@ +# CONFIG.Common.linux-arm_el +# +# This file is maintained by the build community. +# +# Definitions for linux-arm_el (little endian) target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-arm_el +#------------------------------------------------------- + +# Include definitions common to all linux-arm targets +include $(CONFIG)/os/CONFIG.Common.linux-arm + diff --git a/configure/os/CONFIG.Common.linux-athlon b/configure/os/CONFIG.Common.linux-athlon new file mode 100644 index 000000000..51fd07e04 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-athlon @@ -0,0 +1,22 @@ +# CONFIG.Common.linux-athlon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-athlon target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-athlon +#------------------------------------------------------- + +# Include definitions common to all linux x86 targets +include $(CONFIG)/os/CONFIG.Common.linux-x86 + +ARCH_DEP_CFLAGS += -march=athlon-mp -mfpmath=sse + +ifeq ($(BUILD_CLASS),CROSS) + VALID_BUILDS = Ioc +endif + +# If your crosscompiler name has a GNU target prefix like -gcc, +# e.g. athlon-pc-linux-gnu-gcc, put a GNU_TARGET definition in +# CONFIG_SITE..linux-athlon file, e.g. GNU_TARGET=athlon-pc-linux-gnu + diff --git a/configure/os/CONFIG.Common.linux-cris b/configure/os/CONFIG.Common.linux-cris new file mode 100644 index 000000000..d419b23e8 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-cris @@ -0,0 +1,58 @@ +# CONFIG.Common.linux-cris +# +# Author: Peter Zumbruch +# GSI +# P.Zumbruch@gsi.de +# +# Definitions for linux-cris target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-cris +#------------------------------------------------------- + +# Include definitions common to all linux targets +include $(CONFIG)/os/CONFIG.Common.linuxCommon + +ARCH_CLASS = cris + +ifeq ($(BUILD_CLASS),CROSS) + GNU_TARGET = cris-axis-linux-gnu + + # prefix of compiler tools + CMPLR_SUFFIX = + CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) + + # CROSS_TOP_DIR + # usually AXIS_TOP_DIR is defined via + # the init_env script of the SDK provided by Axis + # + ## AXIS_TOP_DIR defined? Make missing mandatory variable visible + AXIS_TOP_DIR?=UNDEFINED_ENV__AXIS_TOP_DIR + AXIS_SDK_DIR?=$(AXIS_TOP_DIR) + + # CROSS_INCLUDES + AXIS_SDK_TARGET_INCLUDE_DIR = $(AXIS_SDK_DIR)/target/$(GNU_TARGET)/include + AXIS_SDK_TARGET_INCLUDE_DIR +=$(AXIS_SDK_DIR)/target/$(GNU_TARGET)/usr/include + + CROSS_INCLUDES = $(addprefix -isystem ,$(AXIS_SDK_TARGET_INCLUDE_DIR)) + + # CROSS_LDFLAGS + AXIS_SDK_TARGET_LIB_DIR = $(AXIS_SDK_DIR)/target/$(GNU_TARGET)/lib + AXIS_SDK_TARGET_LIB_DIR += $(AXIS_SDK_DIR)/target/$(GNU_TARGET)/usr/lib + + CROSS_LDFLAGS = $(addprefix -L,$(AXIS_SDK_TARGET_LIB_DIR)) + +-include $(CONFIG)/os/CONFIG_SITE.Common.linux-cris +ifeq ($(EPICS_HOST_ARCH), linux-x86) +-include $(CONFIG)/os/CONFIG.linux-x86.linux-cris +-include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-cris +endif +endif + +SHARED_LIBRARIES=NO +STATIC_BUILD=YES + +ARCH_DEP_CFLAGS += -mno-mul-bug-workaround +OP_SYS_CFLAGS += -mlinux +ARCH_DEP_CPPFLAGS += -D_cris_ -mlinux + +#uncomment CRIS_COMPILER_DEBUG for debugging cris-compiled code +#CRIS_COMPILER_DEBUG diff --git a/configure/os/CONFIG.Common.linux-ppc b/configure/os/CONFIG.Common.linux-ppc new file mode 100644 index 000000000..5fb9d9fb3 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-ppc @@ -0,0 +1,15 @@ +# CONFIG.Common.linux-ppc +# +# This file is maintained by the build community. +# +# Definitions for linux-ppc target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-ppc +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.linuxCommon + +ARCH_CLASS = ppc + +ARCH_DEP_CPPFLAGS += -D_ppc_ + diff --git a/configure/os/CONFIG.Common.linux-ppc64 b/configure/os/CONFIG.Common.linux-ppc64 new file mode 100644 index 000000000..5bf9095f1 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-ppc64 @@ -0,0 +1,18 @@ +# CONFIG.Common.linux-ppc64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-ppc64 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-ppc64 +#------------------------------------------------------- + +# Include definitions common to all linux targets +include $(CONFIG)/os/CONFIG.Common.linuxCommon + +ARCH_CLASS = ppc64 + +ARCH_DEP_CPPFLAGS += -D_ppc_64_ +ARCH_DEP_CFLAGS += -m64 +ARCH_DEP_LDFLAGS += -m64 + diff --git a/configure/os/CONFIG.Common.linux-x86 b/configure/os/CONFIG.Common.linux-x86 new file mode 100644 index 000000000..7da75fc7e --- /dev/null +++ b/configure/os/CONFIG.Common.linux-x86 @@ -0,0 +1,22 @@ +# CONFIG.Common.linux-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-x86 +#------------------------------------------------------- + +# Include definitions common to all linux targets +include $(CONFIG)/os/CONFIG.Common.linuxCommon + +ARCH_CLASS = x86 + +ARCH_DEP_CPPFLAGS += -D_X86_ +ARCH_DEP_CFLAGS += -m32 +ARCH_DEP_LDFLAGS += -m32 + +# If your crosscompiler name has a GNU target prefix like -gcc, +# e.g. x86-redhat-linux-gcc, put a GNU_TARGET definition in +# CONFIG_SITE..linux-x86 file, e.g. GNU_TARGET=x86-redhat-linux + diff --git a/configure/os/CONFIG.Common.linux-x86-borland b/configure/os/CONFIG.Common.linux-x86-borland new file mode 100644 index 000000000..e72fb1490 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-x86-borland @@ -0,0 +1,25 @@ +# CONFIG.Common.linux-x86-borland +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-x86-borland +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = Linux +ARCH_CLASS = x86 + +POSIX_LDLIBS = libpthread.so + +OP_SYS_CPPFLAGS += -D_BSD_SOURCE -D_SVID_SOURCE +OP_SYS_CPPFLAGS += -Dlinux +OP_SYS_LDLIBS = libc.so libm.so libdl.so librt.so + +ARCH_DEP_CPPFLAGS += -D_X86_ + +RUNTIME_LDFLAGS += $(RUNTIME_LDFLAGS_$(SHARED_LIBRARIES)) + diff --git a/configure/os/CONFIG.Common.linux-x86-debug b/configure/os/CONFIG.Common.linux-x86-debug new file mode 100644 index 000000000..28ad3f572 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-x86-debug @@ -0,0 +1,13 @@ +# CONFIG.Common.linux-x86-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86 with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.Common.linux-x86-debug +#------------------------------------------------------- + +# Include definitions common to all linux-x86 target archs +include $(CONFIG)/os/CONFIG.Common.linux-x86 + +HOST_OPT=NO diff --git a/configure/os/CONFIG.Common.linux-x86_64 b/configure/os/CONFIG.Common.linux-x86_64 new file mode 100644 index 000000000..11b3d945e --- /dev/null +++ b/configure/os/CONFIG.Common.linux-x86_64 @@ -0,0 +1,22 @@ +# CONFIG.Common.linux-x86_64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86_64 target builds +# Sites may override these definitions in CONFIG_SITE.Common.linux-x86_64 +#------------------------------------------------------- + +# Include definitions common to all linux targets +include $(CONFIG)/os/CONFIG.Common.linuxCommon + +ARCH_CLASS = x86_64 + +ARCH_DEP_CPPFLAGS += -D_X86_64_ +ARCH_DEP_CFLAGS += -m64 +ARCH_DEP_LDFLAGS += -m64 + +# If your crosscompiler name has a GNU target prefix like -gcc, +# e.g. x86_64-redhat-linux-gcc, put a GNU_TARGET definition in +# CONFIG_SITE..linux-x86_64 file, e.g. GNU_TARGET=x86_64-redhat-linux + diff --git a/configure/os/CONFIG.Common.linux-x86_64-debug b/configure/os/CONFIG.Common.linux-x86_64-debug new file mode 100644 index 000000000..f6c31fb6c --- /dev/null +++ b/configure/os/CONFIG.Common.linux-x86_64-debug @@ -0,0 +1,13 @@ +# CONFIG.Common.linux-x86_64-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86_64 with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.Common.linux-x86_64-debug +#------------------------------------------------------- + +# Include definitions common to all linux-x86_64 target archs +include $(CONFIG)/os/CONFIG.Common.linux-x86_64 + +HOST_OPT=NO diff --git a/configure/os/CONFIG.Common.linux-xscale_be b/configure/os/CONFIG.Common.linux-xscale_be new file mode 100644 index 000000000..a70cc79b6 --- /dev/null +++ b/configure/os/CONFIG.Common.linux-xscale_be @@ -0,0 +1,25 @@ +# CONFIG.Common.linux-xscale_be +# +# This file is maintained by the build community. +# +# Definitions for linux-xscale_be (big-endian) target builds. +# This target has been tested with the MOXA UC-7408-LX Plus. + +# Sites may override these definitions in CONFIG_SITE.Common.linux-xscale_be +#------------------------------------------------------- + +# Include definitions common to all Linux targets +include $(CONFIG)/os/CONFIG.Common.linuxCommon + +ARCH_CLASS = xscale + +# +# The vendor's tool chain needs to be located here +# +GNU_DIR=/usr/local/xscale_be + +ifeq ($(BUILD_CLASS),CROSS) + VALID_BUILDS = Ioc + GNU_TARGET = xscale_be + CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) +endif diff --git a/configure/os/CONFIG.Common.linuxCommon b/configure/os/CONFIG.Common.linuxCommon new file mode 100644 index 000000000..db66319b2 --- /dev/null +++ b/configure/os/CONFIG.Common.linuxCommon @@ -0,0 +1,48 @@ +# CONFIG.Common.linuxCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux target builds +# Sites may override these definitions in CONFIG_SITE.Common.linuxCommon +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = Linux + +CODE_CPPFLAGS = -D_REENTRANT + +POSIX_CPPFLAGS = -D_POSIX_C_SOURCE=199506L -D_POSIX_THREADS -D_XOPEN_SOURCE=500 +POSIX_LDLIBS = -lpthread + +# -D_BSD_SOURCE for gethostname() in unistd.h as needed by cacChannelIO.cpp. +OP_SYS_CPPFLAGS += -D_BSD_SOURCE +OP_SYS_CPPFLAGS += -Dlinux +OP_SYS_LDLIBS += -lrt -ldl + +# Added here for cross-target builds which include this file +STATIC_LDFLAGS_YES= -Wl,-Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Wl,-Bdynamic + +# Set runtime path for shared libraries +SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-Wl,-rpath,%) +SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +# Set runtime path for products +PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-Wl,-rpath,%) +PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +# Link libraries controlled by COMMANDLINE_LIBRARY +# The newest Linux versions only need readline, older ones need both +# readline and ncurses, and the oldest need readline and curses +LDLIBS_READLINE = -lreadline +LDLIBS_READLINE_NCURSES = -lreadline -lncurses +LDLIBS_READLINE_CURSES = -lreadline -lcurses + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.Common.linuxCommon +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).linuxCommon diff --git a/configure/os/CONFIG.Common.solaris-sparc b/configure/os/CONFIG.Common.solaris-sparc new file mode 100644 index 000000000..592a06b5a --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-sparc @@ -0,0 +1,67 @@ +# CONFIG.Common.solaris-sparc +# +# Revision-Id: anj@aps.anl.gov-20101119223846-x6sd3oc6pyhdkwf0 +# This file is maintained by the build community. +# +# Definitions for solaris-sparc target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc +#------------------------------------------------------- + +# Include definitions common to all Unix target archs +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = solaris +ARCH_CLASS = sparc + +CODE_CPPFLAGS = -D__EXTENSIONS__ + +COMPILER_CPPFLAGS += -mt +COMPILER_LDFLAGS += -mt + +SOLARIS_VERSION = $(subst 5.,,$(shell uname -r)) + +POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=199506L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION)) +POSIX_CPPFLAGS += -D_XOPEN_SOURCE=500 +POSIX_LDLIBS += -lposix4 -lpthread $(POSIX_LDLIBS_$(SOLARIS_VERSION)) + +OP_SYS_CPPFLAGS += -DSOLARIS=$(SOLARIS_VERSION) $(COMPILER_CPPFLAGS) +OP_SYS_LDFLAGS += $(COMPILER_LDFLAGS) + +# Set runtime path for shared libraries +SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-R%) +SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +# Set runtime path for products +PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-R%) +PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +GNU_TARGET=sparc-sun-solaris2 + +STLPORT_CFLAGS_YES= -library=stlport4 +STLPORT_CFLAGS_NO= +STLPORT_LDLIBS_YES = +STLPORT_LDLIBS_NO = -lCstd + +# can be overridden in CONFIG_SITE.Common.solaris-sparc +USE_STLPORT=NO + +OP_SYS_CFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) +OP_SYS_LDFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) +OP_SYS_LDLIBS += $(STLPORT_LDLIBS_$(USE_STLPORT)) + +# OS libraries used when generating shared libraries or static binaries +OP_SYS_LDLIBS += -lsocket -lnsl +OP_SYS_LDLIBS_8 += -ldl -lCrun -lc +OP_SYS_LDLIBS_9 += -ldl -lumem -lCrun -lc +OP_SYS_LDLIBS_10 += -lumem -lCrun -lc +OP_SYS_LDLIBS += $(OP_SYS_LDLIBS_$(SOLARIS_VERSION)) + +# Definitions used when COMMANDLINE_LIBRARY is READLINE +READLINE_DIR = $(GNU_DIR) +INCLUDES_READLINE = -I$(READLINE_DIR)/include +RUNTIME_LDFLAGS_READLINE_YES += -R$(READLINE_DIR)/lib +RUNTIME_LDFLAGS_READLINE += $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) +LDFLAGS_READLINE += -L$(READLINE_DIR)/lib +LDLIBS_READLINE = -lreadline -lcurses +# Use archive if there is a problem with the readline shared library +#LDLIBS_READLINE = -Bstatic -lreadline -Bdynamic -lcurses diff --git a/configure/os/CONFIG.Common.solaris-sparc-debug b/configure/os/CONFIG.Common.solaris-sparc-debug new file mode 100644 index 000000000..979fd3b2c --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-sparc-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.solaris-sparc-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc-debug +#------------------------------------------------------- + +# Include definitions common to all solaris-sparc target archs +include $(CONFIG)/os/CONFIG.Common.solaris-sparc + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO diff --git a/configure/os/CONFIG.Common.solaris-sparc-gnu b/configure/os/CONFIG.Common.solaris-sparc-gnu new file mode 100644 index 000000000..4ddf1b258 --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-sparc-gnu @@ -0,0 +1,21 @@ +# CONFIG.Common.solaris-sparc-gnu +# +# Revision-Id: anj@aps.anl.gov-20101119223846-x6sd3oc6pyhdkwf0 +# This file is maintained by the build community. +# +# Definitions for solaris-sparc gnu compiler target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc-gnu +#------------------------------------------------------- + +# Include definitions common to all solaris-sparc target archs +include $(CONFIG)/os/CONFIG.Common.solaris-sparc +# CONFIG.Common.solaris-sparc + +COMPILER_CPPFLAGS = -D_REENTRANT + +STLPORT_LDLIBS_NO = + +OP_SYS_LDLIBS_8 = -ldl +OP_SYS_LDLIBS_9 = -ldl +OP_SYS_LDLIBS_10 = + diff --git a/configure/os/CONFIG.Common.solaris-sparc64 b/configure/os/CONFIG.Common.solaris-sparc64 new file mode 100644 index 000000000..3c3a675b9 --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-sparc64 @@ -0,0 +1,14 @@ +# CONFIG.Common.solaris-sparc64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc64 compiler target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc64 +#------------------------------------------------------- + +# Include definitions common to all solaris-sparc target archs +include $(CONFIG)/os/CONFIG.Common.solaris-sparc + +ARCH_DEP_CFLAGS += -m64 +ARCH_DEP_LDFLAGS += -m64 diff --git a/configure/os/CONFIG.Common.solaris-sparc64-gnu b/configure/os/CONFIG.Common.solaris-sparc64-gnu new file mode 100644 index 000000000..31715110a --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-sparc64-gnu @@ -0,0 +1,15 @@ +# CONFIG.Common.solaris-sparc64-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc64 gnu compiler target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-sparc64-gnu +#------------------------------------------------------- + +# Include definitions common to all solaris-sparc-gnu target archs +include $(CONFIG)/os/CONFIG.Common.solaris-sparc-gnu + +ARCH_DEP_CFLAGS += -mcpu=v9 -m64 +ARCH_DEP_LDFLAGS += -mcpu=v9 -m64 +ARCH_DEP_LDFLAGS += -L$(GNU_LIB)/sparcv9 -R$(GNU_LIB)/sparcv9 diff --git a/configure/os/CONFIG.Common.solaris-x86 b/configure/os/CONFIG.Common.solaris-x86 new file mode 100644 index 000000000..35202551c --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-x86 @@ -0,0 +1,68 @@ +# CONFIG.Common.solaris-x86 +# +# Revision-Id: anj@aps.anl.gov-20101119223846-x6sd3oc6pyhdkwf0 +# This file is maintained by the build community. +# +# Definitions for solaris-x86 target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-x86 +#------------------------------------------------------- + +# Include definitions common to all Unix target archs +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = solaris +ARCH_CLASS = x86 + +CODE_CPPFLAGS = -D__EXTENSIONS__ + +COMPILER_CPPFLAGS += -mt +COMPILER_LDFLAGS += -mt + +SOLARIS_VERSION = $(subst 5.,,$(shell uname -r)) + +POSIX_CPPFLAGS += -D_POSIX_C_SOURCE=199506L $(POSIX_CPPFLAGS_$(SOLARIS_VERSION)) +POSIX_CPPFLAGS += -D_XOPEN_SOURCE=500 +POSIX_LDLIBS += -lposix4 -lpthread $(POSIX_LDLIBS_$(SOLARIS_VERSION)) + +OP_SYS_CPPFLAGS += -DSOLARIS=$(SOLARIS_VERSION) $(COMPILER_CPPFLAGS) +OP_SYS_LDFLAGS += $(COMPILER_LDFLAGS) +ARCH_DEP_CPPFLAGS = -D_X86_ + +# Set runtime path for shared libraries +SHRLIBDIR_RPATH_LDFLAGS_YES += $(SHRLIB_DEPLIB_DIRS:%=-R%) +SHRLIBDIR_LDFLAGS += $(SHRLIBDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +# Set runtime path for products +PRODDIR_RPATH_LDFLAGS_YES += $(PROD_DEPLIB_DIRS:%=-R%) +PRODDIR_LDFLAGS += $(PRODDIR_RPATH_LDFLAGS_$(LINKER_USE_RPATH)) + +GNU_TARGET=x86-sun-solaris2 + +STLPORT_CFLAGS_YES= -library=stlport4 +STLPORT_CFLAGS_NO= +STLPORT_LDLIBS_YES = +STLPORT_LDLIBS_NO = -lCstd + +# can be overridden from ...SITE +USE_STLPORT=NO + +OP_SYS_CFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) +OP_SYS_LDFLAGS+=$(STLPORT_CFLAGS_$(USE_STLPORT)) + +OP_SYS_LDLIBS += -lsocket -lnsl +OP_SYS_LDLIBS_8 += -ldl -lCrun -lc +OP_SYS_LDLIBS_9 += -ldl -lCrun -lc +OP_SYS_LDLIBS_10 += -lCrun -lc +OP_SYS_LDLIBS += $(OP_SYS_LDLIBS_$(SOLARIS_VERSION)) +OP_SYS_LDLIBS += $(STLPORT_LDLIBS_$(USE_STLPORT)) + +# Definitions used when COMMANDLINE_LIBRARY is READLINE +READLINE_DIR = $(GNU_DIR) +INCLUDES_READLINE = -I$(READLINE_DIR)/include +RUNTIME_LDFLAGS_READLINE_YES += -R$(READLINE_DIR)/lib +RUNTIME_LDFLAGS_READLINE += $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)) +LDFLAGS_READLINE += -L$(READLINE_DIR)/lib +LDLIBS_READLINE = -lreadline -lcurses +# Use archive if there is a problem with the readline shared library +#LDLIBS_READLINE = -Bstatic -lreadline -Bdynamic -lcurses + diff --git a/configure/os/CONFIG.Common.solaris-x86-gnu b/configure/os/CONFIG.Common.solaris-x86-gnu new file mode 100644 index 000000000..61a3506e8 --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-x86-gnu @@ -0,0 +1,20 @@ +# CONFIG.Common.solaris-x86-gnu +# +# Revision-Id: anj@aps.anl.gov-20101119223846-x6sd3oc6pyhdkwf0 +# This file is maintained by the build community. +# +# Definitions for solaris-x86 gnu compiler target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-x86-gnu +#------------------------------------------------------- + +# Include definitions common to all solaris-x86 target archs +include $(CONFIG)/os/CONFIG.Common.solaris-x86 + +COMPILER_CPPFLAGS = -D_REENTRANT + +STLPORT_LDLIBS_NO = + +OP_SYS_LDLIBS_8 = -ldl -lc +OP_SYS_LDLIBS_9 = -ldl -lc +OP_SYS_LDLIBS_10 = -lc + diff --git a/configure/os/CONFIG.Common.solaris-x86_64 b/configure/os/CONFIG.Common.solaris-x86_64 new file mode 100644 index 000000000..9b423a038 --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-x86_64 @@ -0,0 +1,14 @@ +# CONFIG.Common.solaris-x86_64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86_64 compiler target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-x86_64 +#------------------------------------------------------- + +# Include definitions common to all solaris-x86 target archs +include $(CONFIG)/os/CONFIG.Common.solaris-x86 + +ARCH_DEP_CFLAGS += -m64 +ARCH_DEP_LDFLAGS += -m64 diff --git a/configure/os/CONFIG.Common.solaris-x86_64-gnu b/configure/os/CONFIG.Common.solaris-x86_64-gnu new file mode 100644 index 000000000..0c9a4be0e --- /dev/null +++ b/configure/os/CONFIG.Common.solaris-x86_64-gnu @@ -0,0 +1,15 @@ +# CONFIG.Common.solaris-x86_64-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86_64 gnu compiler target archs +# Sites may override these definitions in CONFIG_SITE.Common.solaris-x86_64-gnu +#------------------------------------------------------- + +# Include definitions common to all solaris-x86-gnu target archs +include $(CONFIG)/os/CONFIG.Common.solaris-x86-gnu + +ARCH_DEP_CFLAGS += -m64 +ARCH_DEP_LDFLAGS += -m64 +#ARCH_DEP_LDFLAGS += -L$(GNU_LIB)/amd64 -R$(GNU_LIB)/amd64 diff --git a/configure/os/CONFIG.Common.vxWorks-486 b/configure/os/CONFIG.Common.vxWorks-486 new file mode 100644 index 000000000..809562d60 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-486 @@ -0,0 +1,25 @@ +# CONFIG.Common.vxWorks-486 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-486 target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-486 +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 386 + +ARCH_CLASS = pc486 + +ARCH_DEP_CPPFLAGS = -DCPU=I80486 -D_X86_ +ARCH_DEP_CFLAGS = -m486 +ARCH_DEP_CXXFLAGS += -x 'c++' +ARCH_DEP_CFLAGS += -fno-defer-pop + +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-486 + diff --git a/configure/os/CONFIG.Common.vxWorks-486-debug b/configure/os/CONFIG.Common.vxWorks-486-debug new file mode 100644 index 000000000..5df50d004 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-486-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-486-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-486-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-486-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-486 + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-68040 b/configure/os/CONFIG.Common.vxWorks-68040 new file mode 100644 index 000000000..971f8e475 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-68040 @@ -0,0 +1,24 @@ +# CONFIG.Common.vxWorks-68040 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-68040 target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040 +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68040 +ARCH_DEP_CFLAGS = -m68040 + +OPT_CFLAGS_YES = -O0 + +GNU_TARGET = m68k-wrs-vxworks diff --git a/configure/os/CONFIG.Common.vxWorks-68040-debug b/configure/os/CONFIG.Common.vxWorks-68040-debug new file mode 100644 index 000000000..db823c231 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-68040-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-68040-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-68040-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-68040 + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-68040lc b/configure/os/CONFIG.Common.vxWorks-68040lc new file mode 100644 index 000000000..6800b2561 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-68040lc @@ -0,0 +1,24 @@ +# CONFIG.Common.vxWorks-68040lc +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-68040lc target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040lc +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68LC040 +ARCH_DEP_CFLAGS = -m68040 -msoft-float + +OPT_CFLAGS_YES = -O0 + +GNU_TARGET = m68k-wrs-vxworks diff --git a/configure/os/CONFIG.Common.vxWorks-68040lc-debug b/configure/os/CONFIG.Common.vxWorks-68040lc-debug new file mode 100644 index 000000000..d457e5034 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-68040lc-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-68040lc-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-68040lc-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68040lc-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-68040lc + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-68060 b/configure/os/CONFIG.Common.vxWorks-68060 new file mode 100644 index 000000000..d558592f3 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-68060 @@ -0,0 +1,24 @@ +# CONFIG.Common.vxWorks-68060 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-68060 target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68060 +#------------------------------------------------------- + +# Include definitions common to all vxWorks target archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 68k + +ARCH_CLASS = 68k + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=MC68060 +ARCH_DEP_CFLAGS = -m68040 + +OPT_CFLAGS_YES = -O0 + +GNU_TARGET = m68k-wrs-vxworks diff --git a/configure/os/CONFIG.Common.vxWorks-68060-debug b/configure/os/CONFIG.Common.vxWorks-68060-debug new file mode 100644 index 000000000..07bcc55ee --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-68060-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-68060-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-68060-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-68060-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-68060 + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-mpc8540 b/configure/os/CONFIG.Common.vxWorks-mpc8540 new file mode 100644 index 000000000..d3296602e --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-mpc8540 @@ -0,0 +1,24 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for vxWorks-mpc8540 targets: MPC8540 CPU with >32MB RAM. +# Site-specific overrides go in CONFIG_SITE.Common.vxWorks-mpc8540 +# +# This file is maintained by the EPICS build community. +#------------------------------------------------------- + +# Include definitions common to all vxWorks target archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +GNU_DIR_5 = $(WIND_BASE)/gnu/3.3/$(WIND_HOST_TYPE) + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC85XX +ARCH_DEP_CFLAGS = -mcpu=8540 -msoft-float -mspe=no -mabi=no-spe +ARCH_DEP_CFLAGS += -mstrict-align -mlongcall + +GNU_TARGET = powerpc-wrs-vxworks diff --git a/configure/os/CONFIG.Common.vxWorks-mpc8540-debug b/configure/os/CONFIG.Common.vxWorks-mpc8540-debug new file mode 100644 index 000000000..dba61f310 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-mpc8540-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-mpc8540-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-mpc8540-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-mpc8540-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-mpc8540 + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-pentium b/configure/os/CONFIG.Common.vxWorks-pentium new file mode 100644 index 000000000..e0dc5eb76 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-pentium @@ -0,0 +1,22 @@ +# CONFIG.Common.vxWorks-pentium +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-pentium target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-pentium +#------------------------------------------------------- + +# Include definitions common to all vxWorks target archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = 386 + +ARCH_CLASS = pcPentium + +ARCH_DEP_CPPFLAGS = -DCPU=PENTIUM -D_X86_ +ARCH_DEP_CFLAGS = -mpentium +ARCH_DEP_CXXFLAGS += -x 'c++' +ARCH_DEP_CFLAGS += -fno-defer-pop + diff --git a/configure/os/CONFIG.Common.vxWorks-pentium-debug b/configure/os/CONFIG.Common.vxWorks-pentium-debug new file mode 100644 index 000000000..345ab1903 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-pentium-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-pentium-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-pentium-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-pentium-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-pentium + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-ppc603 b/configure/os/CONFIG.Common.vxWorks-ppc603 new file mode 100644 index 000000000..ce5007ffe --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc603 @@ -0,0 +1,21 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for vxWorks-ppc603 targets: PPC603 and PMC8240 CPUs +# Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc603 +# +# This file is maintained by the EPICS build community. +#------------------------------------------------------- + +# Include definitions common to all vxWorks target archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC603 +ARCH_DEP_CFLAGS = -mcpu=603 -mstrict-align + +GNU_TARGET = powerpc-wrs-vxworks diff --git a/configure/os/CONFIG.Common.vxWorks-ppc603-debug b/configure/os/CONFIG.Common.vxWorks-ppc603-debug new file mode 100644 index 000000000..8f7acbf1a --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc603-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-ppc603-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-ppc603-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc603-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc603 + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-ppc603_long b/configure/os/CONFIG.Common.vxWorks-ppc603_long new file mode 100644 index 000000000..ce221f365 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc603_long @@ -0,0 +1,13 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for vxWorks-ppc603 targets with >32MB of RAM +# Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc603_long +# +# This file is maintained by the EPICS build community. +#------------------------------------------------------- + +# Inherit the settings from vxWorks-ppc603 +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc603 + +# Tell compiler to generate long branches +ARCH_DEP_CFLAGS += -mlongcall diff --git a/configure/os/CONFIG.Common.vxWorks-ppc603_long-debug b/configure/os/CONFIG.Common.vxWorks-ppc603_long-debug new file mode 100644 index 000000000..e51f52242 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc603_long-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-ppc603_long-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-ppc603_long-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc603_long-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc603_long + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-ppc604 b/configure/os/CONFIG.Common.vxWorks-ppc604 new file mode 100644 index 000000000..3dc510a82 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc604 @@ -0,0 +1,24 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for vxWorks-ppc604 targets: PPC604, MPC7xx and MPC74xx CPUs +# Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc604 +# +# This file is maintained by the EPICS build community. +#------------------------------------------------------- + +# Include definitions common to all vxWorks target archs +include $(CONFIG)/os/CONFIG.Common.vxWorksCommon + +# Vx GNU cross compiler suffix +CMPLR_SUFFIX = ppc + +ARCH_CLASS = ppc + +# Architecture specific build flags +ARCH_DEP_CPPFLAGS = -DCPU=PPC604 +ARCH_DEP_CFLAGS_2 = -mcpu=604 -mstrict-align -mno-implicit-fp +ARCH_DEP_CFLAGS_3 = -mcpu=604 -mstrict-align -mno-implicit-fp +ARCH_DEP_CFLAGS_4 = -mcpu=604 -mstrict-align -fno-implicit-fp +ARCH_DEP_CFLAGS = $(ARCH_DEP_CFLAGS_$(VX_GNU_MAJOR_VERSION)) + +GNU_TARGET = powerpc-wrs-vxworks diff --git a/configure/os/CONFIG.Common.vxWorks-ppc604-debug b/configure/os/CONFIG.Common.vxWorks-ppc604-debug new file mode 100644 index 000000000..5c3b849a4 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc604-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-ppc604-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-ppc604-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc604-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604 + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-ppc604_altivec b/configure/os/CONFIG.Common.vxWorks-ppc604_altivec new file mode 100644 index 000000000..a8593ef18 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc604_altivec @@ -0,0 +1,22 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for vxWorks-ppc604 targets with an Altivec and >32MB of RAM. +# Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc604_altivec +# +# This file is maintained by the EPICS build community. +#------------------------------------------------------- + +# Inherit the settings from vxWorks-ppc604_long +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604_long + +defval = $(if $(strip $(1),),$(1),$(2)) + +# Tell compiler to include altivec support; not available in 5.4 +ALTIVEC_CFLAG_5.4 = THIS_VERSION_OF_VXWORKS DOES_NOT_SUPPORT_ALTIVEC +ALTIVEC_CFLAG_5.5 = -fvec -DALTIVEC +ALTIVEC_CFLAG_6.0 = -fvec -DALTIVEC + +# From 6.1 onwards the compiler option changed +ALTIVEC_CFLAG = -maltivec -DALTIVEC + +ARCH_DEP_CFLAGS += $(call defval,$(ALTIVEC_CFLAG_$(VXWORKS_VERSION)),$(ALTIVEC_CFLAG)) diff --git a/configure/os/CONFIG.Common.vxWorks-ppc604_altivec-debug b/configure/os/CONFIG.Common.vxWorks-ppc604_altivec-debug new file mode 100644 index 000000000..bf478ade9 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc604_altivec-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-ppc604_altivec-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-ppc604_altivec-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc604_altivec-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604_altivec + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorks-ppc604_long b/configure/os/CONFIG.Common.vxWorks-ppc604_long new file mode 100644 index 000000000..897c9fc80 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc604_long @@ -0,0 +1,13 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for vxWorks-ppc604 targets with >32MB of RAM +# Site-specific overrides go in CONFIG_SITE.Common.vxWorks-ppc604_long +# +# This file is maintained by the EPICS build community. +#------------------------------------------------------- + +# Inherit the settings from vxWorks-ppc604 +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604 + +# Tell compiler to generate long branches +ARCH_DEP_CFLAGS += -mlongcall diff --git a/configure/os/CONFIG.Common.vxWorks-ppc604_long-debug b/configure/os/CONFIG.Common.vxWorks-ppc604_long-debug new file mode 100644 index 000000000..e749482f8 --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorks-ppc604_long-debug @@ -0,0 +1,14 @@ +# CONFIG.Common.vxWorks-ppc604_long-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks-ppc604_long-debug target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorks-ppc604_long-debug +#------------------------------------------------------- + +# Include definitions common to all vxWorks archs +include $(CONFIG)/os/CONFIG.Common.vxWorks-ppc604_long + +CROSS_OPT = NO + diff --git a/configure/os/CONFIG.Common.vxWorksCommon b/configure/os/CONFIG.Common.vxWorksCommon new file mode 100644 index 000000000..daf1a84dc --- /dev/null +++ b/configure/os/CONFIG.Common.vxWorksCommon @@ -0,0 +1,217 @@ +# CONFIG.Common.vxWorksCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for vxWorks target archs +# Sites may override these definitions in CONFIG_SITE.Common.vxWorksCommon +# or CONFIG_SITE..vxWorksCommon +#------------------------------------------------------- + +# Vx valid build types +VALID_BUILDS = Ioc + +#-------------------------------------------------- +# operating system class (include/os/) +OS_CLASS = vxWorks + +#------------------------------------------------------- +# Prefix and suffix definitions +EXE = +OBJ = .o +LIB_PREFIX =lib +LIB_SUFFIX = .a +MUNCH_SUFFIX = .munch + +#------------------------------------------------------- +# Compiler definitions +CMPLR_PREFIX= +CC = $(GNU_BIN)/$(CMPLR_PREFIX)cc$(CMPLR_SUFFIX) +CCC = $(GNU_BIN)/$(CMPLR_PREFIX)cc$(CMPLR_SUFFIX) + +#------------------------------------------------------- +# Library definitions +LIBNAME = $(BUILD_LIBRARY:%=$(LIB_PREFIX)%$(LIB_SUFFIX)) + +#-------------------------------------------------- +# Prod: DEPLIBS, LDFLAGS, and LDLIBS definitions + +PROD_DEPLIBS=$(foreach lib,$(PROD_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib).*, \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + + +PROD_LDLIBS = $(addprefix -l,$($*_LDLIBS) $(PROD_LIBS) $(USR_LIBS)\ + $($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS)) + +PROD_DEPLIB_DIRS = $(dir $($*_DEPLIBS)) $(dir $(PROD_DEPLIBS)) +PRODDIR_LDFLAGS += $(sort $(PROD_DEPLIB_DIRS:%=-L%)) + +#------------------------------------------------------- +# Prod definitions +MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +CTDT_SRCS = $(PRODNAME:%$(EXE)=%_ctdt.c) +CTDT_OBJS = $(PRODNAME:%$(EXE)=%_ctdt$(OBJ)) +NMS = $(PRODNAME:%$(EXE)=%.nm) +MUNCH_DEPENDS = %_ctdt$(OBJ) + +#------------------------------------------------------- +# R3.13 compatability object library definitions +# Does not allow $*_SRCS or $*_OBJS. Allows only OBJLIB_SRCS and OBJLIB_OBJS. +OBJLIB += $(OBJLIB_$(OS_CLASS)) +OBJLIB_SUFFIX = Library.o +OBJLIBNAME = $(addsuffix $(OBJLIB_SUFFIX),$(basename $(OBJLIB))) +OBJLIB_LD_OBJS += $(addsuffix $(OBJ),$(basename $(OBJLIB_SRCS) $(OBJLIB_OBJS))) +PRODTARGETS += $(OBJLIBNAME) +INSTALL_PROD = $(OBJLIBNAME:%=$(INSTALL_BIN)/%) +OBJLIB_MUNCHNAME += $(OBJLIBNAME:%$(OBJ)=%$(MUNCH_SUFFIX)) +INSTALL_MUNCHS += $(OBJLIB_MUNCHNAME:%=$(INSTALL_BIN)/%) +PRODTARGETS += $(OBJLIB_MUNCHNAME) +CTDT_SRCS += $(OBJLIBNAME:%$(OBJ)=%_ctdt.c) +CTDT_OBJS += $(OBJLIBNAME:%$(OBJ)=%_ctdt$(OBJ)) +NMS += $(OBJLIBNAME:%$(OBJ)=%.nm) +OBJLIB_LINK.cpp = $(LD) -o $@ $(OBJLIB_LD_OBJS) +SRC_FILES += $(OBJLIB_SRCS) + +#-------------------------------------------------- +# vxWorks version numbers + +VXWORKS_MAJOR_VERSION = $(basename $(basename $(VXWORKS_VERSION))) + +# These are needed for vxWorks 6.x; the GNU toolset version number +# is in the path to the compiler tools: +VX_GNU_VERSION_5.4 = 2.95 +VX_GNU_VERSION_5.5 = 2.96 +VX_GNU_VERSION_6.0 = 3.3.2 +VX_GNU_VERSION_6.1 = 3.3.2 +VX_GNU_VERSION_6.2 = 3.3.2 +VX_GNU_VERSION_6.3 = 3.4.4 +VX_GNU_VERSION_6.4 = 3.4.4 +VX_GNU_VERSION_6.5 = 3.4.4 +VX_GNU_VERSION_6.6 = 4.1.2 +VX_GNU_VERSION_6.7 = 4.1.2 +VX_GNU_VERSION_6.8 = 4.1.2 +VX_GNU_VERSION = $(VX_GNU_VERSION_$(VXWORKS_VERSION)) + +VX_GNU_MAJOR_VERSION = $(basename $(basename $(VX_GNU_VERSION))) + +#-------------------------------------------------- +# Fix WIND_BASE for vxWorks 6.x on linux +# NB: We know the value of WIND_HOST_TYPE here, but not VXWORKS_VERSION +ifeq ($(WIND_HOST_TYPE),x86-linux) + WIND_HOST_TYPE_5 = x86-linux + WIND_HOST_TYPE_6 = x86-linux2 + WIND_HOST_TYPE = $(WIND_HOST_TYPE_$(VXWORKS_MAJOR_VERSION)) +endif + +#-------------------------------------------------- +# vxWorks directory definitions +VX_DIR_5 = $(WIND_BASE) +VX_DIR_6 = $(WIND_BASE)/vxworks-$(VXWORKS_VERSION) +VX_DIR = $(VX_DIR_$(VXWORKS_MAJOR_VERSION)) + +VX_INCLUDE_DIRS_5 = $(VX_DIR)/target/h +VX_INCLUDE_DIRS_6 = $(VX_DIR)/target/h $(VX_DIR)/target/h/wrn/coreip +GNU_TARGET_INCLUDE_DIR = $(VX_INCLUDE_DIRS_$(VXWORKS_MAJOR_VERSION)) + +#-------------------------------------------------- +# vxWorks GNU directories + +GNU_DIR_5 = $(WIND_BASE)/host/$(WIND_HOST_TYPE) +GNU_DIR_6 = $(WIND_BASE)/gnu/$(VX_GNU_VERSION)-vxworks-$(VXWORKS_VERSION)/$(WIND_HOST_TYPE) +GNU_DIR = $(GNU_DIR_$(VXWORKS_MAJOR_VERSION)) + +#-------------------------------------------------- +# Wind River moved nm out of GNU_BIN in some versions + +WORKBENCH_BIN = $(WIND_BASE)/workbench-$(WORKBENCH_VERSION)/$(WIND_HOST_TYPE)/bin +UTILITIES_BIN = $(WIND_BASE)/utilities-$(UTILITIES_VERSION)/$(WIND_HOST_TYPE)/bin + +NM_DIR_6.4 = $(WORKBENCH_BIN) +NM_DIR_6.5 = $(WORKBENCH_BIN) +NM_DIR_6.6 = $(WORKBENCH_BIN) +NM_DIR_6.7 = $(GNU_BIN) +NM_DIR_6.8 = $(UTILITIES_BIN) +NM_DIR = $(firstword $(NM_DIR_$(VXWORKS_VERSION)) $(GNU_BIN)) + +NM = $(NM_DIR)/$(CMPLR_PREFIX)nm$(CMPLR_SUFFIX)$(HOSTEXE) + +#-------------------------------------------------- +# A linker script is essential for munching from vxWorks 6.6 onwards +# (i.e. with versions that use gcc 4.1.2 or later). It can be used +# with any vxWorks 5 or 6 version, but apparently should not be used +# when compiling for 68K (which isn't supported in vxWorks 6 anyway) +MUNCH_LDFLAGS_6 = -T $(VX_DIR)/target/h/tool/gnu/ldscripts/link.OUT +MUNCH_LDFLAGS = $(MUNCH_LDFLAGS_$(VXWORKS_MAJOR_VERSION)) + +#-------------------------------------------------- +# The follow 2 exports prevent gnu cross-compiler +# from finding wrong assembler (as). +export WIND_BASE +export WIND_HOST_TYPE + +#-------------------------------------------------- +# Tornado2.2 +# The follow export allows vxWorks.h to include gnu header files +export TOOL_FAMILY = GNU + +#-------------------------------------------------- +# Operating system flags +OP_SYS_CPPFLAGS += -DvxWorks +OP_SYS_CFLAGS += -fno-builtin + +# Fix for vxWorks 5 headers that use macros defined in vxWorks.h but +# which don't actually include vxWorks.h themselves, for example the +# target/h/sys/stat.h file which uses ULONG. This also stops dbDefs.h +# from defining the OFFSET macro, which generates lots of warnings in +# both vxWorks 5 and 6. +OP_SYS_INCLUDE_CPPFLAGS += -include $(VX_DIR)/target/h/vxWorks.h + +#-------------------------------------------------- +# Optimization: Officially vxWorks only supports -O2 or less. +OPT_CFLAGS_YES = -O2 +OPT_CXXFLAGS_YES = -O2 + +#-------------------------------------------------- +# code flags +CODE_CFLAGS = +# +# For vxWorks versions before 6.3 we need this g++ compiler flag +CODE_CXXFLAGS_5 = -fno-implicit-templates +CODE_CXXFLAGS_6.0 = -fno-implicit-templates +CODE_CXXFLAGS_6.1 = -fno-implicit-templates +CODE_CXXFLAGS_6.2 = -fno-implicit-templates +CODE_CXXFLAGS_6 = $(CODE_CXXFLAGS_$(VXWORKS_VERSION)) +CODE_CXXFLAGS = $(CODE_CXXFLAGS_$(VXWORKS_MAJOR_VERSION)) + +#-------------------------------------------------- +# no shared libs for vxWorks +SHRLIB_CFLAGS = +SHRLIB_LDFLAGS = + +#-------------------------------------------------- +# osithead use default stack, YES or NO override +OSITHREAD_USE_DEFAULT_STACK = NO + +#-------------------------------------------------- +# Link definitions +LINK.cpp = $(LD) -o $@ $(STATIC_LDFLAGS) $(PRODDIR_LDFLAGS) $(LDFLAGS) +LINK.cpp += $(PROD_LDFLAGS) $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS) + +#-------------------------------------------------- +# Definitions for compile of *_ctdt.c file +CFLAGS_ctdt = $(filter-out -pedantic,$(CFLAGS)) -fdollars-in-identifiers +COMPILE.ctdt = $(CC) -c $(CPPFLAGS) $(CFLAGS_ctdt) $(call PATH_FILTER,$(INCLUDES)) $(SOURCE_FLAG) + +#-------------------------------------------------- +# C preprocessor command +VXCPPFLAGS = $(filter-out $(OP_SYS_INCLUDE_CPPFLAGS),$(CPPFLAGS)) +PREPROCESS.cpp = $(CPP) $(VXCPPFLAGS) $(INCLUDES) $< > $@ + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.Common.vxWorksCommon +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon + diff --git a/configure/os/CONFIG.Common.win32-x86-cygwin b/configure/os/CONFIG.Common.win32-x86-cygwin new file mode 100644 index 000000000..e5f1757af --- /dev/null +++ b/configure/os/CONFIG.Common.win32-x86-cygwin @@ -0,0 +1,78 @@ +# CONFIG.Common.win32-x86-cygwin +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for cygwin-x86 target builds +# Sites may override these definitions in CONFIG_SITE.Common.cygwin-x86 +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = WIN32 +ARCH_CLASS = x86 + +# Definitions used when COMMANDLINE_LIBRARY is READLINE +LDLIBS_READLINE = -lreadline -lcurses + +POSIX_CPPFLAGS = -D_POSIX_THREADS -D_POSIX_TIMERS +#POSIX_CPPFLAGS += -D_POSIX_SOURCE +POSIX_LDLIBS += -lpthread + +ARCH_DEP_CFLAGS += -m32 +ARCH_DEP_LDFLAGS += -m32 + +# With no-cygwin option: +# compiler defines _X86_ 1 +# compiler defines __MSVCRT__ 1 +# compiler defines __MINGW32__ 1 +# compiler defines __WIN32 1 +# compiler defines __WIN32__ 1 +# compiler defines _WIN32 1 +# compiler defines WIN32 1 +# compiler defines WINNT 1 +# compiler defines __GNUC__ 3 +# compiler does not define __unix __unix__ unix +# compiler does not define __CYGWIN__ __CYGWIN32__ + +OP_SYS_CPPFLAGS = -mno-cygwin +OP_SYS_LDFLAGS += -mno-cygwin +OP_SYS_LDLIBS = -lws2_32 -ladvapi32 -luser32 -lkernel32 -lwinmm + +EXE=.exe + +VISC_DLL_NO = -DEPICS_DLL_NO +VISC_DLL_YES = +VISC_DLL = $(VISC_DLL_$(SHARED_LIBRARIES)) +STATIC_CFLAGS_YES= $(VISC_DLL) +STATIC_CFLAGS_NO= $(VISC_DLL) -D_DLL +STATIC_CXXFLAGS_YES= $(VISC_DLL) +STATIC_CXXFLAGS_NO= $(VISC_DLL) -D_DLL + +# adjust names of libraries to build +# +# But: if there are no objects LIBRARY_LD_OBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +SHRLIB_PREFIX= +SHRLIB_SUFFIX=.dll +SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) +LOADABLE_SHRLIB_PREFIX= +LOADABLE_SHRLIB_SUFFIX=.dll +LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(LOADABLE_SHRLIB_SUFFIX)) + +# +# When SHARED_LIBRARIES is YES we are building a DLL link library +# and when SHARED_LIBRARIES is NO we are building an object library +# +LIB_PREFIX= +LIB_SUFFIX=.lib +DLL_LINK_LIBNAME_YES = $(BUILD_LIBRARY:%=%$(LIB_SUFFIX)) +DLL_LINK_LIBNAME = $(DLL_LINK_LIBNAME_$(SHARED_LIBRARIES)) +LIBNAME_NO = $(BUILD_LIBRARY:%=%$(LIB_SUFFIX)) +LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) + +# dll install location +INSTALL_SHRLIB = $(INSTALL_BIN) + diff --git a/configure/os/CONFIG.Common.win32-x86-mingw b/configure/os/CONFIG.Common.win32-x86-mingw new file mode 100644 index 000000000..ed93626d5 --- /dev/null +++ b/configure/os/CONFIG.Common.win32-x86-mingw @@ -0,0 +1,73 @@ +# CONFIG.Common.win32-x86-mingw +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86-mingw target builds +# Sites may override these definitions in CONFIG_SITE.Common.win32-x86-mingw +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +OS_CLASS = WIN32 +ARCH_CLASS = x86 + +# Definitions used when COMMANDLINE_LIBRARY is READLINE +LDLIBS_READLINE = -lreadline -lcurses + +ARCH_DEP_CFLAGS += -m32 +ARCH_DEP_LDFLAGS += -m32 + +# Compiler defines _X86_ 1 +# Compiler defines __MSVCRT__ 1 +# Compiler defines __MINGW32__ 1 +# Compiler defines __WIN32 1 +# Compiler defines __WINNT 1 +# Compiler defines __WINNT__ 1 +# Compiler defines __WIN32__ 1 +# Compiler defines _WIN32 1 +# Compiler defines WIN32 1 +# Compiler defines WINNT 1 +# Compiler does not define __unix __unix__ unix + +# Override for -DUNIX from CONFIG.Common.UnixCommon +OP_SYS_CPPFLAGS = -D_MINGW + +EXE=.exe + +VISC_DLL_NO = -DEPICS_DLL_NO +VISC_DLL_YES = +VISC_DLL = $(VISC_DLL_$(SHARED_LIBRARIES)) +STATIC_CFLAGS_YES= $(VISC_DLL) +STATIC_CFLAGS_NO= $(VISC_DLL) -D_DLL +STATIC_CXXFLAGS_YES= $(VISC_DLL) +STATIC_CXXFLAGS_NO= $(VISC_DLL) -D_DLL + +# adjust names of libraries to build +# +# But: if there are no objects LIBRARY_LD_OBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +SHRLIB_PREFIX= +SHRLIB_SUFFIX=.dll +SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) +LOADABLE_SHRLIB_PREFIX= +LOADABLE_SHRLIB_SUFFIX=.dll +LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(LOADABLE_SHRLIB_SUFFIX)) + +# +# When SHARED_LIBRARIES is YES we are building a DLL link library +# and when SHARED_LIBRARIES is NO we are building an object library +# +LIB_PREFIX= +LIB_SUFFIX=.lib +DLL_LINK_LIBNAME_YES = $(BUILD_LIBRARY:%=%$(LIB_SUFFIX)) +DLL_LINK_LIBNAME = $(DLL_LINK_LIBNAME_$(SHARED_LIBRARIES)) +LIBNAME_NO = $(BUILD_LIBRARY:%=%$(LIB_SUFFIX)) +LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) + +# dll install location +INSTALL_SHRLIB = $(INSTALL_BIN) + + diff --git a/configure/os/CONFIG.UnixCommon.Common b/configure/os/CONFIG.UnixCommon.Common new file mode 100644 index 000000000..fec97cf0e --- /dev/null +++ b/configure/os/CONFIG.UnixCommon.Common @@ -0,0 +1,18 @@ +# CONFIG.UnixCommon.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions common to unix hosts +# Sites may override these definitions in CONFIG_SITE.UnixCommon.Common +#------------------------------------------------------- + +# Unix command definitions +CP = cp +MV = mv +RM = rm -f +MKDIR = mkdir +RMDIR = rm -rf + +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.UnixCommon.Common diff --git a/configure/os/CONFIG.aix-ppc-gnu.Common b/configure/os/CONFIG.aix-ppc-gnu.Common new file mode 100644 index 000000000..7953a40c8 --- /dev/null +++ b/configure/os/CONFIG.aix-ppc-gnu.Common @@ -0,0 +1,12 @@ +# CONFIG.aix-ppc-gnu.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for aix-ppc gnu compiler host builds +# Sites may override these definitions in CONFIG_SITE.aix-ppc-gnu.Common +#------------------------------------------------------- + +#Include definitions common to aix-ppc hosts +include $(CONFIG)/os/CONFIG.aix-ppc.Common + diff --git a/configure/os/CONFIG.aix-ppc-gnu.aix-ppc-gnu b/configure/os/CONFIG.aix-ppc-gnu.aix-ppc-gnu new file mode 100644 index 000000000..989bda8ff --- /dev/null +++ b/configure/os/CONFIG.aix-ppc-gnu.aix-ppc-gnu @@ -0,0 +1,16 @@ +# CONFIG.aix-ppc-gnu.aix-ppc-gnu +# +# +# Definitions for aix-ppc-gnu host - aix-ppc-gnu target builds +# Sites may override these definitions in CONFIG_SITE.aix-ppc-gnu.aix-ppc-gnu +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +GNU_DIR = /usr/common/usg/gcc/3.2.1 + +AR = ar +ARFLAGS = rcv +RANLIB = ranlib -t + diff --git a/configure/os/CONFIG.aix-ppc.Common b/configure/os/CONFIG.aix-ppc.Common new file mode 100644 index 000000000..d791599e1 --- /dev/null +++ b/configure/os/CONFIG.aix-ppc.Common @@ -0,0 +1,10 @@ +# CONFIG.aix-ppc.Common +# +# This file is maintained by the build community. +# +# Definitions for aix-ppc host builds +# Sites may override these definitions in CONFIG_SITE.aix-ppc.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common diff --git a/configure/os/CONFIG.cygwin-x86.Common b/configure/os/CONFIG.cygwin-x86.Common new file mode 100644 index 000000000..ca9153b0e --- /dev/null +++ b/configure/os/CONFIG.cygwin-x86.Common @@ -0,0 +1,20 @@ +# CONFIG.cygwin-x86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for cygwin-x86 host archs +# Sites may override these definitions in CONFIG_SITE.cygwin-x86.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + +WIND_HOST_TYPE = x86-win32 +OSITHREAD_USE_DEFAULT_STACK = NO + +HOSTEXE=.exe + +# Needed to find dlls for base installed build tools (antelope,eflex,...) +PATH := $(EPICS_BASE_BIN):$(PATH) + diff --git a/configure/os/CONFIG.cygwin-x86.cygwin-x86 b/configure/os/CONFIG.cygwin-x86.cygwin-x86 new file mode 100644 index 000000000..bb7026b8e --- /dev/null +++ b/configure/os/CONFIG.cygwin-x86.cygwin-x86 @@ -0,0 +1,29 @@ +# CONFIG.cygwin-x86.cygwin-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for cygwin-x86 host - cygwin-x86 target builds +# Sites may override these definitions in CONFIG_SITE.cygwin-x86.cygwin-x86 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +# cygwin's gcc, g++, ar, ld, and ranlib must be in user's path +CC = gcc +CCC = g++ +AR = ar -rc +LD = ld -r +RANLIB = ranlib +RES=.coff +RCCMD = windres $(INCLUDES) $< $@ + +# No -fPIC avoids "-fPIC ignored for target (all code is position independent)" +SHRLIB_CFLAGS = +SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) +LOADABLE_SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) + +# Override linking with gcc library from CONFIG.gnuCommon +GNU_LDLIBS_YES = + + diff --git a/configure/os/CONFIG.cygwin-x86.cygwin-x86-debug b/configure/os/CONFIG.cygwin-x86.cygwin-x86-debug new file mode 100644 index 000000000..108e39b33 --- /dev/null +++ b/configure/os/CONFIG.cygwin-x86.cygwin-x86-debug @@ -0,0 +1,17 @@ +# CONFIG.cygwin-x86.cygwin-x86-debug +# +# Revision-Id: jba@aps.anl.gov-20101101193549-7279bslv1hpwa7xr +# This file is maintained by the build community. +# +# Definitions for cygwin-x86 host - cygwin-x86-debug target build +# Sites may override these definitions in CONFIG_SITE.cygwin-x86.cygwin-x86-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.cygwin-x86 +-include $(CONFIG)/os/CONFIG.cygwin-x86.cygwin-x86 +-include $(CONFIG)/os/CONFIG_SITE.Common.cygwin-x86 +-include $(CONFIG)/os/CONFIG_SITE.cygwin-x86.cygwin-x86 + +BUILD_CLASS = HOST + +HOST_OPT = NO diff --git a/configure/os/CONFIG.darwin-ppc.Common b/configure/os/CONFIG.darwin-ppc.Common new file mode 100644 index 000000000..b17660b78 --- /dev/null +++ b/configure/os/CONFIG.darwin-ppc.Common @@ -0,0 +1,11 @@ +# CONFIG.darwin-ppc.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for darwin-ppc host builds +# Sites may override these definitions in CONFIG_SITE.darwin-ppc.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common diff --git a/configure/os/CONFIG.darwin-ppc.darwin-ppc-debug b/configure/os/CONFIG.darwin-ppc.darwin-ppc-debug new file mode 100644 index 000000000..c8032f3a1 --- /dev/null +++ b/configure/os/CONFIG.darwin-ppc.darwin-ppc-debug @@ -0,0 +1,16 @@ +# CONFIG.darwin-ppc.darwin-ppc-debug +# +# Revision-Id: jba@aps.anl.gov-20101115014917-mbxh37863qdzkm5t +# This file is maintained by the build community. +# +# Definitions for darwin-ppc host - darwin-ppc-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-ppc.darwin-ppc-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.darwin-ppc +-include $(CONFIG)/os/CONFIG.darwin-ppc.darwin-ppc +-include $(CONFIG)/os/CONFIG_SITE.Common.darwin-ppc +-include $(CONFIG)/os/CONFIG_SITE.darwin-ppc.darwin-ppc + +BUILD_CLASS=HOST +HOST_OPT = NO diff --git a/configure/os/CONFIG.darwin-ppcx86.Common b/configure/os/CONFIG.darwin-ppcx86.Common new file mode 100644 index 000000000..1782d1cc6 --- /dev/null +++ b/configure/os/CONFIG.darwin-ppcx86.Common @@ -0,0 +1,11 @@ +# CONFIG.darwin-ppcx86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for Darwin universal PowerPC + x86 host builds +# Sites may override these definitions in CONFIG_SITE.darwin-ppcx86.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common diff --git a/configure/os/CONFIG.darwin-x86.Common b/configure/os/CONFIG.darwin-x86.Common new file mode 100644 index 000000000..36abc339a --- /dev/null +++ b/configure/os/CONFIG.darwin-x86.Common @@ -0,0 +1,11 @@ +# CONFIG.darwin-x86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for darwin-x86 host builds +# Sites may override these definitions in CONFIG_SITE.darwin-x86.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common diff --git a/configure/os/CONFIG.darwin-x86.darwin-x86-debug b/configure/os/CONFIG.darwin-x86.darwin-x86-debug new file mode 100644 index 000000000..34ab56911 --- /dev/null +++ b/configure/os/CONFIG.darwin-x86.darwin-x86-debug @@ -0,0 +1,17 @@ +# CONFIG.darwin-x86.darwin-x86-debug +# +# Revision-Id: jba@aps.anl.gov-20101115014917-mbxh37863qdzkm5t +# This file is maintained by the build community. +# +# Definitions for darwin-x86 host - darwin-x86-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.darwin-x86.darwin-x86-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.darwin-x86 +-include $(CONFIG)/os/CONFIG.darwin-x86.darwin-x86 +-include $(CONFIG)/os/CONFIG_SITE.Common.darwin-x86 +-include $(CONFIG)/os/CONFIG_SITE.darwin-x86.darwin-x86 + + +BUILD_CLASS=HOST +HOST_OPT = NO diff --git a/configure/os/CONFIG.darwinCommon.darwinCommon b/configure/os/CONFIG.darwinCommon.darwinCommon new file mode 100644 index 000000000..165a15080 --- /dev/null +++ b/configure/os/CONFIG.darwinCommon.darwinCommon @@ -0,0 +1,121 @@ +# CONFIG.darwinCommon.darwinCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Common definitions for darwin builds +# Sites may override these definitions in CONFIG_SITE.darwinCommon.darwinCommon +#------------------------------------------------------- + +# Include definitions common to all Unix targets +include $(CONFIG)/os/CONFIG.Common.UnixCommon + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +# +# Set OS-specific information +# +OS_CLASS = Darwin + +# +# Build architecture flags +# For Darwin, ARCH_CLASS may be empty, or may contain a list of CPU +# architectures which must be valid arguments to the -arch options +# for the cc and ld commands. +# ARCH_CLASS is defined in a CONFIG_SITE file which is not loaded +# until after this file. +# +ARCH_DEP_FLAGS = $(addprefix -arch ,$(ARCH_CLASS)) +ARCH_DEP_CFLAGS += $(ARCH_DEP_FLAGS) +ARCH_DEP_LDFLAGS += $(ARCH_DEP_FLAGS) + +# +# Special flags for Darwin +# No common blocks (as required when using shared libraries) +# +OP_SYS_CFLAGS += -fno-common + +# +# Don't try to use precompiled headers when converting sequencer files +# +CPPSNCFLAGS += -no-cpp-precomp + +# +# Darwin os definition +# +OP_SYS_CPPFLAGS += -Ddarwin + +# +# Always compile in debugging symbol table information +# +OPT_CFLAGS_YES += -g +OPT_CXXFLAGS_YES += -g + +# +# The following two definitions enable the use of DarwinPorts packages. +# +OP_SYS_INCLUDES += -I/opt/local/include +# dir/firstword/wildcard used to avoid warning -L: directory name (...) does not exist +OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /opt/local/lib/*)))) + +# +# The following two definitions enable the use of Fink packages. +# +OP_SYS_INCLUDES += -I/sw/include +# dir/firstword/wildcard used to avoid warning -L: directory name (...) does not exist +OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /sw/lib/*)))) + +# +# Libraries for command-line editing. +# +LDLIBS_READLINE = -lreadline + +# +# Command-line input support +# +COMMANDLINE_LIBRARY=READLINE + +GNU_DIR = /usr + +CC = $(GNU_BIN)/cc +CCC = $(GNU_BIN)/c++ + +# +# Darwin shared libraries +# +SHRLIB_VERSION = $(EPICS_VERSION).$(EPICS_REVISION).$(EPICS_MODIFICATION) +SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined suppress \ + -install_name $(shell perl $(TOOLS)/fullPathName.pl $(INSTALL_LIB))/$@ \ + -compatibility_version $(EPICS_VERSION).$(EPICS_REVISION) \ + -current_version $(SHRLIB_VERSION) +SHRLIB_SUFFIX = .$(SHRLIB_VERSION).dylib + +LOADABLE_SHRLIB_LDFLAGS = -bundle -flat_namespace -undefined suppress + +# +# Position-independent code is the default on Darwin. +# +CODE_CFLAGS = +CODE_CXXFLAGS = + +# +# Add support for Objective-C source +# +vpath %.m $(USR_VPATH) $(ALL_SRC_DIRS) +%.o: %.m + $(COMPILE.c) -c $< + +# +# Header dependency file generation +# +# Use GNU compiler flags when ARCH_CLASS is a single arch value +# otherwise use perl script command +SINGLE_ARCH=$(filter 1,$(words $(ARCH_CLASS))) +HDEPENDS_CFLAGS = $(if $(SINGLE_ARCH),$(HDEPENDS_CFLAGS_$(HDEPENDS))) +HDEPENDS_METHOD = $(if $(SINGLE_ARCH),CFLAGS,CMD) + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.darwinCommon.darwinCommon +-include $(CONFIG)/os/CONFIG_SITE.$(EPICS_HOST_ARCH).darwinCommon diff --git a/configure/os/CONFIG.freebsd-x86.Common b/configure/os/CONFIG.freebsd-x86.Common new file mode 100644 index 000000000..2d10bce26 --- /dev/null +++ b/configure/os/CONFIG.freebsd-x86.Common @@ -0,0 +1,10 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file is maintained by the build community. +# +# Definitions for freebsd host builds +# Sites may override these definitions in CONFIG_SITE.freebsd-x86.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common diff --git a/configure/os/CONFIG.freebsd-x86.freebsd-x86 b/configure/os/CONFIG.freebsd-x86.freebsd-x86 new file mode 100644 index 000000000..a79958ce7 --- /dev/null +++ b/configure/os/CONFIG.freebsd-x86.freebsd-x86 @@ -0,0 +1,14 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for freebsd-x86 host - freebsd-x86 target builds +# Sites may override these definitions in CONFIG_SITE.freebsd-x86.freebsd-x86 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +STATIC_LDFLAGS_YES= -Wl,-Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Wl,-Bdynamic +STATIC_LDLIBS_NO= + diff --git a/configure/os/CONFIG.freebsd-x86_64.Common b/configure/os/CONFIG.freebsd-x86_64.Common new file mode 100644 index 000000000..153c3b8ca --- /dev/null +++ b/configure/os/CONFIG.freebsd-x86_64.Common @@ -0,0 +1,11 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file is maintained by the build community. +# +# Definitions for freebsd host builds +# Sites may override these definitions in CONFIG_SITE.freebsd-x86_64.Common +#------------------------------------------------------- + +# Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + diff --git a/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 b/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 new file mode 100644 index 000000000..17fe09769 --- /dev/null +++ b/configure/os/CONFIG.freebsd-x86_64.freebsd-x86_64 @@ -0,0 +1,14 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for freebsd-x86_64 host - freebsd-x86_64 target builds +# Sites may override these definitions in CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +STATIC_LDFLAGS_YES= -Wl,-Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Wl,-Bdynamic +STATIC_LDLIBS_NO= + diff --git a/configure/os/CONFIG.linux-ppc.Common b/configure/os/CONFIG.linux-ppc.Common new file mode 100644 index 000000000..f93334210 --- /dev/null +++ b/configure/os/CONFIG.linux-ppc.Common @@ -0,0 +1,11 @@ +# CONFIG.linux-ppc.Common +# +# This file is maintained by the build community. +# +# Definitions for linux-ppc host builds +# Sites may override these definitions in CONFIG_SITE.linux-ppc.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + diff --git a/configure/os/CONFIG.linux-ppc.linux-ppc b/configure/os/CONFIG.linux-ppc.linux-ppc new file mode 100644 index 000000000..3c437df59 --- /dev/null +++ b/configure/os/CONFIG.linux-ppc.linux-ppc @@ -0,0 +1,10 @@ +# CONFIG.linux-ppc.linux-ppc +# +# +# Definitions for linux-ppc host - linux-ppc target builds +# Sites may override these definitions in CONFIG_SITE.linux-ppc.linux-ppc +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + diff --git a/configure/os/CONFIG.linux-ppc64.Common b/configure/os/CONFIG.linux-ppc64.Common new file mode 100644 index 000000000..761be6ac6 --- /dev/null +++ b/configure/os/CONFIG.linux-ppc64.Common @@ -0,0 +1,12 @@ +# CONFIG.linux-ppc64.Common +# +# Revision-Id: anj@aps.anl.gov-20101025192356-roqyubib1aep8k14 +# This file is maintained by the build community. +# +# Definitions for linux-ppc64 host builds +# Sites may override these definitions in CONFIG_SITE.linux-ppc64.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + diff --git a/configure/os/CONFIG.linux-ppc64.linux-ppc64 b/configure/os/CONFIG.linux-ppc64.linux-ppc64 new file mode 100644 index 000000000..41958a30c --- /dev/null +++ b/configure/os/CONFIG.linux-ppc64.linux-ppc64 @@ -0,0 +1,11 @@ +# CONFIG.linux-ppc64.linux-ppc64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for linux-ppc64 host - linux-ppc64 target builds +# Sites may override these definitions in CONFIG_SITE.linux-ppc64.linux-ppc64 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 + diff --git a/configure/os/CONFIG.linux-x86-borland.Common b/configure/os/CONFIG.linux-x86-borland.Common new file mode 100644 index 000000000..cca41e218 --- /dev/null +++ b/configure/os/CONFIG.linux-x86-borland.Common @@ -0,0 +1,13 @@ +# CONFIG.linux-x86-borland.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86-borland host builds +# Sites may override these definitions in CONFIG_SITE.linux-x86-borland.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + +WIND_HOST_TYPE = x86-linux diff --git a/configure/os/CONFIG.linux-x86-borland.linux-x86-borland b/configure/os/CONFIG.linux-x86-borland.linux-x86-borland new file mode 100644 index 000000000..83095e8d5 --- /dev/null +++ b/configure/os/CONFIG.linux-x86-borland.linux-x86-borland @@ -0,0 +1,198 @@ +# CONFIG.linux-x86-borland.linux-x86-borland +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for linux-x86-borland host - linux-x86-borland target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86-borland.linux-x86-borland +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon +SHRLIB_CFLAGS = +SHRLIB_LDFLAGS = +LOADABLE_SHRLIB_LDFLAGS = +GPROF=NO +PROFILE=NO + +GNU_DIR = /usr + +KYLIX=1 + +BORLAND_INC_STLPORT = $(BORLAND)/include/stlport +BORLAND_INC = $(BORLAND)/include +BORLAND_LIB = $(BORLAND)/lib +BORLAND_BIN = $(BORLAND)/bin + +WINLINK = $(BORLAND_BIN)/ilink -q +# +# Configure Borland C compiler +# -q suppress compiler identification banner +# -a8 quad word alignment +#CCLINKOPT = -q -a8 -I/usr/include +CCLINKOPT = -q -a8 +CC = $(BORLAND_BIN)/bc++ $(CCLINKOPT) +LD = $(BORLAND_BIN)/bc++ -r +RANLIB = + +# -w display warnings on +# -g0 no limit to warning messages +# some warning message here are always disabled because they are +# trivial and numerous +# -w-8012 Comparing signed and unsigned values +# -w-8060 Possibly incorrect assignment +# -w-8071 Conversion may lose significant digits +WARN_CFLAGS_YES = -w -g0 -w-8012 -w-8060 -w-8071 -w-dup +# -w- display warnings off +WARN_CFLAGS_NO = -w- + +# +# -k- turn off standard stack frame +# -H- turn off precompiled headers +# -R- don't include browser info in .obj files +# -O1 optimization for size +# -v- turn off source debugging +# -vi control expansion of inline functions +OPT_CFLAGS_YES = -k- -H- -R- -O1 -v- -vi +# +OPT_CFLAGS_NO = + +# OS vendor c preprocessor +CPP = $(BORLAND_BIN)/bcpp + +# Configure OS vendor C++ compiler +# +CCC = $(BORLAND_BIN)/bc++ $(CCLINKOPT) + +# -w display warnings on +# -g0 no limit to warning messages +# -w-8012 Comparing signed and unsigned values +# -w-8060 Possibly incorrect assignment +# -w-8071 Conversion may lose significant digits +# -w-8008 Condition is always true or always false (compile time +# know template parameters causes this to spew garbage) +# -w-8027 Functions containing reserved words are not expanded inline +# (Some compilers are better than others with inline) +# -w-8066 Unreachable code (compile time know template parameters +# causes this to spew garbage) +# -w-8080 'identifier' declared but never used +# (instantiating unused static const external parameters is +# anachronistic compiler practice) +# -w-8004 'identifier' is assigned a value that is never used +# (compile time know template parameters +# causes this to spew garbage) +# -w-8026 Functions with exception specifications are not expanded inline +# +WARN_CXXFLAGS_YES = -w -g0 -w-8012 -w-8060 -w-8071 -w-8008 -w-8027 -w-8066 -w-8080 -w-8004 -w-8026 +# -w- display warnings off +WARN_CXXFLAGS_NO = -w- + +# +# -k- turn off standard stack frame +# -H- Turn off precompiled headers +# -R- Don't include browser info in .obj files +# -O1 optimization for size +# -v- turn off source debugging +# -vi control expansion of inline functions +OPT_CXXFLAGS_YES = -k- -H- -R- -O1 -v- -vi +# +OPT_CXXFLAGS_NO = + +# -c case sensitive linking +# -C clear state before linking +# -Gi generate import library +# -Gn no state files +# -Tpd targets a Windows .DLL file +# -x no map +# -w display warnings on +#LINK_OPT_FLAGS_YES = -c -C -Gi -Gn -Tpd -x -w +#LINK_OPT_FLAGS_NO = -c -C -Gi -Gn -Tpd -x -w- +#Linux_DLLFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT)) $(TARGET_LDFLAGS) $(LIB_LDFLAGS) +OPT_LDFLAGS = + +ARCH_DEP_CFLAGS= + +OS_CLASS=Linux + + +OBJ=.o + +# Problem: BorlandC does not recognize *.cc as C++ source, +# we have to compile xx.cc using the flag -P xx.cc, +COMPILER_CXXFLAGS = -P + +# Operating system flags +OP_SYS_CFLAGS = + +# +# Borland specific include files +# +OP_SYS_INCLUDES = -I/usr/include +# + +#------------------------------------------------------- +# Prod: DEPLIBS, LDFLAGS, and LDLIBS definitions + +PROD_DEPLIBS= $(foreach lib,$(PROD_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib).so, \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib).a, \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_EXT), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +SHRLIB_DEPLIBS= $(foreach lib,$(LIB_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib).so, \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib).a, \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_EXT), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +PROD_SYS_DEPLIBS= $(foreach lib,$(PROD_SYS_LIBS), \ + $(firstword $(wildcard \ + /lib/$(LIB_PREFIX)$(lib).so \ + /lib/$(LIB_PREFIX)$(lib).a \ + /usr/lib/$(LIB_PREFIX)$(lib).so \ + /usr/lib/$(LIB_PREFIX)$(lib).a) \ + $(lib))) + +PROD_LDLIBS = $(addprefix lib, $($*_LDLIBS)) \ + $(notdir $(PROD_DEPLIBS)) \ + $(notdir $(PROD_SYS_DEPLIBS)) \ + $(addprefix lib, $($*_SYS_LIBS) $(USR_SYS_LIBS)) + +SHRLIB_LDLIBS = $(addprefix lib, $($*_LDLIBS)) \ + $(notdir $(SHRLIB_DEPLIBS)) \ + $(addprefix lib, $($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS)) $(LDLIBS) + +LDLIBS_STATIC_YES = LDLIBS +LDLIBS_SHARED_NO = LDLIBS +LDLIBS_SHARED_YES = LDLIBS +PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \ + $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) + +empty:= +space:= $(empty) $(empty) +PROD_DEPLIB_DIRS = $(dir $($*_DEPLIBS)) $(dir $(PROD_DEPLIBS)) +SHRLIB_DEPLIB_DIRS = $(dir $($*_DEPLIBS)) $(dir $(SHRLIB_DEPLIBS)) + +PRODDIR_LDFLAGS = -L$(subst $(space),:,$(sort $(PROD_DEPLIB_DIRS) $(BORLAND_LIB))) +SHRLIBDIR_LDFLAGS = -L$(subst $(space),:,$(sort $(SHRLIB_DEPLIB_DIRS) $(BORLAND_LIB))) + +# -c case sensitive linking +# -C clear state before linking +# -Gn no state files +# -Tpe targets a Windows .EXE file +# -x no map +# -w display warnings on +LDFLAGS += -c -C -Gn -x -w +LINKLIBS=rtl.a visualclx.a rtle.a libborcrtl.a libborstl.a libborunwind.a + +LINK.cpp = $(WINLINK) $(STATIC_LDFLAGS) $(PRODDIR_LDFLAGS) $(LDFLAGS) -Tpe -w-dup +LINK.cpp += $(PROD_LDFLAGS) borinit.o /usr/lib/crt1.o $(PROD_LD_OBJS) +LINK.cpp += , $@ ,,$(LINKLIBS) $(PROD_LDLIBS) + +LINK.shrlib = $(WINLINK) $(STATIC_LDFLAGS) $(SHRLIBDIR_LDFLAGS) $(LDFLAGS) -Tpd -Gi +LINK.shrlib+= $(TARGET_LIB_LDFLAGS) borinitso.o $(LIBRARY_LD_OBJS) +LINK.shrlib+= , $@ ,,$(LINKLIBS) $(SHRLIB_LDLIBS) + diff --git a/configure/os/CONFIG.linux-x86-debug.Common b/configure/os/CONFIG.linux-x86-debug.Common new file mode 100644 index 000000000..f6c2ba8b7 --- /dev/null +++ b/configure/os/CONFIG.linux-x86-debug.Common @@ -0,0 +1,15 @@ +# CONFIG.linux-x86-debug.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86 debug with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.linux-x86-debug.Common +#------------------------------------------------------- + +#Include definitions common to linux-x86 hosts +include $(CONFIG)/os/CONFIG.linux-x86.Common + +# Make all crosscompiler builds debug builds +#CROSS_OPT=NO + diff --git a/configure/os/CONFIG.linux-x86-debug.linux-x86-debug b/configure/os/CONFIG.linux-x86-debug.linux-x86-debug new file mode 100644 index 000000000..495e13bc7 --- /dev/null +++ b/configure/os/CONFIG.linux-x86-debug.linux-x86-debug @@ -0,0 +1,14 @@ +# CONFIG.linux-x86-debug.linux-x86-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86 host - linux-x86 target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.linux-x86-debug.linux-x86-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO + diff --git a/configure/os/CONFIG.linux-x86.Common b/configure/os/CONFIG.linux-x86.Common new file mode 100644 index 000000000..92913f661 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.Common @@ -0,0 +1,13 @@ +# CONFIG.linux-x86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86 host builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + +WIND_HOST_TYPE = x86-linux diff --git a/configure/os/CONFIG.linux-x86.linux-arm b/configure/os/CONFIG.linux-x86.linux-arm new file mode 100644 index 000000000..2d38bfcc9 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-arm @@ -0,0 +1,12 @@ +# CONFIG.linux-x86.linux-arm +# +# Definitions for linux-x86 host - linux-arm target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-arm +#------------------------------------------------------- + +# Copied from x86.x86 +STATIC_LDFLAGS_YES= -Wl,-Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Wl,-Bdynamic +STATIC_LDLIBS_NO= + diff --git a/configure/os/CONFIG.linux-x86.linux-arm_eb b/configure/os/CONFIG.linux-x86.linux-arm_eb new file mode 100644 index 000000000..f67bf3990 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-arm_eb @@ -0,0 +1,9 @@ +# CONFIG.linux-x86.linux-arm_eb +# +# Definitions for linux-x86 host - linux-arm_eb (big endian) target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-arm_eb +#------------------------------------------------------- + +# Include definitions for linux-arm targets +include $(CONFIG)/os/CONFIG.linux-x86.linux-arm + diff --git a/configure/os/CONFIG.linux-x86.linux-arm_el b/configure/os/CONFIG.linux-x86.linux-arm_el new file mode 100644 index 000000000..f924796a9 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-arm_el @@ -0,0 +1,9 @@ +# CONFIG.linux-x86.linux-arm_el +# +# Definitions for linux-x86 host - linux-arm_el (little endian) target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-arm_el +#------------------------------------------------------- + +# Include definitions for linux-arm targets +include $(CONFIG)/os/CONFIG.linux-x86.linux-arm + diff --git a/configure/os/CONFIG.linux-x86.linux-cris b/configure/os/CONFIG.linux-x86.linux-cris new file mode 100644 index 000000000..8244916e5 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-cris @@ -0,0 +1,38 @@ +# CONFIG.linux-x86.linux-cris +# +# Author: Peter Zumbruch +# GSI +# P.Zumbruch@gsi.de +# +# Definitions for linux-x86 host - linux-cris target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-cris +#------------------------------------------------------- + +GNU_DIR = $(CRIS_CROSS_COMPILER) + +#STATIC_... +STATIC_LDFLAGS_YES= -Wl,-Bstatic + +## debian-gcc Bug#438641 +GNU_LDLIBS_YES = +STATIC_LDFLAGS_YES += -static-libgcc + +# if not in debug mode strip all symbols +ifndef CRIS_COMPILER_DEBUG +STATIC_LDFLAGS_YES += -Wl,--strip-all +endif + +ifeq ($(GNU),YES) +STATIC_LDFLAGS_NO = -lgcc +else +STATIC_LDFLAGS_NO = +endif + +STATIC_LDLIBS_YES= +STATIC_LDLIBS_NO= + +OPT_CXXFLAGS_YES = -Os + +ifeq ($(STATIC_BUILD), YES) + $(shell echo yes) +endif diff --git a/configure/os/CONFIG.linux-x86.linux-cris_v10 b/configure/os/CONFIG.linux-x86.linux-cris_v10 new file mode 100644 index 000000000..a227ac07c --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-cris_v10 @@ -0,0 +1,20 @@ +# CONFIG.linux-x86.linux-cris_v10 +# +# Author: Peter Zumbruch +# GSI +# P.Zumbruch@gsi.de +# +# Definitions for linux-x86 host - linux-cris_v10 target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-cris_v10 +#------------------------------------------------------- + +# Include definitions common to all linux-cris targets +include $(CONFIG)/os/CONFIG.Common.linux-cris + +GNU_TARGET = cris-axis-linux-gnu + +ARCH_DEP_CFLAGS += -march=v10 + +# if you are using different places for cris_v10 cris_v32 +# you have to define for each architecture +# AXIS_SDK_DIR= diff --git a/configure/os/CONFIG.linux-x86.linux-cris_v32 b/configure/os/CONFIG.linux-x86.linux-cris_v32 new file mode 100644 index 000000000..eee8f3344 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-cris_v32 @@ -0,0 +1,20 @@ +# CONFIG.linux-x86.linux-cris_v32 +# +# Author: Peter Zumbruch +# GSI +# P.Zumbruch@gsi.de +# +# Definitions for linux-x86 host - linux-cris_v32 target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-cris_v32 +#------------------------------------------------------- + +# Include definitions common to all linux-cris targets +include $(CONFIG)/os/CONFIG.Common.linux-cris + +GNU_TARGET = crisv32-axis-linux-gnu + +ARCH_DEP_CFLAGS += -march=v32 + +# if you are using different places for cris_v10 cris_v32 +# you have to define for each architecture +# AXIS_SDK_DIR= diff --git a/configure/os/CONFIG.linux-x86.linux-x86 b/configure/os/CONFIG.linux-x86.linux-x86 new file mode 100644 index 000000000..604e5f9c3 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-x86 @@ -0,0 +1,18 @@ +# CONFIG.linux-x86.linux-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for linux-x86 host - linux-x86 target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-x86 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +STATIC_LDFLAGS_YES= -Wl,-Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Wl,-Bdynamic +STATIC_LDLIBS_NO= + +SHRLIB_LDFLAGS += -Wl,-h$@ +LOADABLE_SHRLIB_LDFLAGS += -Wl,-h$@ diff --git a/configure/os/CONFIG.linux-x86.linux-x86-debug b/configure/os/CONFIG.linux-x86.linux-x86-debug new file mode 100644 index 000000000..007d01877 --- /dev/null +++ b/configure/os/CONFIG.linux-x86.linux-x86-debug @@ -0,0 +1,15 @@ +# CONFIG.linux-x86.linux-x86-debug +# +# Revision-Id: jba@aps.anl.gov-20101115163116-3xboj0bh17bood7s +# This file is maintained by the build community. +# +# Definitions for linux-x86 host - linux-x86-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.linux-x86.linux-x86-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 +#-include $(CONFIG)/os/CONFIG_SITE.Common.linux-x86 +#-include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-x86 + +BUILD_CLASS=HOST +HOST_OPT=NO diff --git a/configure/os/CONFIG.linux-x86_64-debug.Common b/configure/os/CONFIG.linux-x86_64-debug.Common new file mode 100644 index 000000000..da7ae0a3f --- /dev/null +++ b/configure/os/CONFIG.linux-x86_64-debug.Common @@ -0,0 +1,15 @@ +# CONFIG.linux-x86_64-debug.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86_64 debug with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.linux-x86_64-debug.Common +#------------------------------------------------------- + +#Include definitions common to linux-x86_64 hosts +include $(CONFIG)/os/CONFIG.linux-x86_64.Common + +# Make all crosscompiler builds debug builds +#CROSS_OPT=NO + diff --git a/configure/os/CONFIG.linux-x86_64-debug.linux-x86_64-debug b/configure/os/CONFIG.linux-x86_64-debug.linux-x86_64-debug new file mode 100644 index 000000000..13713672d --- /dev/null +++ b/configure/os/CONFIG.linux-x86_64-debug.linux-x86_64-debug @@ -0,0 +1,14 @@ +# CONFIG.linux-x86_64-debug.linux-x86_64-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86_64 host - linux-x86_64 target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.linux-x86_64.linux-x86_64 + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO + diff --git a/configure/os/CONFIG.linux-x86_64.Common b/configure/os/CONFIG.linux-x86_64.Common new file mode 100644 index 000000000..d527cbddc --- /dev/null +++ b/configure/os/CONFIG.linux-x86_64.Common @@ -0,0 +1,13 @@ +# CONFIG.linux-x86_64.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86_64 host builds +# Sites may override these definitions in CONFIG_SITE.linux-x86_64.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + +WIND_HOST_TYPE = x86-linux diff --git a/configure/os/CONFIG.linux-x86_64.linux-x86_64 b/configure/os/CONFIG.linux-x86_64.linux-x86_64 new file mode 100644 index 000000000..f60748a20 --- /dev/null +++ b/configure/os/CONFIG.linux-x86_64.linux-x86_64 @@ -0,0 +1,11 @@ +# CONFIG.linux-x86_64.linux-x86_64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for linux-x86_64 host - linux-x86_64 target builds +# Sites may override these definitions in CONFIG_SITE.linux-x86_64.linux-x86_64 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/os/CONFIG.linux-x86.linux-x86 + diff --git a/configure/os/CONFIG.linux-x86_64.linux-x86_64-debug b/configure/os/CONFIG.linux-x86_64.linux-x86_64-debug new file mode 100644 index 000000000..6775e8614 --- /dev/null +++ b/configure/os/CONFIG.linux-x86_64.linux-x86_64-debug @@ -0,0 +1,15 @@ +# CONFIG.linux-x86_64.linux-x86_64-debug +# +# Revision-Id: jba@aps.anl.gov-20101115163116-3xboj0bh17bood7s +# This file is maintained by the build community. +# +# Definitions for linux-x86_64 host - linux-x86_64-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.linux-x86_64.linux-x86_64-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.linux-x86_64.linux-x86_64 +-include $(CONFIG)/os/CONFIG_SITE.Common.linux-x86_64 +-include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.linux-x86_64 + +BUILD_CLASS=HOST +HOST_OPT = NO diff --git a/configure/os/CONFIG.solaris-sparc-debug.Common b/configure/os/CONFIG.solaris-sparc-debug.Common new file mode 100644 index 000000000..b83b209dd --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc-debug.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-sparc-debug.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc debug with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.solaris-sparc-debug.Common +#------------------------------------------------------- + +#Include definitions common to solaris-sparc hosts +include $(CONFIG)/os/CONFIG.solaris-sparc.Common + diff --git a/configure/os/CONFIG.solaris-sparc-debug.solaris-sparc-debug b/configure/os/CONFIG.solaris-sparc-debug.solaris-sparc-debug new file mode 100644 index 000000000..afe4534e0 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc-debug.solaris-sparc-debug @@ -0,0 +1,14 @@ +# CONFIG.solaris-sparc-debug.solaris-sparc-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host - solaris-sparc target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc +include $(CONFIG)/os/CONFIG.solaris-sparc.solaris-sparc + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO diff --git a/configure/os/CONFIG.solaris-sparc-gnu.Common b/configure/os/CONFIG.solaris-sparc-gnu.Common new file mode 100644 index 000000000..521d93171 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc-gnu.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-sparc-gnu.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc gnu compiler host builds +# Sites may override these definitions in CONFIG_SITE.solaris-sparc-gnu.Common +#------------------------------------------------------- + +#Include definitions common to solaris-sparc hosts +include $(CONFIG)/os/CONFIG.solaris-sparc.Common + diff --git a/configure/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu b/configure/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu new file mode 100644 index 000000000..cbb1641e2 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu @@ -0,0 +1,26 @@ +# CONFIG.solaris-sparc-gnu.solaris-sparc-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc gnu compiler host - solaris-sparc gnu compiler target builds +# Sites may override these definitions in CONFIG_SITE.solaris-sparc-gnu.solaris-sparc-gnu +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +AR = ar -rc +RANLIB= +LD = ld -r + +STATIC_LDFLAGS_YES= -Wl,-Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Wl,-Bdynamic +STATIC_LDLIBS_NO= + +OP_SYS_LDFLAGS += -z ignore -z combreloc -z lazyload + +SHRLIB_LDFLAGS += -Wl,-z,defs -Wl,-z,text -Wl,-h,$@ +LOADABLE_SHRLIB_LDFLAGS += -Wl,-z,text -Wl,-h,$@ +GNU_LDLIBS_YES += -lc diff --git a/configure/os/CONFIG.solaris-sparc.Common b/configure/os/CONFIG.solaris-sparc.Common new file mode 100644 index 000000000..f5d888bbc --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc.Common @@ -0,0 +1,14 @@ +# CONFIG.solaris-sparc.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host archs +# Sites may override these definitions in CONFIG_SITE.solaris-sparc.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + +WIND_HOST_TYPE = sun4-solaris2 + diff --git a/configure/os/CONFIG.solaris-sparc.solaris-sparc b/configure/os/CONFIG.solaris-sparc.solaris-sparc new file mode 100644 index 000000000..fa545ef2f --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc.solaris-sparc @@ -0,0 +1,12 @@ +# CONFIG.solaris-sparc.solaris-sparc +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host - solaris-sparc target build +# Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.solarisCommon.solarisCommon + +SHRLIB_CFLAGS = -xcode=pic32 diff --git a/configure/os/CONFIG.solaris-sparc.solaris-sparc-debug b/configure/os/CONFIG.solaris-sparc.solaris-sparc-debug new file mode 100644 index 000000000..cf00d1c47 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc.solaris-sparc-debug @@ -0,0 +1,16 @@ +# CONFIG.solaris-sparc.solaris-sparc-debug +# +# Revision-Id: jba@aps.anl.gov-20101115163116-3xboj0bh17bood7s +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host - solaris-sparc-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.solaris-sparc.solaris-sparc +-include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc +-include $(CONFIG)/os/CONFIG_SITE.solaris-sparc.solaris-sparc + +BUILD_CLASS=HOST +HOST_OPT=NO + diff --git a/configure/os/CONFIG.solaris-sparc.solaris-sparc-gnu b/configure/os/CONFIG.solaris-sparc.solaris-sparc-gnu new file mode 100644 index 000000000..c1e28452a --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc.solaris-sparc-gnu @@ -0,0 +1,13 @@ +# CONFIG.solaris-sparc.solaris-sparc-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host - solaris-sparc-gnu target build with gnu compiler +# Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc-gnu +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu + +BUILD_CLASS = HOST + diff --git a/configure/os/CONFIG.solaris-sparc.solaris-sparc64 b/configure/os/CONFIG.solaris-sparc.solaris-sparc64 new file mode 100644 index 000000000..6932c65e8 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc.solaris-sparc64 @@ -0,0 +1,14 @@ +# CONFIG.solaris-sparc.solaris-sparc64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host - solaris-sparc64 target build with Sun compiler +# Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc64 +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.solaris-sparc64.solaris-sparc64 + +BUILD_CLASS = HOST + +GNU = NO diff --git a/configure/os/CONFIG.solaris-sparc.solaris-sparc64-gnu b/configure/os/CONFIG.solaris-sparc.solaris-sparc64-gnu new file mode 100644 index 000000000..e4a3c0485 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc.solaris-sparc64-gnu @@ -0,0 +1,13 @@ +# CONFIG.solaris-sparc.solaris-sparc64-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host - solaris-sparc64-gnu target build with gnu compiler +# Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc64-gnu +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu + +BUILD_CLASS = HOST + diff --git a/configure/os/CONFIG.solaris-sparc64-gnu.Common b/configure/os/CONFIG.solaris-sparc64-gnu.Common new file mode 100644 index 000000000..fa320dd89 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc64-gnu.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-sparc64-gnu.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc64 gnu compiler host builds +# Sites may override these definitions in CONFIG_SITE.solaris-sparc64-gnu.Common +#------------------------------------------------------- + +#Include definitions common to solaris-sparc-gnu hosts +include $(CONFIG)/os/CONFIG.solaris-sparc-gnu.Common + diff --git a/configure/os/CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu b/configure/os/CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu new file mode 100644 index 000000000..6efbe4355 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu @@ -0,0 +1,12 @@ +# CONFIG.solaris-sparc64-gnu.solaris-sparc64-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc64 gnu compiler host - solaris-sparc64 gnu compiler target builds +# Sites may override these definitions in CONFIG_SITE.solaris-sparc64-gnu.solaris-sparc64-gnu +#------------------------------------------------------- + +# Include common solaris-sparc-gnu definitions +include $(CONFIG)/os/CONFIG.solaris-sparc-gnu.solaris-sparc-gnu + diff --git a/configure/os/CONFIG.solaris-sparc64.Common b/configure/os/CONFIG.solaris-sparc64.Common new file mode 100644 index 000000000..55f35aff5 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc64.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-sparc64.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc Sun compiler host builds +# Sites may override these definitions in CONFIG_SITE.solaris-sparc64.Common +#------------------------------------------------------- + +#Include definitions common to solaris-sparc hosts +include $(CONFIG)/os/CONFIG.solaris-sparc.Common + diff --git a/configure/os/CONFIG.solaris-sparc64.solaris-sparc64 b/configure/os/CONFIG.solaris-sparc64.solaris-sparc64 new file mode 100644 index 000000000..725294d04 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc64.solaris-sparc64 @@ -0,0 +1,11 @@ +# CONFIG.solaris-sparc64.solaris-sparc64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc Sun compiler host - solaris-sparc Sun compiler target builds +# Sites may override these definitions in CONFIG_SITE.solaris-sparc64.solaris-sparc64 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/os/CONFIG.solaris-sparc.solaris-sparc diff --git a/configure/os/CONFIG.solaris-sparc64.solaris-sparc64-debug b/configure/os/CONFIG.solaris-sparc64.solaris-sparc64-debug new file mode 100644 index 000000000..b6559b774 --- /dev/null +++ b/configure/os/CONFIG.solaris-sparc64.solaris-sparc64-debug @@ -0,0 +1,16 @@ +# CONFIG.solaris-sparc64.solaris-sparc64-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc64 host - solaris-sparc64-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.solaris-sparc64.solaris-sparc64-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.Common.solaris-sparc64 +include $(CONFIG)/os/CONFIG.solaris-sparc64.solaris-sparc64 + +BUILD_CLASS=HOST + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO diff --git a/configure/os/CONFIG.solaris-x86-gnu.Common b/configure/os/CONFIG.solaris-x86-gnu.Common new file mode 100644 index 000000000..c36d83721 --- /dev/null +++ b/configure/os/CONFIG.solaris-x86-gnu.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-x86-gnu.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86 gnu compiler host builds +# Sites may override these definitions in CONFIG_SITE.solaris-x86-gnu.Common +#------------------------------------------------------- + +#Include definitions common to solaris-x86 hosts +include $(CONFIG)/os/CONFIG.solaris-x86.Common + diff --git a/configure/os/CONFIG.solaris-x86-gnu.solaris-x86-gnu b/configure/os/CONFIG.solaris-x86-gnu.solaris-x86-gnu new file mode 100644 index 000000000..d0f18b42c --- /dev/null +++ b/configure/os/CONFIG.solaris-x86-gnu.solaris-x86-gnu @@ -0,0 +1,20 @@ +# CONFIG.solaris-x86-gnu.solaris-x86-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86 gnu compiler host - solaris-x86 gnu compiler target builds +# Sites may override these definitions in CONFIG_SITE.solaris-x86-gnu.solaris-x86-gnu +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +AR = ar -rc +RANLIB= +LD = ld -r + +SHRLIB_LDFLAGS += -h $@ -z defs +LOADABLE_SHRLIB_LDFLAGS += -h $@ + +OP_SYS_LDFLAGS += -z ignore -z combreloc -z lazyload diff --git a/configure/os/CONFIG.solaris-x86.Common b/configure/os/CONFIG.solaris-x86.Common new file mode 100644 index 000000000..02e142856 --- /dev/null +++ b/configure/os/CONFIG.solaris-x86.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-x86.Common +# +# Revision-Id: anj@aps.anl.gov-20101025192356-roqyubib1aep8k14 +# This file is maintained by the build community. +# +# Definitions for solaris-x86 host builds +# Sites may override these definitions in CONFIG_SITE.solaris-x86.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + diff --git a/configure/os/CONFIG.solaris-x86.solaris-x86 b/configure/os/CONFIG.solaris-x86.solaris-x86 new file mode 100644 index 000000000..ddbfa60f2 --- /dev/null +++ b/configure/os/CONFIG.solaris-x86.solaris-x86 @@ -0,0 +1,12 @@ +# CONFIG.solaris-x86.solaris-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86 host - solaris-x86 target builds +# Sites may override these definitions in CONFIG_SITE.solaris-x86.solaris-x86 +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.solarisCommon.solarisCommon + +SHRLIB_CFLAGS = -KPIC diff --git a/configure/os/CONFIG.solaris-x86.solaris-x86-debug b/configure/os/CONFIG.solaris-x86.solaris-x86-debug new file mode 100644 index 000000000..5566c9cc0 --- /dev/null +++ b/configure/os/CONFIG.solaris-x86.solaris-x86-debug @@ -0,0 +1,18 @@ +# CONFIG.solaris-x86.solaris-x86-debug +# +# Revision-Id: jba@aps.anl.gov-20101115014917-mbxh37863qdzkm5t +# This file is maintained by the build community. +# +# Definitions for solaris-x86 host - solaris-x86-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.solaris-x86.solaris-x86-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.solaris-x86 +-include $(CONFIG)/os/CONFIG.solaris-x86.solaris-x86 +-include $(CONFIG)/os/CONFIG_SITE.Common.solaris-x86 +-include $(CONFIG)/os/CONFIG_SITE.solaris-x86.solaris-x86 + +BUILD_CLASS=HOST + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO diff --git a/configure/os/CONFIG.solaris-x86.solaris-x86_64 b/configure/os/CONFIG.solaris-x86.solaris-x86_64 new file mode 100644 index 000000000..9ba0e7db9 --- /dev/null +++ b/configure/os/CONFIG.solaris-x86.solaris-x86_64 @@ -0,0 +1,14 @@ +# CONFIG.solaris-x86.solaris-x86_64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86 host - solaris-x86_64 target build with Sun compiler +# Sites may override these definitions in CONFIG_SITE.solaris-x86.solaris-x86_64 +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.solaris-x86_64.solaris-x86_64 + +BUILD_CLASS = HOST + +GNU = NO diff --git a/configure/os/CONFIG.solaris-x86_64-gnu.Common b/configure/os/CONFIG.solaris-x86_64-gnu.Common new file mode 100644 index 000000000..84a58f7ef --- /dev/null +++ b/configure/os/CONFIG.solaris-x86_64-gnu.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-x86_64-gnu.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86_64 gnu compiler host builds +# Sites may override these definitions in CONFIG_SITE.solaris-sparc64-gnu.Common +#------------------------------------------------------- + +#Include definitions common to solaris-x86-gnu hosts +include $(CONFIG)/os/CONFIG.solaris-x86-gnu.Common + diff --git a/configure/os/CONFIG.solaris-x86_64-gnu.solaris-x86_64-gnu b/configure/os/CONFIG.solaris-x86_64-gnu.solaris-x86_64-gnu new file mode 100644 index 000000000..34a38f82c --- /dev/null +++ b/configure/os/CONFIG.solaris-x86_64-gnu.solaris-x86_64-gnu @@ -0,0 +1,12 @@ +# CONFIG.solaris-x86_64-gnu.solaris-x86_64-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86_64 gnu compiler host - solaris-sx86_64 gnu compiler target builds +# Sites may override these definitions in CONFIG_SITE.solaris-x86_64-gnu.solaris-x86_64-gnu +#------------------------------------------------------- + +# Include common solaris-x86-gnu definitions +include $(CONFIG)/os/CONFIG.solaris-x86-gnu.solaris-x86-gnu + diff --git a/configure/os/CONFIG.solaris-x86_64.Common b/configure/os/CONFIG.solaris-x86_64.Common new file mode 100644 index 000000000..8953fd7f8 --- /dev/null +++ b/configure/os/CONFIG.solaris-x86_64.Common @@ -0,0 +1,12 @@ +# CONFIG.solaris-x86_64.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86_64 Sun compiler host builds +# Sites may override these definitions in CONFIG_SITE.solaris-x86_64.Common +#------------------------------------------------------- + +#Include definitions common to solaris-sparc hosts +include $(CONFIG)/os/CONFIG.solaris-x86.Common + diff --git a/configure/os/CONFIG.solaris-x86_64.solaris-x86_64 b/configure/os/CONFIG.solaris-x86_64.solaris-x86_64 new file mode 100644 index 000000000..21deb353d --- /dev/null +++ b/configure/os/CONFIG.solaris-x86_64.solaris-x86_64 @@ -0,0 +1,11 @@ +# CONFIG.solaris-x86_64.solaris-x86_64 +# +# $Id +# This file is maintained by the build community. +# +# Definitions for solaris-x86_64 Sun compiler host - solaris-x86_64 Sun compiler target builds +# Sites may override these definitions in CONFIG_SITE.solaris-x86_64.solaris-x86_64 +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/os/CONFIG.solaris-x86.solaris-x86 diff --git a/configure/os/CONFIG.solaris-x86_64.solaris-x86_64-debug b/configure/os/CONFIG.solaris-x86_64.solaris-x86_64-debug new file mode 100644 index 000000000..0fefb3d2a --- /dev/null +++ b/configure/os/CONFIG.solaris-x86_64.solaris-x86_64-debug @@ -0,0 +1,16 @@ +# CONFIG.solaris-x86_64.solaris-x86_64-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-x86_64 host - solaris-x86_64-debug target build with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.solaris-x86_64.solaris-x86_64-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.Common.solaris-x86_64 +include $(CONFIG)/os/CONFIG.solaris-x86_64.solaris-x86_64 + +BUILD_CLASS=HOST + +# Removes -O optimization and adds -g compile option +HOST_OPT=NO diff --git a/configure/os/CONFIG.solarisCommon.solarisCommon b/configure/os/CONFIG.solarisCommon.solarisCommon new file mode 100644 index 000000000..a885922f0 --- /dev/null +++ b/configure/os/CONFIG.solarisCommon.solarisCommon @@ -0,0 +1,68 @@ +# CONFIG.solarisCommon.solarisCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for solaris-sparc host - solaris-sparc target build +# Sites may override these definitions in CONFIG_SITE.solaris-sparc.solaris-sparc +#------------------------------------------------------- + +SPARCWORKS = /opt/SUNWspro +GNU = NO + +CC = $(SPARCWORKS)/bin/cc +CCC = $(SPARCWORKS)/bin/CC +CPP = $(CC) -E -Qn +RANLIB = +# required by sun's C++ compiler +AR = $(CCC) -xar -o +LD = ld -r + +#Prepare the object code for profiling with prof. (YES or NO) +PROFILE=NO +#Prepare the object code for profiling with gprof. (YES or NO) +GPROF=NO + +# Configure OS vendor C compiler +PROF_CFLAGS_YES = -p +GPROF_CFLAGS_YES = -xpg +CODE_CFLAGS = $(PROF_CFLAGS_$(PROFILE)) $(GPROF_CFLAGS_$(GPROF)) +WARN_CFLAGS_YES = +WARN_CFLAGS_NO = -w +OPT_CFLAGS_YES = -xO4 +OPT_CFLAGS_NO = -g + +# Configure OS vendor C++ compiler +PROF_CXXFLAGS_YES = -p +GPROF_CXXFLAGS_YES = -xpg +CODE_CXXFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) +WARN_CXXFLAGS_YES = +w +WARN_CXXFLAGS_NO = +OPT_CXXFLAGS_YES = -O +OPT_CXXFLAGS_NO = -g + +CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) + +STATIC_LDFLAGS_YES= -Bstatic +STATIC_LDFLAGS_NO= +STATIC_LDLIBS_YES= -Bdynamic +STATIC_LDLIBS_NO= + +SHRLIB_LDFLAGS = -z defs -G -h $@ -z text +LOADABLE_SHRLIB_LDFLAGS = -G -h $@ -z text + +OP_SYS_LDFLAGS += -z ignore -z combreloc -z lazyload + +# Filter for getting rid of "invalid white space character in directive" compiler warnings +# when including headers created during win32 build. +#COMPILE_FILTER.c = 2>&1 | $(EPICS_BASE)/configure/tools/filterWarnings.pl +#COMPILE_FILTER.cpp = $(COMPILE_FILTER.c) + +# Header dependency file generation command +HDEPENDS_METHOD=CMD +HDEPENDSCMD = $(if $(filter .c,$(suffix $<)),$(COMPILE.c),$(COMPILE.cpp)) -xM1 $< >$*$(DEP) + +#-------------------------------------------------- +# Allow site overrides +-include $(CONFIG)/os/CONFIG_SITE.solarisCommon.solarisCommon +-include $(CONFIG)/os/CONFIG_SITE.(EPICS_HOST_ARCH).solarisCommon diff --git a/configure/os/CONFIG.win32-x86-borland.Common b/configure/os/CONFIG.win32-x86-borland.Common new file mode 100644 index 000000000..1e70b7541 --- /dev/null +++ b/configure/os/CONFIG.win32-x86-borland.Common @@ -0,0 +1,29 @@ +# CONFIG.win32-x86-borland.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86-borland host archs +# Sites may override these definitions in CONFIG_SITE.win32-x86-borland.Common +#------------------------------------------------------- + +CP = $(PERL) -MExtUtils::Command -e cp +MV = $(PERL) -MExtUtils::Command -e mv +RM = $(PERL) -MExtUtils::Command -e rm_f +MKDIR = $(PERL) -MExtUtils::Command -e mkpath +RMDIR = $(PERL) -MExtUtils::Command -e rm_rf + +WIND_HOST_TYPE = x86-win32 +OSITHREAD_USE_DEFAULT_STACK = NO + +HOSTEXE=.exe + +# Does not work if using cygwin make +# because HOME is always defined +ifndef HOME +HOME = $(HOMEDRIVE)$(HOMEPATH) +endif + +# Needed to find dlls for base installed build tools (antelope,eflex,...) +PATH := $(EPICS_BASE_BIN):$(PATH) + diff --git a/configure/os/CONFIG.win32-x86-borland.win32-x86-borland b/configure/os/CONFIG.win32-x86-borland.win32-x86-borland new file mode 100644 index 000000000..700f0f076 --- /dev/null +++ b/configure/os/CONFIG.win32-x86-borland.win32-x86-borland @@ -0,0 +1,267 @@ +# CONFIG.win32-x86-borland.win32-x86-borland +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86-borland target arch when host arch is win32-x86-borland +# Sites may override these definitions in CONFIG_SITE.win32-x86-borland.win32-x86-borland +#------------------------------------------------------- + +# Win32 valid build types and include directory suffixes + +VALID_BUILDS = Host Ioc + +#------------------------------------------------------- + +BORLAND_INC = $(BORLAND)\\include +BORLAND_LIB = $(BORLAND)\\lib +BORLAND_BIN = $(BORLAND)\\bin + +# +# "\ " forces gnu make to keep this as one token +# +WINLINK = $(BORLAND_BIN)/ilink32 -q + +# -l specifies default language +# -fo Renames the output .RES file +RCCMD = $(BORLAND_BIN)/brcc32 $(subst -I,-i,$(INCLUDES)) -l0x409 -fo$@ $< + +ARCMD = $(BORLAND_BIN)/tlib $@ $(foreach lib,$(LIBRARY_LD_OBJS),+$(lib)) + +#############BAFCMD = bscmake /nologo /o $@ + +# +# Configure Borland C compiler +# -q suppress compiler identification banner +# -tWM generate a 32-bit multi-threaded target +# -tWD generate a .DLL executable +# -a8 quad word alignment +# -D_WIN32 macro defined to be consistant with Microsoft Visual C++ +# -D_RTLDLL macro defined to use Borland C++ RTL library +CCLINKOPT = -q -tWM -tWD -a8 -D_WIN32 -D_RTLDLL +CC = $(BORLAND_BIN)/bcc32 $(CCLINKOPT) +LD = $(BORLAND_BIN)/bcc32 -r +RANLIB = + +# -w display warnings on +# -g0 no limit to warning messages +# some warning message here are always disabled because they are +# trivial and numerous +# -w-8012 Comparing signed and unsigned values +# -w-8060 Possibly incorrect assignment +# -w-8071 Conversion may lose significant digits +WARN_CFLAGS_YES = -w -g0 -w-8012 -w-8060 -w-8071 +# -w- display warnings off +WARN_CFLAGS_NO = -w- + +# +# -k- turn off standard stack frame +# -H- turn off precompiled headers +# -R- don't include browser info in .obj files +# -O1 optimization for size +# -v- turn off source debugging +# -vi control expansion of inline functions +OPT_CFLAGS_YES = -k- -H- -R- -O1 -v- -vi + +# +OPT_CFLAGS_NO = + +# OS vendor c preprocessor +CPP = $(BORLAND_BIN)/cpp32 -Sr +PREPROCESS.cpp = $(CPP) $(CPPFLAGS) $(subst /,\\,$(INCLUDES)) -o$@ $< + +# Configure OS vendor C++ compiler +CCC = $(BORLAND_BIN)/bcc32 $(CCLINKOPT) + +# -w display warnings on +# -g0 no limit to warning messages +# -w-8012 Comparing signed and unsigned values +# -w-8060 Possibly incorrect assignment +# -w-8071 Conversion may lose significant digits +# -w-8008 Condition is always true or always false (compile time +# know template parameters causes this to spew garbage) +# -w-8027 Functions containing reserved words are not expanded inline +# (Some compilers are better than others with inline) +# -w-8066 Unreachable code (compile time know template parameters +# causes this to spew garbage) +# -w-8080 'identifier' declared but never used +# (instantiating unused static const external parameters is +# anachronistic compiler practice) +# -w-8004 'identifier' is assigned a value that is never used +# (compile time know template parameters +# causes this to spew garbage) +# -w-8026 Functions with exception specifications are not expanded inline +# +WARN_CXXFLAGS_YES = -w -g0 -w-8012 -w-8060 -w-8071 -w-8008 -w-8027 -w-8066 -w-8080 -w-8004 -w-8026 +# -w- display warnings off +WARN_CXXFLAGS_NO = -w- + +# +# -k- turn off standard stack frame +# -H- Turn off precompiled headers +# -R- Don't include browser info in .obj files +# -O1 optimization for size +# -v- turn off source debugging +# -vi control expansion of inline functions +OPT_CXXFLAGS_YES = -k- -H- -R- -O1 -v- -vi + +# +OPT_CXXFLAGS_NO = + +# +# no special libs for static link +# +LDLIBS = ws2_32.lib advapi32.lib user32.lib kernel32.lib winmm.lib + +# -c case sensitive linking +# -C clear state before linking +# -Gi generate import library +# -Gn no state files +# -Tpd targets a Windows .DLL file +# -x no map +# -w display warnings on +LINK_OPT_FLAGS_YES = -c -C -Gi -Gn -Tpd -x -w +LINK_OPT_FLAGS_NO = -c -C -Gi -Gn -Tpd -x -w- +WIN32_DLLFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT)) $(TARGET_LDFLAGS) $(LIB_LDFLAGS) +OPT_LDFLAGS = + +SHRLIB_CFLAGS= +ARCH_DEP_CFLAGS= + +OS_CLASS=WIN32 +POSIX=NO + +# ifdef WIN32 looks better that ifeq ($(OS_CLASS),WIN32) ?? +WIN32=1 +BORLANDC=1 + +EXE=.exe +OBJ=.obj +RES=.res +BAF=.bsc +BOF=.sbr + +# Problem: BorlandC does not recognize *.cc as C++ source, +# we have to compile xx.cc using the flag -P xx.cc, +SOURCE_CXXFLAG = -P -D__cplusplus + +# Operating system flags +OP_SYS_CFLAGS = + +# +# Borland specific include files +# +OP_SYS_INCLUDES = -I$(BORLAND_INC) +# + +# +# specify dll .def file only if it exists +# +#DLL_DEF_FLAG = $(addprefix /def:,$(wildcard ../$(addsuffix .def,$(LIBRARY)))) +DLL_DEF_FLAG = $(subst /,\\,$(wildcard ../$(addsuffix .def,$*))) + +# +# A WIN32 dll has three parts: +# x.dll: the real dll (SHRLIBNAME) +# x.lib: what you link to progs that use the dll (DLL_LINK_LIBNAME) +# x.exp: what you need to build the dll (in no variable) +# +LINK.shrlib = $(WINLINK) $(WIN32_DLLFLAGS) -L$(BORLAND_LIB) -L$(BORLAND_LIB)\\Psdk c0d32.obj +LINK.shrlib+= $(LIBRARY_LD_OBJS) , $@ ,,$(LINKLIBS) $(subst /,\\,$(SHRLIB_LDLIBS)), +LINK.shrlib+= $(DLL_DEF_FLAG),$(LIBRARY_LD_RESS) + + +# adjust names of libraries to build +# +# But: if there are no objects LIBRARY_LD_OBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +SHRLIB_SUFFIX=.dll +SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) +LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) + + +# When SHARED_LIBRARIES is YES we are building a DLL link library +# and when SHARED_LIBRARIES is NO we are building an object library +# +DLL_LINK_LIBNAME_YES = $(BUILD_LIBRARY:%=%.lib) +DLL_LINK_LIBNAME = $(DLL_LINK_LIBNAME_$(SHARED_LIBRARIES)) +LIB_PREFIX= +LIB_SUFFIX=.lib +LIBNAME_NO = $(BUILD_LIBRARY:%=%.lib) +LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) + +# dll install location +INSTALL_SHRLIB = $(INSTALL_BIN) + +#-------------------------------------------------- +# Products dependancy definitions + +# SYS_PROD_LIBS deprecated +# Use PROD_SYS_LIBS + +PROD_DEPLIBS=$(foreach lib, $(PROD_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +PROD_LDLIBS += $($*_DEPLIBS) $(PROD_DEPLIBS) +PROD_LDLIBS += $(addsuffix .lib, \ + $($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS) ) + +LDLIBS_STATIC_YES = LDLIBS +LDLIBS_SHARED_NO = LDLIBS +PROD_LDLIBS += $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \ + $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) + +#-------------------------------------------------- +#Libraries dependancy definitions + +# _DLL_LIBS _SYS_DLL_LIBS deprecated. +# Use _LIBS and _SYS_LIBS. +# DLL_LIBS, and SYS_DLL_LIBS deprecated +# Use LIB_LIBS and LIB_SYS_LIBS +LIB_LIBS += $(DLL_LIBS) +LIB_SYS_LIBS += $(SYS_DLL_LIBS) + +# libs that we need to link the DLL with +# (it isnt necessary to rebuild the dll if these change) + +SHRLIB_DEPLIBS += $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +SHRLIB_LDLIBS += $($*_DLL_DEPLIBS) $($*_DEPLIBS) $(SHRLIB_DEPLIBS) +SHRLIB_LDLIBS += $(addsuffix .lib, \ + $($*_SYS_DLL_LIBS) \ + $($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS) ) + + +#-------------------------------------------------- + +#multithreaded import library +LIBSUF=mti +# -c case sensitive linking +# -C clear state before linking +# -Gn no state files +# -Tpe targets a Windows .EXE file +# -x no map +# -w display warnings on +LDFLAGS += -c -C -Gn -Tpe -x -w -L$(BORLAND_LIB) -L$(BORLAND_LIB)\\Psdk +LINKSTARTUP = c0x32.obj +LINKLIBS=import32.lib cw32$(LIBSUF).lib + +LINK.cpp = $(WINLINK) $(STATIC_LDFLAGS) $(LDFLAGS) $(PROD_LDFLAGS) $(LINKSTARTUP) $(subst /,\\,$(PROD_LD_OBJS)) +LINK.cpp += , $@ ,,$(LINKLIBS) $(subst /,\\,$(PROD_LDLIBS)) + +#-------------------------------------------------- + +# override of CONFIG_COMMON +HDEPENDS_INCLUDES_MKMF = $(filter-out $(BORLAND_INC),$(subst -I,,$(INCLUDES))) + + +TDS = $(addsuffix .tds,$(basename $(PROD) $(TESTPROD) $(BUILD_LIBRARY))) + diff --git a/configure/os/CONFIG.win32-x86-cygwin.Common b/configure/os/CONFIG.win32-x86-cygwin.Common new file mode 100644 index 000000000..766039b4b --- /dev/null +++ b/configure/os/CONFIG.win32-x86-cygwin.Common @@ -0,0 +1,22 @@ +# CONFIG.win32-x86-cygwin.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86-cygwin host archs +# Sites may override these definitions in CONFIG_SITE.win32-x86-cygwin.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + +HOSTEXE=.exe + +WIND_HOST_TYPE = x86-win32 + +# osithead use default stack, YES or NO override +OSITHREAD_USE_DEFAULT_STACK = NO + +# Needed to find dlls for base installed build tools (antelope,eflex,...) +PATH := $(EPICS_BASE_BIN):$(PATH) + diff --git a/configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin b/configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin new file mode 100644 index 000000000..37ae3e986 --- /dev/null +++ b/configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin @@ -0,0 +1,28 @@ +# CONFIG.win32-x86-cygwin.win32-x86-cygwin +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Definitions for win32-x86-cygwin host - win32-x86-cygwin target builds +# Sites may override these definitions in CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +# cygwin's gcc, g++, ar, ld, and ranlib must be in user's path +CC = gcc +CCC = g++ +AR = ar -rc +LD = ld -r +RANLIB = ranlib +RES=.coff +RCCMD = windres $(INCLUDES) $< $@ + +# No -fPIC avoids "-fPIC ignored for target (all code is position independent)" +SHRLIB_CFLAGS = +SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) +LOADABLE_SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) + +# Override linking with gcc library from CONFIG.gnuCommon +GNU_LDLIBS_YES = + diff --git a/configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin-debug b/configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin-debug new file mode 100644 index 000000000..80f051739 --- /dev/null +++ b/configure/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin-debug @@ -0,0 +1,14 @@ +# CONFIG.win32-x86-cygwin.win32-x86-cygwin-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86-cygwin compiler host - win32-x86-cygwin debug compiler target builds +# Sites may override these definitions in CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.Common.win32-x86-cygwin +include $(CONFIG)/os/CONFIG.win32-x86-cygwin.win32-x86-cygwin + +BUILD_CLASS=HOST +HOST_OPT=NO diff --git a/configure/os/CONFIG.win32-x86-debug.Common b/configure/os/CONFIG.win32-x86-debug.Common new file mode 100644 index 000000000..8464356d1 --- /dev/null +++ b/configure/os/CONFIG.win32-x86-debug.Common @@ -0,0 +1,12 @@ +# CONFIG.win32-x86-debug.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86 debug with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.win32-x86-debug.Common +#------------------------------------------------------- + +#Include definitions common to win32-x86 hosts +include $(CONFIG)/os/CONFIG.win32-x86.Common + diff --git a/configure/os/CONFIG.win32-x86-debug.win32-x86-debug b/configure/os/CONFIG.win32-x86-debug.win32-x86-debug new file mode 100644 index 000000000..1f28a01dc --- /dev/null +++ b/configure/os/CONFIG.win32-x86-debug.win32-x86-debug @@ -0,0 +1,12 @@ +# CONFIG.win32-x86-debug.win32-x86-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86 debug compiler host - win32-x86 debug compiler target builds +# Sites may override these definitions in CONFIG_SITE.win32-x86-debug.win32-x86-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 + +HOST_OPT=NO diff --git a/configure/os/CONFIG.win32-x86-mingw.Common b/configure/os/CONFIG.win32-x86-mingw.Common new file mode 100644 index 000000000..e78bc3c5d --- /dev/null +++ b/configure/os/CONFIG.win32-x86-mingw.Common @@ -0,0 +1,26 @@ +# CONFIG.win32-x86-mingw.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86-cygwin host archs +# Sites may override these definitions in CONFIG_SITE.win32-x86-cygwin.Common +#------------------------------------------------------- + +#Include definitions common to unix hosts +include $(CONFIG)/os/CONFIG.UnixCommon.Common + +CP = $(PERL) -MExtUtils::Command -e cp +MV = $(PERL) -MExtUtils::Command -e mv +RM = $(PERL) -MExtUtils::Command -e rm_f +MKDIR = $(PERL) -MExtUtils::Command -e mkpath +RMDIR = $(PERL) -MExtUtils::Command -e rm_rf + +WIND_HOST_TYPE = x86-win32 +OSITHREAD_USE_DEFAULT_STACK = NO + +HOSTEXE=.exe + +# Needed to find dlls for base installed build tools (antelope,eflex,...) +PATH := $(EPICS_BASE_BIN):$(PATH) + diff --git a/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw b/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw new file mode 100644 index 000000000..cfef76c9e --- /dev/null +++ b/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw @@ -0,0 +1,29 @@ +# CONFIG.win32-x86-mingw.win32-x86-mingw +# +# Revision-Id: jba@aps.anl.gov-20101108141612-a8fp39dp7wnk5hu5 +# +# Definitions for win32-x86-mingw host - win32-x86-mingw target builds +# Sites may override these definitions in CONFIG_SITE.win32-x86-mingw.win32-x86-mingw +#------------------------------------------------------- + +# Include common gnu compiler definitions +include $(CONFIG)/CONFIG.gnuCommon + +# gcc, g++, ar, ld, and ranlib must be in user's path +CC = gcc +CCC = g++ +AR = ar -rc +LD = ld -r +RANLIB = ranlib +RES=.coff +RCCMD = windres $(INCLUDES) $< $@ + +# No -fPIC avoids "-fPIC ignored for target (all code is position independent)" +SHRLIB_CFLAGS = +SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) +LOADABLE_SHRLIB_LDFLAGS = -shared -Wl,--out-implib,$(LIB_PREFIX)$*$(LIB_SUFFIX) + +# Override linking with gcc library from CONFIG.gnuCommon +GNU_LDLIBS_YES = + +OP_SYS_LDLIBS = -lws2_32 diff --git a/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw-debug b/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw-debug new file mode 100644 index 000000000..41db82bf9 --- /dev/null +++ b/configure/os/CONFIG.win32-x86-mingw.win32-x86-mingw-debug @@ -0,0 +1,17 @@ +# CONFIG.win32-x86-mingw.win32-x86-mingw-debug +# +# Revision-Id: jba@aps.anl.gov-20101108141858-3elofx068sjvihtp +# This file is maintained by the build community. +# +# Definitions for win32-x86-mingw compiler host - win32-x86-mingw debug compiler target builds +# Sites may override these definitions in CONFIG_SITE.win32-x86-mingw.win32-x86-mingw-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.win32-x86-mingw +-include $(CONFIG)/os/CONFIG.win32-x86-mingw.win32-x86-mingw +-include $(CONFIG)/os/CONFIG_SITE.Common.win32-x86-mingw +-include $(CONFIG)/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw + +BUILD_CLASS = HOST + +HOST_OPT=NO diff --git a/configure/os/CONFIG.win32-x86.Common b/configure/os/CONFIG.win32-x86.Common new file mode 100644 index 000000000..1cf2418ef --- /dev/null +++ b/configure/os/CONFIG.win32-x86.Common @@ -0,0 +1,29 @@ +# CONFIG.win32-x86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for win32-x86 host archs +# Sites may override these definitions in CONFIG_SITE.win32-x86.Common +#------------------------------------------------------- + +CP = $(PERL) -MExtUtils::Command -e cp +MV = $(PERL) -MExtUtils::Command -e mv +RM = $(PERL) -MExtUtils::Command -e rm_f +MKDIR = $(PERL) -MExtUtils::Command -e mkpath +RMDIR = $(PERL) -MExtUtils::Command -e rm_rf + +WIND_HOST_TYPE = x86-win32 +OSITHREAD_USE_DEFAULT_STACK = NO + +HOSTEXE=.exe + +# Does not work if using cygwin make +# because HOME is always defined +ifndef HOME +HOME = $(HOMEDRIVE)$(HOMEPATH) +endif + +# Needed to find dlls for base installed build tools (antelope,eflex,...) +PATH := $(EPICS_BASE_BIN):$(PATH) + diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 new file mode 100644 index 000000000..091161c69 --- /dev/null +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -0,0 +1,294 @@ +# CONFIG.win32-x86.win32-x86 +# +# Revision-Id: jba@aps.anl.gov-20101021200611-k20b8rbfxat4nk3i +# This file is maintained by the build community. +# +# Definitions for win32-x86 target archs when host arch is win32-x86 +# Sites may override these definitions in CONFIG_SITE.win32-x86.win32-x86 +#------------------------------------------------------- + +# Win32 valid build types and include directory suffixes + +VALID_BUILDS = Host Ioc + +# convert UNIX path to native path +PATH_FILTER = $(subst /,\\,$(1)) + +#------------------------------------------------------- + +# +# "\ " forces gnu make to keep this as one token +# +WINLINK = link + +RCCMD = rc -l 0x409 $(INCLUDES) -fo $@ $< + +ARCMD = lib /nologo /verbose /out:$@ $(LIB_OPT_LDFLAGS) $(LIBRARY_LD_OBJS) + +BAFCMD = bscmake /nologo /o $@ + +# +# Configure OS vendor C compiler +CC = cl + +GNU = NO + +# +# /W use warning level N +# (maximum (lint type) warnings at level 4) +# /w44355 set "'this' used in the base initializer list" to be level 4 +WARN_CFLAGS_YES = /W3 /w44355 +WARN_CFLAGS_NO = /W1 + +# +# /Ox maximum optimizations +# /MD use MSVCRT (run-time as DLL, multi-thread support) +# /GL whole program optimization +# /Zi generate program database for debugging information +OPT_CFLAGS_YES = /Ox /GL + +# +# /Zi generate program database for debugging information +# /Z7 include debugging info in object files +# /Fr create source browser file +# /GZ catch bugs occurring only in optimized code +# /D_CRTDBG_MAP_ALLOC +# /RTCsu catch bugs occuring only inoptimized code +# /DEPICS_FREELIST_DEBUG good for detecting mem mrg bugs +OPT_CFLAGS_NO = /Zi /RTCsu + +# specify object file name and location +OBJ_CFLAG = /Fo + +# +# the following options are required when +# vis c++ compiles the code (and includes +# the header files) +# +# /MT static multithreaded C RTL +# /MTd static multithreaded C RTL (debug version) +# /MD multithreaded C RTL in DLL +# /MDd multithreaded C RTL in DLL (debug version) +VISC_DLL_NO = -DEPICS_DLL_NO +VISC_DLL_YES = +VISC_DLL = $(VISC_DLL_$(SHARED_LIBRARIES)) +VISC_STATIC_CFLAGS_DEBUG_NO = d +VISC_STATIC_CFLAGS_DEBUG_YES = +VISC_STATIC_CFLAGS_DEBUG = $(VISC_STATIC_CFLAGS_DEBUG_$(HOST_OPT)) +STATIC_CFLAGS_YES= /MT$(VISC_STATIC_CFLAGS_DEBUG) $(VISC_DLL) +STATIC_CFLAGS_NO= /MD$(VISC_STATIC_CFLAGS_DEBUG) $(VISC_DLL) + +# OS vendor c preprocessor +CPP = cl /C /E +#GNU c preprocessor +#CPP = gcc -x c -E + +# Configure OS vendor C++ compiler +# +# __STDC__=0 is a real great idea of Jeff that gives us both: +# 1) define STDC for code (pretend ANSI conformance) +# 2) set it to 0 to use MS C "extensions" (open for _open etc.) +# because MS uses: if __STDC__ ... disable many nice things +# +# Use of /Za would dissable DLL import/export keywords which +# include/excludes using architecture neutral macros +# +# /EHsc - generate code for exceptions +# /GR - generate code for run time type identification +# +CCC = cl /nologo /EHsc /GR +CODE_CPPFLAGS += /nologo /D__STDC__=0 +CODE_CPPFLAGS += /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE + +# +# /W use warning level N +# (maximum lint level warnings at level 4) +# /w44355 set "'this' used in the base initializer list" to be level 4 +WARN_CXXFLAGS_YES = /W3 /w44355 +WARN_CXXFLAGS_NO = /W1 + +# +# /Ox maximum optimizations +# /GL whole program optimization +# /Zi generate program database for debugging information +OPT_CXXFLAGS_YES = /Ox /GL + +# +# /Zi generate program database for debugging information +# /Z7 include debugging info in object files +# /Fr create source browser file +# /D_CRTDBG_MAP_ALLOC +# /RTCsu catch bugs occurring only in optimized code +# /DEPICS_FREELIST_DEBUG good for detecting mem mrg bugs +OPT_CXXFLAGS_NO = /RTCsu /Zi + +# specify object file name and location +OBJ_CXXFLAG = /Fo + +# +# the following options are required when +# vis c++ compiles the code (and includes +# the header files) +# +# /MT static multithreaded C RTL +# /MTd static multithreaded C RTL (debug version) +# /MD multithreaded C RTL in DLL +# /MDd multithreaded C RTL in DLL (debug version) +STATIC_CXXFLAGS_YES= /MT$(VISC_STATIC_CFLAGS_DEBUG) $(VISC_DLL) +STATIC_CXXFLAGS_NO= /MD$(VISC_STATIC_CFLAGS_DEBUG) $(VISC_DLL) + +STATIC_LDLIBS_YES=ws2_32.lib advapi32.lib user32.lib kernel32.lib winmm.lib +STATIC_LDLIBS_NO= +STATIC_LDFLAGS= +RANLIB= + +# add /profile here to run the ms profiler +# /LTCG - whole program optimization +# /fixed:no good for programs such as purify and quantify +# /debug good for programs such as purify and quantify +LINK_OPT_FLAGS_YES = /LTCG /incremental:no /opt:ref /release $(PROD_VERSION:%=/version:%) +LINK_OPT_FLAGS_NO = /debug /incremental:no /fixed:no +OPT_LDFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT)) +LIB_OPT_FLAGS_YES = /LTCG +LIB_OPT_LDFLAGS = $(LIB_OPT_FLAGS_$(HOST_OPT)) + +ARCH_DEP_CFLAGS= +SHRLIB_CFLAGS= + +OS_CLASS=WIN32 +POSIX=NO + +# ifdef WIN32 looks better that ifeq ($(OS_CLASS),WIN32) ?? +WIN32=1 + +EXE=.exe +OBJ=.obj +RES=.res +BAF=.bsc +BOF=.sbr + +# Problem: MS Visual C++ does not recognize *.cc as C++ source, +# so we do C++ compiles using the global flag -TP +COMPILER_CXXFLAGS = /TP + +# Operating system flags +OP_SYS_CFLAGS = +OP_SYS_CXXFLAGS = $(COMPILER_CXXFLAGS) + +# +# WIN32 specific include files +# +#OP_SYS_INCLUDES = -I$(INSTALL_INCLUDE)\\os\\WIN32 + +# Files and flags needed to link DLLs (used in RULES_BUILD) +# +# Strange but seems to work without: WIN32_DLLFLAGS should contain +# an entry point: +# '-entry:_DllMainCRTStartup$(DLLENTRY)' +DLLENTRY = @12 + +WIN32_DLLFLAGS = /subsystem:windows /dll $(OPT_LDFLAGS) $(USR_LDFLAGS) $(TARGET_LDFLAGS) $(LIB_LDFLAGS) + +# +# specify dll .def file only if it exists +# +DLL_DEF_FLAG = $(addprefix /def:,$(wildcard ../$(addsuffix .def,$*))) + +# +# A WIN32 dll has three parts: +# x.dll: the real dll (SHRLIBNAME) +# x.lib: what you link to progs that use the dll (DLL_LINK_LIBNAME) +# x.exp: what you need to build the dll (in no variable) +# +LINK.shrlib = $(WINLINK) /nologo $(WIN32_DLLFLAGS) /implib:$*.lib /out:$*.dll $(DLL_DEF_FLAG) +LINK.shrlib += $(call PATH_FILTER, $(LIBRARY_LD_OBJS) $(LIBRARY_LD_RESS) $(SHRLIB_LDLIBS)) +MUNCH_CMD = $(CCC) /Fo $@ $^ + + +# adjust names of libraries to build +# +# But: if there are no objects LIBRARY_LD_OBJS to include +# in this library (may be for e.g. base/src/libCompat +# on some archs), don't define (and build) any library! +SHRLIB_SUFFIX=.dll +SHRLIBNAME_YES = $(BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) +LOADABLE_SHRLIBNAME = $(LOADABLE_BUILD_LIBRARY:%=%$(SHRLIB_SUFFIX)) + +# +# When SHARED_LIBRARIES is YES we are building a DLL link library +# and when SHARED_LIBRARIES is NO we are building an object library +# +DLL_LINK_LIBNAME_YES = $(BUILD_LIBRARY:%=%.lib) +DLL_LINK_LIBNAME = $(DLL_LINK_LIBNAME_$(SHARED_LIBRARIES)) +LIB_PREFIX= +LIB_SUFFIX=.lib +LIBNAME_NO = $(BUILD_LIBRARY:%=%.lib) +LIBNAME = $(LIBNAME_$(SHARED_LIBRARIES)) + +# dll install location +INSTALL_SHRLIB = $(INSTALL_BIN) + + +#-------------------------------------------------- +# Products dependancy definitions + +# SYS_PROD_LIBS deprecated +# Use PROD_SYS_LIBS + +PROD_DEPLIBS=$(foreach lib, $(PROD_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +PROD_LDLIBS += $($*_DEPLIBS) $(PROD_DEPLIBS) +PROD_LDLIBS += $(addsuffix .lib, \ + $($*_SYS_LIBS) $(PROD_SYS_LIBS) $(USR_SYS_LIBS) ) + +LDLIBS_STATIC_YES = LDLIBS +LDLIBS_SHARED_NO = LDLIBS +PROD_LDLIBS += $(STATIC_LDLIBS) $($(firstword $(LDLIBS_STATIC_$(STATIC_BUILD)) \ + $(LDLIBS_SHARED_$(SHARED_LIBRARIES)))) + +#-------------------------------------------------- +#Libraries dependancy definitions + +# _DLL_LIBS _SYS_DLL_LIBS deprecated. +# Use _LIBS and _SYS_LIBS. +# DLL_LIBS, and SYS_DLL_LIBS deprecated +# Use LIB_LIBS and LIB_SYS_LIBS +LIB_LIBS += $(DLL_LIBS) +LIB_SYS_LIBS += $(SYS_DLL_LIBS) + +# libs that we need to link the DLL with +# (it isnt necessary to rebuild the dll if these change) + +SHRLIB_DEPLIBS += $(foreach lib, $(LIB_LIBS) $(USR_LIBS), \ + $(firstword $(wildcard $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $($(lib)_DIR) $(SHRLIB_SEARCH_DIRS))) \ + $(addsuffix /$(LIB_PREFIX)$(lib)$(LIB_SUFFIX), \ + $(firstword $($(lib)_DIR) $(INSTALL_LIB))))) + +SHRLIB_LDLIBS += $($*_DLL_DEPLIBS) $($*_DEPLIBS) $(SHRLIB_DEPLIBS) +SHRLIB_LDLIBS += $(addsuffix .lib, \ + $($*_SYS_DLL_LIBS) \ + $($*_SYS_LIBS) $(LIB_SYS_LIBS) $(USR_SYS_LIBS) ) + +#-------------------------------------------------- +# Linker definition +LINK.cpp = $(WINLINK) -nologo $(STATIC_LDFLAGS) $(LDFLAGS) $(PROD_LDFLAGS) -out:$@ \ + $(call PATH_FILTER, $(PROD_LD_OBJS) $(PROD_LD_RESS) $(PROD_LDLIBS)) + +#-------------------------------------------------- +# UseManifestTool.pl checks MS Visual c++ compiler version number to +# decide whether or not to use the Manifest Tool command to embed the +# linker created .manifest file into a library or product target. +# useManifestTool.pl returns 0(don't use) or 1(use). +# +MT_DLL_COMMAND1 = mt.exe /manifest $@.manifest "/outputresource:$@;\#2" +MT_EXE_COMMAND_YES = +MT_EXE_COMMAND_NO = mt.exe /manifest $@.manifest "/outputresource:$@;\#1" +MT_EXE_COMMAND1 = $(MT_EXE_COMMAND_$(STATIC_BUILD)) +MT_DLL_COMMAND = $(MT_DLL_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl)) +MT_EXE_COMMAND = $(MT_EXE_COMMAND$(shell $(PERL) $(TOOLS)/useManifestTool.pl)) + diff --git a/configure/os/CONFIG.win32-x86.win32-x86-debug b/configure/os/CONFIG.win32-x86.win32-x86-debug new file mode 100644 index 000000000..e7511d730 --- /dev/null +++ b/configure/os/CONFIG.win32-x86.win32-x86-debug @@ -0,0 +1,20 @@ +# CONFIG.win32-x86.win32-x86-debug +# +# Revision-Id: jba@ctlpc26-20101115194317-os43t1uon2i6t2gv +# This file is maintained by the build community. +# +# Definitions for win32-x86 host - win32-x86-debug target build +# Sites may override these definitions in CONFIG_SITE.win32-x86.win32-x86-debug +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.win32-x86 +-include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 +-include $(CONFIG)/os/CONFIG_SITE.Common.win32-x86 +-include $(CONFIG)/os/CONFIG_SITE.win32-x86.win32-x86 + +GNU = NO + +HDEPENDS_METHOD = CMD +BUILD_CLASS = HOST + +HOST_OPT = NO diff --git a/configure/os/CONFIG.win32-x86.windows-x64 b/configure/os/CONFIG.win32-x86.windows-x64 new file mode 100644 index 000000000..0cc7be16b --- /dev/null +++ b/configure/os/CONFIG.win32-x86.windows-x64 @@ -0,0 +1,28 @@ +# CONFIG.win-x86.windows-x64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for windows-x64 target archs when host arch is win32-x86 +# Sites may override these definitions in CONFIG_SITE.win32-x86.windows-x64 +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.Common.win32-x86 +-include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 + +ARCH_DEP_CPPFLAGS += /favor:blend +#ARCH_DEP_CPPFLAGS += /Wp64 + +# /favor:blend both AMD64 and INTEL64 +# /favor:AMD64 +# /favor:INTEL64 (new value) +# /favor:EN64T (old value) + +OPT_LDFLAGS += /MACHINE:X64 + +# /MACHINE:X64 +# /MACHINE:IA64 (Itanium) +# /MACHINE:X86 + +#The following option does not work +#ARCH_DEP_CPPFLAGS += /env x64 diff --git a/configure/os/CONFIG.windows-x64-debug.Common b/configure/os/CONFIG.windows-x64-debug.Common new file mode 100644 index 000000000..ddc917cd0 --- /dev/null +++ b/configure/os/CONFIG.windows-x64-debug.Common @@ -0,0 +1,12 @@ +# CONFIG.windows-x64-debug.Common +# +# Revision-Id: jba@aps.anl.gov-20101026195902-0ofp7t77eo7tpr1j +# This file is maintained by the build community. +# +# Definitions for windows-x64 debug with debug compiler flags +# Sites may override these definitions in CONFIG_SITE.windows-x64-debug.Common +#------------------------------------------------------- + +#Include definitions common to windows-x64 hosts +include $(CONFIG)/os/CONFIG.windows-x64.Common + diff --git a/configure/os/CONFIG.windows-x64-debug.windows-x64-debug b/configure/os/CONFIG.windows-x64-debug.windows-x64-debug new file mode 100644 index 000000000..33b0567c2 --- /dev/null +++ b/configure/os/CONFIG.windows-x64-debug.windows-x64-debug @@ -0,0 +1,12 @@ +# CONFIG.windows-x64-debug.windows-x64-debug +# +# Revision-Id: jba@aps.anl.gov-20101026195902-0ofp7t77eo7tpr1j +# This file is maintained by the build community. +# +# Definitions for windows-x64 debug compiler host - windows-x64 debug compiler target builds +# Sites may override these definitions in CONFIG_SITE.windows-x64-debug.windows-x64-debug +#------------------------------------------------------- + +include $(CONFIG)/os/CONFIG.windows-x64.windows-x64 + +HOST_OPT=NO diff --git a/configure/os/CONFIG.windows-x64.Common b/configure/os/CONFIG.windows-x64.Common new file mode 100644 index 000000000..901a0ea1a --- /dev/null +++ b/configure/os/CONFIG.windows-x64.Common @@ -0,0 +1,13 @@ +# CONFIG.windows-x64.Common +# +# Revision-Id: anj@aps.anl.gov-20101025192356-roqyubib1aep8k14 +# This file is maintained by the build community. +# +# Definitions for windows-x64 host archs +# Sites may override these definitions in CONFIG_SITE.windows-x64.Common +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.win32-x86.Common + +WIND_HOST_TYPE = x86-win32 + diff --git a/configure/os/CONFIG.windows-x64.windows-x64 b/configure/os/CONFIG.windows-x64.windows-x64 new file mode 100644 index 000000000..e04975497 --- /dev/null +++ b/configure/os/CONFIG.windows-x64.windows-x64 @@ -0,0 +1,25 @@ +# CONFIG.windows-x64.windows-x64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for windows-x64 target archs when host arch is windows-x64 +# Sites may override these definitions in CONFIG_SITE.windows-x64.windows-x64 +#------------------------------------------------------- + +-include $(CONFIG)/os/CONFIG.win32-x86.win32-x86 + +ARCH_DEP_CPPFLAGS += /favor:blend +#ARCH_DEP_CPPFLAGS += /Wp64 + +# /favor:blend both AMD64 and INTEL64 +# /favor:AMD64 +# /favor:INTEL64 (new value) +# /favor:EN64T (old value) + +OPT_LDFLAGS += /MACHINE:X64 + +# /MACHINE:X64 +# /MACHINE:IA64 (Itanium) +# /MACHINE:X86 + diff --git a/configure/os/CONFIG_COMPAT b/configure/os/CONFIG_COMPAT new file mode 100644 index 000000000..b1fc1173d --- /dev/null +++ b/configure/os/CONFIG_COMPAT @@ -0,0 +1,26 @@ +# CONFIG_COMPAT +# +# Revision-Id: anj@aps.anl.gov-20101014190140-s9gxgtnoyhbck6id +# +# Convert old HOST_ARCH environment variable to +# new EPICS_HOST_ARCH environment variable +#------------------------------------------------------- + +#Syntax: +# ARCH_ = +ARCH_solaris = solaris-sparc +ARCH_solarisGnu = solaris-sparc-gnu +ARCH_Linux = linux-x86 +ARCH_Darwin = darwin-ppc +ARCH_WIN32 = win32-x86 +ARCH_cygwin32 = cygwin-x86 +ARCH_Borland = win32-x86-borland +ARCH_freebsd = freebsd-x86 + +ifndef EPICS_HOST_ARCH +ifdef HOST_ARCH +EPICS_HOST_ARCH = $(firstword $(ARCH_$(HOST_ARCH)) $(HOST_ARCH)) +else +EPICS_HOST_ARCH = unsupported +endif +endif diff --git a/configure/os/CONFIG_SITE.Common.RTEMS b/configure/os/CONFIG_SITE.Common.RTEMS new file mode 100644 index 000000000..bf6195bcf --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.RTEMS @@ -0,0 +1,25 @@ +# +# Site-specific information for all RTEMS targets +# +#------------------------------------------------------- + +# Where to find RTEMS +# +RTEMS_VERSION=4.9.2 +RTEMS_BASE=/usr/local/rtems/rtems-$(RTEMS_VERSION) + +# Cross-compile toolchain in $(RTEMS_TOOLS)/bin +# +RTEMS_TOOLS=$(RTEMS_BASE) + + +# If you're using neither BOOTP/DHCP nor FLASH to pick up your IOC +# network configuration you must uncomment and specify your Internet +# Domain Name here +# +#OP_SYS_CFLAGS += -DRTEMS_NETWORK_CONFIG_DNS_DOMAINNAME= + +# +# Specify your desired command-line-input library +# +COMMANDLINE_LIBRARY = EPICS diff --git a/configure/os/CONFIG_SITE.Common.RTEMS-pc386 b/configure/os/CONFIG_SITE.Common.RTEMS-pc386 new file mode 100644 index 000000000..c772c44fc --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.RTEMS-pc386 @@ -0,0 +1,3 @@ +# +# Site-specific overrides for RTEMS-pc386 target +# diff --git a/configure/os/CONFIG_SITE.Common.cygwin-x86 b/configure/os/CONFIG_SITE.Common.cygwin-x86 new file mode 100644 index 000000000..e0eb585ec --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.cygwin-x86 @@ -0,0 +1,10 @@ +# CONFIG_SITE.Common.cygwin-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for cygwin-x86 target +# Only the local epics system manager should modify this file + +# If readline is installed uncomment the following line +# to add command-line editing and history support +COMMANDLINE_LIBRARY = READLINE diff --git a/configure/os/CONFIG_SITE.Common.darwin-ppc b/configure/os/CONFIG_SITE.Common.darwin-ppc new file mode 100644 index 000000000..a60f84e68 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.darwin-ppc @@ -0,0 +1,15 @@ +# CONFIG_SITE.Common.darwin-ppc +# +# Revision-Id: anj@aps.anl.gov-20101012202556-m22qur2eligwq95h +# This file is maintained by the build community. +# +# Site override definitions for darwin-ppc target builds +#------------------------------------------------------- + +# Select which CPU architectures to include in your universal binaries: +# ppc +# ppc64 - Not tested + +ARCH_CLASS = ppc +#ARCH_CLASS = ppc64 +#ARCH_CLASS = ppc ppc64 diff --git a/configure/os/CONFIG_SITE.Common.darwin-ppcx86 b/configure/os/CONFIG_SITE.Common.darwin-ppcx86 new file mode 100644 index 000000000..f198aa465 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.darwin-ppcx86 @@ -0,0 +1,23 @@ +# CONFIG_SITE.Common.darwin-ppcx86 +# +# Revision-Id: anj@aps.anl.gov-20101012202556-m22qur2eligwq95h +# This file is maintained by the build community. +# +# Site override definitions for darwin-ppcx86 target builds +#---------------------------------------------------------- + +# Select which CPU architectures to include in your universal binaries: +# ppc +# i386 +# ppc64 - Not tested +# x86_64 - Needs MacOS 10.4 with Universal SDK, or 10.5 or later. + +ARCH_CLASS = ppc i386 +#ARCH_CLASS = ppc x86_64 +#ARCH_CLASS = ppc i386 x86_64 +#ARCH_CLASS = ppc64 i386 +#ARCH_CLASS = ppc64 x86_64 +#ARCH_CLASS = ppc64 i386 x86_64 +#ARCH_CLASS = ppc ppc64 i386 +#ARCH_CLASS = ppc ppc64 x86_64 +#ARCH_CLASS = ppc ppc64 i386 x86_64 diff --git a/configure/os/CONFIG_SITE.Common.darwin-x86 b/configure/os/CONFIG_SITE.Common.darwin-x86 new file mode 100644 index 000000000..c2fc5070d --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.darwin-x86 @@ -0,0 +1,15 @@ +# CONFIG_SITE.Common.darwin-x86 +# +# Revision-Id: anj@aps.anl.gov-20101012202556-m22qur2eligwq95h +# This file is maintained by the build community. +# +# Site override definitions for darwin-x86 target builds +#------------------------------------------------------- + +# Select which CPU architecture(s) to include in your MacOS binaries: +# i386 +# x86_64 - Needs MacOS 10.4 with the Universal SDK, or 10.5 and later + +ARCH_CLASS = i386 +#ARCH_CLASS = x86_64 +#ARCH_CLASS = i386 x86_64 diff --git a/configure/os/CONFIG_SITE.Common.iosCommon b/configure/os/CONFIG_SITE.Common.iosCommon new file mode 100644 index 000000000..006931563 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.iosCommon @@ -0,0 +1,24 @@ +# CONFIG_SITE.Common.iosCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site-specific settings for Apple iOS builds +#------------------------------------------------------- + +# iOS Version number + +#IOS_VERSION = 3.2 +IOS_VERSION = 4.1 + + +# Most sites will want shared libraries + +STATIC_BUILD=NO +SHARED_LIBRARIES=YES + + +# Platform path, this is probably correct + +XCODE_PATH := $(shell xcode-select -print-path) +PLATFORM_DIR = $(XCODE_PATH)/Platforms/$(IOS_PLATFORM).platform diff --git a/configure/os/CONFIG_SITE.Common.linux-cris b/configure/os/CONFIG_SITE.Common.linux-cris new file mode 100644 index 000000000..7e2f95448 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.linux-cris @@ -0,0 +1,37 @@ +# CONFIG_SITE.Common.linux-cris +# +# Revision-Id: anj@aps.anl.gov-20101122164049-brpnm0uwvpptmw80 +# +# Site Specific definitions for linux-cris target +# Only the local epics system manager should modify this file + +# NOTE for SHARED_LIBRARIES: In most cases if this is set to YES the +# shared libraries will be found automatically. However if the .so +# files are installed at a different path to their compile-time path +# then in order to be found at runtime do one of these: +# a) LD_LIBRARY_PATH must include the full absolute pathname to +# $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking base +# executables. +# b) Add the runtime path to SHRLIB_DEPLIB_DIRS and PROD_DEPLIB_DIRS, which +# will add the named directory to the list contained in the executables. +# c) Add the runtime path to /etc/ld.so.conf and run ldconfig +# to inform the system of the shared library location. + +# Depending on your version of Linux you may want one of the following +# lines to enable command-line editing and history in iocsh. If you're +# not sure which, start with the top one and work downwards until the +# build doesn't fail to link the readline library. If none of them work, +# comment them all out to build without readline support. + +# No other libraries needed (recent Fedora, Ubuntu etc.): +#COMMANDLINE_LIBRARY = READLINE + +# Needs -lncurses (RHEL 5 etc.): +#COMMANDLINE_LIBRARY = READLINE_NCURSES + +# Needs -lcurses (older versions) +#COMMANDLINE_LIBRARY = READLINE_CURSES + + +OP_SYS_CFLAGS += -g + diff --git a/configure/os/CONFIG_SITE.Common.linux-x86 b/configure/os/CONFIG_SITE.Common.linux-x86 new file mode 100644 index 000000000..b404b368c --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.linux-x86 @@ -0,0 +1,36 @@ +# CONFIG_SITE.Common.linux-x86 +# +# Revision-Id: anj@aps.anl.gov-20101122164049-brpnm0uwvpptmw80 +# +# Site Specific definitions for linux-x86 target +# Only the local epics system manager should modify this file + +# NOTE for SHARED_LIBRARIES: In most cases if this is set to YES the +# shared libraries will be found automatically. However if the .so +# files are installed at a different path to their compile-time path +# then in order to be found at runtime do one of these: +# a) LD_LIBRARY_PATH must include the full absolute pathname to +# $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking base +# executables. +# b) Add the runtime path to SHRLIB_DEPLIB_DIRS and PROD_DEPLIB_DIRS, which +# will add the named directory to the list contained in the executables. +# c) Add the runtime path to /etc/ld.so.conf and run ldconfig +# to inform the system of the shared library location. + +# Depending on your version of Linux you'll want one of the following +# lines to enable command-line editing and history in iocsh. If you're +# not sure which, start with the top one and work downwards until the +# build doesn't fail to link the readline library. If none of them work, +# comment them all out to build without readline support. + +# No other libraries needed (recent Fedora, Ubuntu etc.): +COMMANDLINE_LIBRARY = READLINE + +# Needs -lncurses (RHEL 5 etc.): +#COMMANDLINE_LIBRARY = READLINE_NCURSES + +# Needs -lcurses (older versions) +#COMMANDLINE_LIBRARY = READLINE_CURSES + + +OP_SYS_CFLAGS += -g diff --git a/configure/os/CONFIG_SITE.Common.linux-x86_64 b/configure/os/CONFIG_SITE.Common.linux-x86_64 new file mode 100644 index 000000000..00dcd1bd2 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.linux-x86_64 @@ -0,0 +1,36 @@ +# CONFIG_SITE.Common.linux-x86_64 +# +# Revision-Id: anj@aps.anl.gov-20101122164049-brpnm0uwvpptmw80 +# +# Site Specific definitions for linux-x86_64 target +# Only the local epics system manager should modify this file + +# NOTE for SHARED_LIBRARIES: In most cases if this is set to YES the +# shared libraries will be found automatically. However if the .so +# files are installed at a different path to their compile-time path +# then in order to be found at runtime do one of these: +# a) LD_LIBRARY_PATH must include the full absolute pathname to +# $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking base +# executables. +# b) Add the runtime path to SHRLIB_DEPLIB_DIRS and PROD_DEPLIB_DIRS, which +# will add the named directory to the list contained in the executables. +# c) Add the runtime path to /etc/ld.so.conf and run ldconfig +# to inform the system of the shared library location. + +# Depending on your version of Linux you'll want one of the following +# lines to enable command-line editing and history in iocsh. If you're +# not sure which, start with the top one and work downwards until the +# build doesn't fail to link the readline library. If none of them work, +# comment them all out to build without readline support. + +# No other libraries needed (recent Fedora, Ubuntu etc.): +COMMANDLINE_LIBRARY = READLINE + +# Needs -lncurses (RHEL 5 etc.): +#COMMANDLINE_LIBRARY = READLINE_NCURSES + +# Needs -lcurses (older versions) +#COMMANDLINE_LIBRARY = READLINE_CURSES + + +OP_SYS_CFLAGS += -g diff --git a/configure/os/CONFIG_SITE.Common.solaris-sparc b/configure/os/CONFIG_SITE.Common.solaris-sparc new file mode 100644 index 000000000..4cfdf8369 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.solaris-sparc @@ -0,0 +1,18 @@ +# CONFIG_SITE.Common.solaris-sparc +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for solaris-sparc target +# Only the local epics system manager should modify this file + +# If readline is installed uncomment the following macro definition +# to include command-line editing and history support +# + +# Readline library provides command-line editing and history in IOC shell +#COMMANDLINE_LIBRARY = READLINE + +# Use stLport library instead of default Cstd library +# Must be either YES or NO +#USE_STLPORT=YES + diff --git a/configure/os/CONFIG_SITE.Common.solaris-sparc-gnu b/configure/os/CONFIG_SITE.Common.solaris-sparc-gnu new file mode 100644 index 000000000..c15c488d5 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.solaris-sparc-gnu @@ -0,0 +1,15 @@ +# CONFIG_SITE.Common.solaris-sparc-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for solaris-sparc-gnu target +# Only the local epics system manager should modify this file + +# Include definitions common to all solaris-sparc-gnu target archs +include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc + +# solaris 10 default location +#GNU_DIR=/usr/sfw + +# APS site override +GNU_DIR = /usr/local diff --git a/configure/os/CONFIG_SITE.Common.solaris-sparc64 b/configure/os/CONFIG_SITE.Common.solaris-sparc64 new file mode 100644 index 000000000..be1cd2b67 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.solaris-sparc64 @@ -0,0 +1,11 @@ +# CONFIG_SITE.Common.solaris-sparc64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for solaris-sparc64 target +# Only the local epics system manager should modify this file + +# Include definitions common to all solaris-sparc64 target archs +include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc + +COMMANDLINE_LIBRARY = EPICS diff --git a/configure/os/CONFIG_SITE.Common.solaris-sparc64-gnu b/configure/os/CONFIG_SITE.Common.solaris-sparc64-gnu new file mode 100644 index 000000000..8e98cd12e --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.solaris-sparc64-gnu @@ -0,0 +1,11 @@ +# CONFIG_SITE.Common.solaris-sparc64-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for solaris-sparc64-gnu target +# Only the local epics system manager should modify this file + +# Include definitions common to all solaris-sparc-gnu target archs +include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc-gnu + +COMMANDLINE_LIBRARY = EPICS diff --git a/configure/os/CONFIG_SITE.Common.solaris-x86-gnu b/configure/os/CONFIG_SITE.Common.solaris-x86-gnu new file mode 100644 index 000000000..e350c797f --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.solaris-x86-gnu @@ -0,0 +1,12 @@ +# CONFIG_SITE.Common.solaris-x86-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for solaris-x86-gnu target +# Only the local epics system manager should modify this file + +# solaris 10 default location +#GNU_DIR=/usr/sfw + +# APS site override +GNU_DIR = /usr/local diff --git a/configure/os/CONFIG_SITE.Common.solaris-x86_64 b/configure/os/CONFIG_SITE.Common.solaris-x86_64 new file mode 100644 index 000000000..6afd99ab3 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.solaris-x86_64 @@ -0,0 +1,11 @@ +# CONFIG_SITE.Common.solaris-x86_64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for solaris-x86_64 target +# Only the local epics system manager should modify this file + +# Include definitions common to all solaris-x86_64 target archs +-include $(CONFIG)/os/CONFIG_SITE.Common.solaris-x86 + +COMMANDLINE_LIBRARY = EPICS diff --git a/configure/os/CONFIG_SITE.Common.solaris-x86_64-gnu b/configure/os/CONFIG_SITE.Common.solaris-x86_64-gnu new file mode 100644 index 000000000..2118c70c9 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.solaris-x86_64-gnu @@ -0,0 +1,11 @@ +# CONFIG_SITE.Common.solaris-x86_64-gnu +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for solaris-x86_64-gnu target +# Only the local epics system manager should modify this file + +# Include definitions common to all solaris-sparc-gnu target archs +include $(CONFIG)/os/CONFIG_SITE.Common.solaris-x86-gnu + +COMMANDLINE_LIBRARY = EPICS diff --git a/configure/os/CONFIG_SITE.Common.vxWorks-mpc8540 b/configure/os/CONFIG_SITE.Common.vxWorks-mpc8540 new file mode 100644 index 000000000..33c753ccf --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.vxWorks-mpc8540 @@ -0,0 +1,6 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for the vxWorks-mpc8540 target +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- diff --git a/configure/os/CONFIG_SITE.Common.vxWorks-ppc603 b/configure/os/CONFIG_SITE.Common.vxWorks-ppc603 new file mode 100644 index 000000000..6b3d6919b --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.vxWorks-ppc603 @@ -0,0 +1,7 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for the vxWorks-ppc603 target +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + diff --git a/configure/os/CONFIG_SITE.Common.vxWorks-ppc603_long b/configure/os/CONFIG_SITE.Common.vxWorks-ppc603_long new file mode 100644 index 000000000..9f5728cc1 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.vxWorks-ppc603_long @@ -0,0 +1,10 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for the vxWorks-ppc603_long target +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + +# Inherit the settings from vxWorks-ppc603 +-include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-ppc603 + diff --git a/configure/os/CONFIG_SITE.Common.vxWorks-ppc604 b/configure/os/CONFIG_SITE.Common.vxWorks-ppc604 new file mode 100644 index 000000000..c03a9908c --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.vxWorks-ppc604 @@ -0,0 +1,7 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for the vxWorks-ppc604 target +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + diff --git a/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_altivec b/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_altivec new file mode 100644 index 000000000..1795c800d --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_altivec @@ -0,0 +1,10 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for the vxWorks-ppc604_altivec target +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + +# Inherit the settings from vxWorks-ppc604_long +-include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-ppc604_long + diff --git a/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_long b/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_long new file mode 100644 index 000000000..e4fb578b2 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.vxWorks-ppc604_long @@ -0,0 +1,10 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for the vxWorks-ppc604_long target +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + +# Inherit the settings from vxWorks-ppc604 +-include $(CONFIG)/os/CONFIG_SITE.Common.vxWorks-ppc604 + diff --git a/configure/os/CONFIG_SITE.Common.vxWorksCommon b/configure/os/CONFIG_SITE.Common.vxWorksCommon new file mode 100644 index 000000000..3fdc22ef9 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.vxWorksCommon @@ -0,0 +1,47 @@ +# CONFIG_SITE.Common.vxWorksCommon +# +# Site specific definitions for vxWorks target builds. +# Only the local epics system manager should modify this file + +# Compiler options can vary with the vxWorks version number, so we +# need to know that. However don't include any third-level digits +# (the .2 in 5.4.2) because we don't currently need them. + +# Note: vxWorks 5.3 (Tornado 1.x) is not supported + +#VXWORKS_VERSION = 5.4 +VXWORKS_VERSION = 5.5 +#VXWORKS_VERSION = 6.0 +#VXWORKS_VERSION = 6.1 +#VXWORKS_VERSION = 6.2 +#VXWORKS_VERSION = 6.3 +#VXWORKS_VERSION = 6.4 +#VXWORKS_VERSION = 6.5 +#VXWORKS_VERSION = 6.6 +#VXWORKS_VERSION = 6.7 +#VXWORKS_VERSION = 6.8 + + +# Sites may override the following path for a particular host +# architecture by adding it to an appropriate +# CONFIG_SITE.$(EPICS_HOST_ARCH).vxWorksCommon file. + +# WIND_BASE is where you installed the Wind River software. +# Under vxWorks 6.x this is *not* the same as the old VX_DIR setting + +#WIND_BASE = /usr/local/vw/tornado202p1 +WIND_BASE = /usr/local/vw/tornado22-$(ARCH_CLASS) +#WIND_BASE = /usr/local/vw/vxWorks-$(VXWORKS_VERSION) +#WIND_BASE = /ade/vxWorks/$(VXWORKS_VERSION) + + +# WorkBench Version number, required for vxWorks 6.x + +#WORKBENCH_VERSION = 2.6 +#WORKBENCH_VERSION = 3.0 +#WORKBENCH_VERSION = 3.2 + + +# Utilities Version number, required from vxWorks 6.8 and later + +#UTILITIES_VERSION = 1.0 diff --git a/configure/os/CONFIG_SITE.Common.win32-x86-cygwin b/configure/os/CONFIG_SITE.Common.win32-x86-cygwin new file mode 100644 index 000000000..01af2ec4f --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.win32-x86-cygwin @@ -0,0 +1,7 @@ +# CONFIG_SITE.Common.win32-x86-cygwin +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific definitions for cygwin-x86 target +# Only the local epics system manager should modify this file + diff --git a/configure/os/CONFIG_SITE.Common.win32-x86-mingw b/configure/os/CONFIG_SITE.Common.win32-x86-mingw new file mode 100644 index 000000000..3ca50fe83 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.win32-x86-mingw @@ -0,0 +1,11 @@ +# CONFIG_SITE.win32-x86-mingw.Common +# +# Revision-Id: jba@aps.anl.gov-20101110225906-0j1lkchsu8scplsh +# +# Site Specific definitions for cygwin-x86 target +# Only the local epics system manager should modify this file + +# If readline is not installed comment the following line +# to omit command-line editing and history support +#COMMANDLINE_LIBRARY = READLINE + diff --git a/configure/os/CONFIG_SITE.cygwin-x86.Common b/configure/os/CONFIG_SITE.cygwin-x86.Common new file mode 100644 index 000000000..fc2df9f67 --- /dev/null +++ b/configure/os/CONFIG_SITE.cygwin-x86.Common @@ -0,0 +1,10 @@ +# CONFIG_SITE.cygwin-x86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site override definitions for cygwin-x86 host builds +#------------------------------------------------------- + +CROSS_COMPILER_TARGET_ARCHS = + diff --git a/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 b/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 new file mode 100644 index 000000000..c58713108 --- /dev/null +++ b/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 @@ -0,0 +1,11 @@ +# CONFIG_SITE.cygwin-x86.cygwin-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site override definitions for cygwin-x86 host - cygwin-x86 target builds +#------------------------------------------------------- + +# GNU_DIR used when COMMANDLINE_LIBRARY is READLINE +#GNU_DIR=C:/cygwin + diff --git a/configure/os/CONFIG_SITE.darwin-ppc.Common b/configure/os/CONFIG_SITE.darwin-ppc.Common new file mode 100644 index 000000000..b4bcbc9d5 --- /dev/null +++ b/configure/os/CONFIG_SITE.darwin-ppc.Common @@ -0,0 +1,7 @@ +# CONFIG_SITE.darwin-ppc.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site override definitions for darwin-ppc host builds +#------------------------------------------------------- diff --git a/configure/os/CONFIG_SITE.darwin-ppcx86.Common b/configure/os/CONFIG_SITE.darwin-ppcx86.Common new file mode 100644 index 000000000..c22a3e426 --- /dev/null +++ b/configure/os/CONFIG_SITE.darwin-ppcx86.Common @@ -0,0 +1,7 @@ +# CONFIG_SITE.darwin-ppcx86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site override definitions for darwin-ppcx86 host builds +#------------------------------------------------------- diff --git a/configure/os/CONFIG_SITE.darwin-x86.Common b/configure/os/CONFIG_SITE.darwin-x86.Common new file mode 100644 index 000000000..020ff2b2a --- /dev/null +++ b/configure/os/CONFIG_SITE.darwin-x86.Common @@ -0,0 +1,11 @@ +# CONFIG_SITE.darwin-x86.Common +# +# This file is maintained by the build community. +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# Site override definitions for darwin-x86 host builds +#------------------------------------------------------- + +# Uncomment the following line to cross-compile the +# iOS device (arm) and simulator (x86) binaries +#CROSS_COMPILER_TARGET_ARCHS = ios-arm ios-x86 diff --git a/configure/os/CONFIG_SITE.linux-x86-borland.Common b/configure/os/CONFIG_SITE.linux-x86-borland.Common new file mode 100644 index 000000000..e15fa40d8 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86-borland.Common @@ -0,0 +1,12 @@ +# CONFIG_SITE.linux-x86-borland.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site override definitions for linux-x86-borland host builds +#------------------------------------------------------- + +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 solaris-sparc +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 + +BORLAND = /share2/kylix3 diff --git a/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug b/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug new file mode 100644 index 000000000..69f6e7daf --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug @@ -0,0 +1,7 @@ + +#Prepares the object code to collect data for profiling with prof. +#PROFILE=YES + +#Compiles for profiling with the gprof profiler. +#GPROF=YES + diff --git a/configure/os/CONFIG_SITE.linux-x86.Common b/configure/os/CONFIG_SITE.linux-x86.Common new file mode 100644 index 000000000..7142f9868 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.Common @@ -0,0 +1,12 @@ +# CONFIG_SITE.linux-x86.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site override definitions for linux-x86 host builds +#------------------------------------------------------- + +# JBA test override values +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 solaris-sparc +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 +#CROSS_COMPILER_TARGET_ARCHS = RTEMS-mvme2100 RTEMS-pc386 # RTEMS-mvme5500 RTEMS-mvme167 diff --git a/configure/os/CONFIG_SITE.linux-x86.RTEMS b/configure/os/CONFIG_SITE.linux-x86.RTEMS new file mode 100644 index 000000000..8b0fd9131 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.RTEMS @@ -0,0 +1,8 @@ +# +# Site-specific information for all RTEMS targets +# +#------------------------------------------------------- + +# Needed by gcc +export LD_LIBRARY_PATH := $(LD_LIBRARY_PATH):$(RTEMS_BASE)/lib + diff --git a/configure/os/CONFIG_SITE.linux-x86.UnixCommon b/configure/os/CONFIG_SITE.linux-x86.UnixCommon new file mode 100644 index 000000000..20a6afa5e --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.UnixCommon @@ -0,0 +1,6 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-arm b/configure/os/CONFIG_SITE.linux-x86.linux-arm new file mode 100644 index 000000000..34d3d8aa1 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.linux-arm @@ -0,0 +1,12 @@ +# CONFIG_SITE.linux-x86.linux-arm +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific definitions for linux-x86 host - linux-arm target builds +#------------------------------------------------------- + +# Diamond: +#GNU_DIR = /home/targetOS/linux-arm/host/x86-linux/gcc_3.3.3 + +# anj@aps: +#GNU_DIR = /local/anj/cross-arm/gcc-3.4.5-glibc-2.3.6/arm-linux diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-arm_eb b/configure/os/CONFIG_SITE.linux-x86.linux-arm_eb new file mode 100644 index 000000000..21111f4e6 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.linux-arm_eb @@ -0,0 +1,15 @@ +# CONFIG_SITE.linux-x86.linux-arm_eb +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific definitions for linux-x86 host - linux-arm_eb target builds +#------------------------------------------------------- + +# Include definitions for linux-arm targets +include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-arm + +# Path to the GNU toolset for linux-arm_eb (big endian) target +#GNU_DIR = /local/anj/cross-arm/gcc-3.4.5-glibc-2.3.6/armeb-linux + +# GNU crosscompiler target name +#GNU_TARGET = armeb-linux diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-arm_el b/configure/os/CONFIG_SITE.linux-x86.linux-arm_el new file mode 100644 index 000000000..1938bbec6 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.linux-arm_el @@ -0,0 +1,15 @@ +# CONFIG_SITE.linux-x86.linux-arm_el +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific definitions for linux-x86 host - linux-arm_el target builds +#------------------------------------------------------- + +# Include definitions for linux-arm targets +include $(CONFIG)/os/CONFIG_SITE.linux-x86.linux-arm + +# Path to the GNU toolset for linux-arm_el (little endian) target +#GNU_DIR = /local/anj/cross-arm/gcc-3.4.5-glibc-2.3.6/armel-linux + +# GNU crosscompiler target name +#GNU_TARGET = armel-linux diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-cris b/configure/os/CONFIG_SITE.linux-x86.linux-cris new file mode 100644 index 000000000..eb756ca8a --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.linux-cris @@ -0,0 +1,14 @@ +# CONFIG_SITE.linux-x86.linux-cris +# +# Author: Peter Zumbruch +# GSI +# P.Zumbruch@gsi.de +# +# Site specific definitions for linux-x86 host - linux-cris target builds +#------------------------------------------------------- + +# define site specific location of cris cross compiler's gnu directory +# but without bin sub directory, this will be added automatically. + +CRIS_CROSS_COMPILER ?= UNDEFINED_ENV__CRIS_CROSS_COMPILER + diff --git a/configure/os/CONFIG_SITE.linux-x86.linux-x86 b/configure/os/CONFIG_SITE.linux-x86.linux-x86 new file mode 100644 index 000000000..82212b46b --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.linux-x86 @@ -0,0 +1,7 @@ +# CONFIG_SITE.linux-x86.linux-x86 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific definitions for linux-x86 host - linux-x86 target builds +#------------------------------------------------------- + diff --git a/configure/os/CONFIG_SITE.linux-x86.solaris-sparc b/configure/os/CONFIG_SITE.linux-x86.solaris-sparc new file mode 100644 index 000000000..b61dca07b --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.solaris-sparc @@ -0,0 +1,10 @@ +# CONFIG_SITE.linux-x86.solaris-sparc +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site specific definitions for linux-x86 host - solaris-sparc target builds +#------------------------------------------------------- + +#GNU_DIR = /home/phoebus/JBA/gnu-solaris2 + diff --git a/configure/os/CONFIG_SITE.linux-x86.vxWorks-68040 b/configure/os/CONFIG_SITE.linux-x86.vxWorks-68040 new file mode 100644 index 000000000..345f7b30d --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.vxWorks-68040 @@ -0,0 +1,8 @@ +# CONFIG_SITE.linux-x86.vxWorks-68040 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific definitions for linux-x86 host - vxWorks-68040 target builds +# Only the local epics system manager should modify this file +#------------------------------------------------------- + diff --git a/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603 b/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603 new file mode 100644 index 000000000..23f8e21e8 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603 @@ -0,0 +1,7 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site-specific definitions for linux-x86 builds of vxWorks-ppc603 +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + diff --git a/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603_long b/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603_long new file mode 100644 index 000000000..28ceba47d --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.vxWorks-ppc603_long @@ -0,0 +1,9 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site-specific definitions for linux-x86 builds of vxWorks-ppc603_long +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + +# Inherit settings from vxWorks-ppc603 +-include $(CONFIG)/os/CONFIG_SITE.linux-x86.vxWorks-ppc603 diff --git a/configure/os/CONFIG_SITE.linux-x86.vxWorksCommon b/configure/os/CONFIG_SITE.linux-x86.vxWorksCommon new file mode 100644 index 000000000..0aac779de --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86.vxWorksCommon @@ -0,0 +1,9 @@ +# CONFIG_SITE.linux-x86.vxWorksCommon +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Definitions for linux-x86 host - vxWorks target builds +#------------------------------------------------------- + + diff --git a/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug b/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug new file mode 100644 index 000000000..71975fbd9 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug @@ -0,0 +1,15 @@ +# CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific override definitions for solaris-sparc host builds +# Only the local epics system manager should modify this file + + + +#Prepares the object code to collect data for profiling with prof. +#PROFILE=YES + +#Compiles for profiling with the gprof profiler. +#GPROF=YES + diff --git a/configure/os/CONFIG_SITE.linux-x86_64.Common b/configure/os/CONFIG_SITE.linux-x86_64.Common new file mode 100644 index 000000000..022288f65 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86_64.Common @@ -0,0 +1,12 @@ +# CONFIG_SITE.linux-x86_64.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# This file is maintained by the build community. +# +# Site override definitions for linux-x86_64 host builds +#------------------------------------------------------- + +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 solaris-sparc +#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 +#CROSS_COMPILER_TARGET_ARCHS = RTEMS-mvme2100 + diff --git a/configure/os/CONFIG_SITE.linux-x86_64.UnixCommon b/configure/os/CONFIG_SITE.linux-x86_64.UnixCommon new file mode 100644 index 000000000..54111d659 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86_64.UnixCommon @@ -0,0 +1,7 @@ +# CONFIG_SITE.linux-x86_64.UnixCommon +$ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific configure override definitions +# Only the local epics system manager should modify this file + diff --git a/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 new file mode 100644 index 000000000..57b9228ff --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 @@ -0,0 +1,8 @@ +# CONFIG_SITE.linux-x86_64.linux-x86_64 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific definitions for linux-x86_64 host - linux-x86_64 target builds +#------------------------------------------------------- + + diff --git a/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-68040 b/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-68040 new file mode 100644 index 000000000..eabb88ac6 --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-68040 @@ -0,0 +1,8 @@ +# CONFIG_SITE.linux-x86_64.vxWorks-68040 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific definitions for linux-x86_64 host - vxWorks-68040 target builds +# Only the local epics system manager should modify this file +#------------------------------------------------------- + diff --git a/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603 b/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603 new file mode 100644 index 000000000..58b7de2cb --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603 @@ -0,0 +1,6 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site-specific definitions for linux-x86_64 builds of vxWorks-ppc603 +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- diff --git a/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603_long b/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603_long new file mode 100644 index 000000000..3b37a24ca --- /dev/null +++ b/configure/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603_long @@ -0,0 +1,9 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site-specific definitions for linux-x86 builds of vxWorks-ppc603_long +# +# Only the local epics system manager should modify this file +#------------------------------------------------------- + +# Inherit settings from vxWorks-ppc603 +-include $(CONFIG)/os/CONFIG_SITE.linux-x86_64.vxWorks-ppc603 diff --git a/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug b/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug new file mode 100644 index 000000000..c0b1ec77e --- /dev/null +++ b/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug @@ -0,0 +1,8 @@ +include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc + +#Prepares the object code to collect data for profiling with prof. +#PROFILE=YES + +#Compiles for profiling with the gprof profiler. +#GPROF=YES + diff --git a/configure/os/CONFIG_SITE.solaris-sparc.Common b/configure/os/CONFIG_SITE.solaris-sparc.Common new file mode 100644 index 000000000..4ee049147 --- /dev/null +++ b/configure/os/CONFIG_SITE.solaris-sparc.Common @@ -0,0 +1,10 @@ +# CONFIG_SITE.solaris-sparc.Common +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site specific override definitions for solaris-sparc host builds +# Only the local epics system manager should modify this file + +#INSTALL_LOCATION = /home/phoebus/JBA/testBaseNew + +#CROSS_COMPILER_TARGET_ARCHS += vxWorks-ppc604 vxWorks-ppc603 vxWorks-68040 diff --git a/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc-debug b/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc-debug new file mode 100644 index 000000000..c0b1ec77e --- /dev/null +++ b/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc-debug @@ -0,0 +1,8 @@ +include $(CONFIG)/os/CONFIG_SITE.Common.solaris-sparc + +#Prepares the object code to collect data for profiling with prof. +#PROFILE=YES + +#Compiles for profiling with the gprof profiler. +#GPROF=YES + diff --git a/configure/os/CONFIG_SITE.win32-x86-borland.Common b/configure/os/CONFIG_SITE.win32-x86-borland.Common new file mode 100644 index 000000000..bce60f0ba --- /dev/null +++ b/configure/os/CONFIG_SITE.win32-x86-borland.Common @@ -0,0 +1,11 @@ +# CONFIG_SITE.win32-x86-borland.Common +# +# Revision-Id: jba@aps.anl.gov-20101115012953-0084bw5aaupzfiip +# +# Site specific definitions for win32-x86-borland host +# Only the local epics system manager should modify this file + +# jba overrides +#CROSS_COMPILER_TARGET_ARCHS=vxWorks-486 + +BORLAND = c:\\Borland\\Bcc55 diff --git a/configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin b/configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin @@ -0,0 +1,2 @@ + + diff --git a/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw b/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw new file mode 100644 index 000000000..7793d0327 --- /dev/null +++ b/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw @@ -0,0 +1,17 @@ +# CONFIG_SITE.win32-x86-mingw.win32-x86-mingw +# +# Revision-Id: jba@aps.anl.gov-20101026204231-vnn112mdcfaigwne +# +# Site Specific definitions for win32-x86-mingw target +# Only the local epics system manager should modify this file + + +# Uncomment the following settings for MinGW_w32 releases +# /bin should be in your path +#CMPLR_PREFIX=i686-w64-mingw32- +#CC = $(CMPLR_PREFIX)gcc +#CCC = $(CMPLR_PREFIX)g++ +#AR = $(CMPLR_PREFIX)ar -rc +#LD = $(CMPLR_PREFIX)ld -r +#RANLIB = $(CMPLR_PREFIX)ranlib + diff --git a/configure/os/CONFIG_SITE.win32-x86.Common b/configure/os/CONFIG_SITE.win32-x86.Common new file mode 100644 index 000000000..47dad3e91 --- /dev/null +++ b/configure/os/CONFIG_SITE.win32-x86.Common @@ -0,0 +1,11 @@ +# CONFIG_SITE.win32-x86.Common +# +# Revision-Id: jba@aps.anl.gov-20101115012953-0084bw5aaupzfiip +# +# Site specific definitions for win32-x86 host +# Only the local epics system manager should modify this file + +# jba test overrides +#CROSS_COMPILER_TARGET_ARCHS=vxWorks-486 +#CROSS_COMPILER_TARGET_ARCHS+=vxWorks-68040 +#INSTALL_LOCATION = G:/testInstall diff --git a/configure/os/CONFIG_SITE.win32-x86.win32-x86 b/configure/os/CONFIG_SITE.win32-x86.win32-x86 new file mode 100644 index 000000000..6279aea40 --- /dev/null +++ b/configure/os/CONFIG_SITE.win32-x86.win32-x86 @@ -0,0 +1,17 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +# If you have Visual C++ 6.0, uncomment the following override lines +# This will to eliminate warnings about unknown options /GL, /LTCG, and /w44355. +#OPT_CFLAGS_YES = /Ox +#OPT_CXXFLAGS_YES = /Ox +#OPT_CFLAGS_NO = /Zi +#OPT_CXXFLAGS_NO = /Zi +#LINK_OPT_FLAGS_YES = /incremental:no /opt:ref /release $(PROD_VERSION:%=/version:%) +#WARN_CFLAGS_YES = /W3 +#WARN_CXXFLAGS_YES = /W3 + + +# If you have Visual C++ 8.0 or 8.0 express, uncomment the following override. +# This will eliminate warnings about /GX being deprecated. +#CCC = cl /nologo /EHsc /GR + diff --git a/documentation/BuildingR3.13AppsWithR3.14.html b/documentation/BuildingR3.13AppsWithR3.14.html new file mode 100644 index 000000000..2a5b04492 --- /dev/null +++ b/documentation/BuildingR3.13AppsWithR3.14.html @@ -0,0 +1,239 @@ + + + + + + + + + + +
+

Building EPICS R3.13 applications with R3.14 base

+
+ +


+This document describes how to modify a R3.13 vxWorks application so that it +builds with release R3.14. It describes an easy way to modify +applications subject to the following restrictions:

+
    +
  • The application still uses config rules rather than the configure rules + which are new to release 3.14.
  • +
  • The OSI (Operating System Independent) features of R3.14 are not + available, i.e. iocCore products can only be build for vxWorks.
  • +
  • Once the application is changed to build with base R3.14, it will not + build with earlier base releases.
  • +
+Ultimately applications should be converted to use the new configure rules so +that the OSI features are available. + +

Prerequisite for building R3.13 applications

+

The macro COMPAT_313 must be set to YES in base/configure/CONFIG_SITE +before the base build. This will install vxWorks object files and perl +scripts needed for building R3.13 ioc applications. +

+ +

Gnumake clean uninstall

+ +

At the top of the application execute:

+
"gnumake clean uninstall"
+to remove all files and directories created by earlier builds. + +

Update RELEASE

+ +

Change the EPICS_BASE definition to point to the R3.14 base +release. All hardware support and some soft support is now unbundled. For +example the sequencer is unbundled. You must obtain and build all required +unbundled products before you can build the application. The location of each +unbundled product must be specified in <top>/config/RELEASE. For +example if you are using the sequencer add the line:

+
SNCSEQ=<full path to sequencer build for 3.14>
+ +

Update target arch definitions

+ +

Change any target arch specification in config/CONFIG to new R3.14 target +arch specification. Look in the R3.14 base/bin directory to see the target +arch names.

+ +

For example, in config/CONFIG change

+
CROSS_COMPILER_TARGET_ARCHS = mv167
+to +
CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040
+In the iocBoot/*/Makefile files, change +
ARCH=<old arch name>
+to +
ARCH=<new arch name>
+and do a "gnumake" in the iocBoot directory to update the cdCommands +files. + +

State Notation Language changes

+ +

NOTE: You must have a version of the sequencer that is build for R3.14. +This version uses configure rules rather than config rules. It still, +however, has a config directory. Makesure that config/RELEASE has the same +location for base as configure/RELEASE.

+ +

snc is no longer in base. If snc is used in your application, you must +download and build the seq module with baseR3.14. See the Application +Developer's Guide to see how to build sequence programs.

+ +

If you build sequencer programs add the lines

+
SNC = ${SNCSEQ}/bin/$(HOST_ARCH)/snc$(EXE)
+LIBOBJS += $(SNCSEQ_BIN)/seqLibrary.o
+LIBOBJS += $(SNCSEQ_BIN)/pvLibrary.o
+ +

remove

+
INSTALLS = seq
+ +


+in application src/Makefile.Vx files to link seq library into your +application library,.
+

+Also remove +
ld < seq
+from the st.cmd files in the iocBoot subdirectories. + +

iocCore changes

+
    +
  • src/Makefile.Vx +

    iocCore has been renamed to iocCoreLibrary.o. Change

    +
    INSTALLS=iocCore
    +    
    +

    to

    +
    LIBOBJS += $(EPICS_BASE_BIN)/iocCoreLibrary.o
    +    
    +
  • +
  • iocBoot/iocxxx/st.cmd +

    Remove

    +
    ld < iocCore
    +    
    +

    Immediately after the dbLoadDatabase(xxx.dbd) command add

    +
    xxx_registerRecordDeviceDriver(pdbbase)
    +    
    +
  • +
+ +

Library db changes

+ +

The Db library name was changed to dbStaticHost for host builds and +dbStaticIoc for ioc builds.
+Change

+
PROD_LIBS+=Db
+to +
PROD_LIBS+=dbStaticHost
+and +
Db_DIR=$(EPICS_BASE_LIB)
+to +
dbStaticHost_DIR=$(EPICS_BASE_LIB)
+in your application src/Makefile.Host files. + +

VxWorks C++ munched libraries and object files

+ +

Since R3.14 contains C++ code, the build rules were changed to create a +corresponding munched file for every vx object file built. The objects files +can be combined to form libraries but the munched files must be loaded into +vxWorks. In all st.cmd files add the suffix ".munch" to the ld lines +where object files were previously loaded. For example change

+
ld < exampleLib
+to +
ld < exampleLib.munch
+ +

recGbl calls

+ +

You may need to add the line "#include "recGbl.h" to any source +files that have recGbl* calls so that the file will compile without +errors.

+ +

Record support changes

+ +

The steppermotor, scan, and pid records are no longer in base. If your +application does not use these record types, comment out or remove references +to them in base.dbd and baseLIBOBJS. If these record types are used by your +application you must download and build the modules with R3.14 base, add +appropriate module definitions to your application's config/RELEASE fileand +change the LIBOBJS definitions. For example
+add

+
PID=<full path to modules directory>/pid
+to config/RELEASE.
+Remove +
LIBOBJS += $(EPICS_BASE_BIN)/pidRecord.o
+from baseLIBOBJS,
+and add +
LIBOBJS += $(PID_BIN)/pidRecord.o
+to your application src/Makefile. + +

You should consider changing any existing old steppermotor records to the +new EPICS motor +record module supported by Beamline Controls and Data Acquisition at +APS.

+ +

RecDynLink.o and devPtSoft changes

+ +

recDynLink.o and devPtSoft.o are no longer in base. Remove references to +them in base.dbd and baseLIBOBJS if they are not used in application.

+ +

Hardware support changes

+ +

All hardware support (dev, drv and dbd files) has been unbundled from base +R3.14. This support includes the files symb, symb.dbd, drvHp1404a.o, +drvEpvxiMsg.o, and drvEpvxi.o. If they are not used in your +application, comment out references to these files in base.dbd and +baseLIBOBJS.

+ +

Hardware support files now exist as separate modules available for +download from the EPICS www home page +at ANL. You must now download all the hardware support modules needed for +your application, build the modules with R3.14 base, add the appropriate +module full path definitions to your application config/RELEASE file, and +change LIBOBJS location definition $(EPICS_BASE_BIN) to the +module definition bin directory in your application src directory files.

+ +

For example, remove

+
LIBOBJS+=$(EPICS_BASE_BIN)/symb
+from baseLIBOBJS and add +
LIBOBJS+=$(SYMB_BIN)/symb
+to your application src/Makefile,
+and add the line +
SYMB=<full path definition for the built module SYMB>
+into your application config/RELEASE file . + +

After a gnumake in the symb module followed by a gnumake in the +application config directory, the definitions SYMB_BIN and +SYMB_LIB will be created, the directory $(SYMB)/include will be +added to the include directories on the compiler command lines (in the +INSTALL_INCLUDES definition) and the directory $(SYMB)/dbd will be +added to the dbd search directories on the dbExpand command lines (in the +INSTALL_DBDFLAGS definition).

+ +

dbLoadtemplate tool changes

+ +

The host tool dbLoadTemplate has been replaced by a new extension, msi. +dbLoadTemplate is still supported on iocs. Build the new msi extension with a +R3.13 or R3.14 base and verify that the msi executable is in your path.

+ +

Change the following definitions in your application files.

+ +

In config/RULES.Db replace the line

+
DBLOADTEMPLATE=$(EPICS_BASE_HOST_BIN)/dbLoadTemplate$(EXE)
+with +
MSI = msi
+and change the DBLOADTEMPLATE rule in RULES.Db from +
%.t.db.raw: %.substitutions 
+        @echo "Inflating database from $<" 
+        (RM) $@ 
+        @$(DBLOADTEMPLATE) $< > $@
+ +

to

+
%.t.db.raw: %.substitutions %.template 
+        @echo "Inflating database from $<" 
+        @$(RM) $@ 
+        @$(MSI) -S $^ > $@
+ +

depends changes

+ +

Remove any SRCS.c and SRCS.cc definitions in your application +src/Makefile..*files. The depends rule no longer uses these definitions.

+ + diff --git a/documentation/BuildingR3.13ExtensionsWithR3.14.html b/documentation/BuildingR3.13ExtensionsWithR3.14.html new file mode 100644 index 000000000..02271a4ce --- /dev/null +++ b/documentation/BuildingR3.13ExtensionsWithR3.14.html @@ -0,0 +1,227 @@ + + + + + + + + + + +
+

Building EPICS R3.13 extensions with R3.14 base

+
+ +


+EPICS R3.13 extensions have both a Makefile and a Makefile.Host in the build +directories and the EPICS R3.13 extension tree has an extensions/config +directory.

+ +

Prerequisite for building R3.13 extensions

+ +
+
    +
  • The macro COMPAT_TOOLS_313 must be set to YES in base/configure/CONFIG_SITE + before the base build. If it was not set, set it to YES and then do a gmake + in the base/config directory. This will install some perl scripts needed + for building R3.13 extensions. +
  • +
+
+ +

Preliminary steps for all extensions

+ +
+
    +
  • Download the latest version of the extensions/config files, + extensionsConfig.tar.gz, from the APS EPICS www page.
  • + The extensions directory can have both the base/config (for extensions with + R3.13 makefiles) and base/configure (for extensions with R3.14 makefiles) + directories. + +
  • Make certain that you have set the HOST_ARCH environment + variable.
  • +
  • Set EPICS_BASE in extensions/config/RELEASE to the full path location + of a built R3.14 base.
  • +
  • BASE_3_14 is now defined in the base/config/CONFIG_BASE_VERSION. The + commented BASE_3_14 definition can be removed from + extensions/config/RELEASE.
  • +
+
+ +

Building downloaded APS distribution extensions with base R3.14

+ +
+
    +
  • Download the latest version of extensions distributed from the APS + EPICS www page. The latest versions of the extensions should build with + both R3.13 and R3.14. Note that the order of building extensions is + important, i.e. some extensions depend on other extensions being built + first. The extensions/config/CONFIG_EXTENSIONS file contains notes on + extension dependencies and contains a definition of DIRS (used by + extensions/src/Makefile) with the proper order for building the APS + distributed extensions.
  • +
  • Executing gnumake at the root level of the extensions tree or in the + src directory should build the APS extensions in the proper order.
  • +
+
+ +

Building your extensions with base R3.14

+ +
+
  • Library Db renamed
  • + +

    Library Db has been renamed to dbStaticHost in EPICS base R3.14. In some + extensions lib Db is not used and Db can be removed from the USR_LIBS (or + PROD_LIBS) line. If the library is needed ( you get unresolved items after + removing Db), the following lines should be added to Makefile.Host.

    + +
    +
    ifdef BASE_3_14
    +USR_LIBS += dbStaticHost
    +dbStaticHost_DIR = $(EPICS_BASE_LIB)
    +else
    +USR_LIBS += Db
    +Db_DIR = $(EPICS_BASE_LIB)
    +endif
    + +
  • Library dbStaticHost names changed
  • + +

    Library functions have been renamed. If there are calls with old db + names, add the following lines

    + +
    +
    #if  EPICS_REVISION && EPICS_REVISION == 13
    +#define dbFindRecordType    dbFindRecdes
    +#define dbGetNRecordTypes   dbGetNRecdes
    +#define dbNextRecordType    dbNextRecdes
    +#define dbFirstField        dbFirstFielddes
    +#define dbGetRecordTypeName dbGetRecdesName
    +#define dbFirstRecordType   dbFirstRecdes
    +#define dbNextField         dbNextFielddes
    +#define dbGetMenuChoices    dbGetChoices
    +
    +long dbReadDatabaseFP(DBBASE **ppdbbase,
    +    FILE *fp, const char *path,const char *substitutions)
    +{
    +     return(dbRead(*ppdbbase,fp));
    +}
    +#endif
    +
    + and change the the old names and function calls to the R3.14 versions: + +
    +
    dbFindRecdes         =>  dbFindRecordType
    +dbGetNRecdes         =>  dbGetNRecordTypes
    +dbNextRecdes         =>  dbNextRecordType
    +dbFirstFielddes      =>  dbFirstField
    +dbGetRecdesName      =>  dbGetRecordTypeName
    +dbFirstRecdes        =>  dbFirstRecordType
    +dbNextFielddes       =>  dbNextField
    +dbGetChoices         =>  dbGetMenuChoices
    +dbRead(pdbbase,fp)   =>  dbReadDatabaseFP(&pdbbase,fp,0,0)
    +
    + +
  • Target architecture specifications changed
  • + +

    Since target architecture specifications have been changed (solaris to + solaris-sparc, win32 to win32-x86, ...) Makefile.Host references to and + tests on T_A have to be changed. In most cases T_A can be replaced by + OS_CLASS. Since HOST_ARCH will eventually be phased out, it would be a + good idea to change any Makefile.Host references to HOST_ARCH to OS_CLASS + if possible.
    + For example change

    + +
    +
    ifeq ($(T_A),solaris)
    +RPCFLAGS = -K -1
    +endif
    +
    + to + +
    +
    ifeq ($(OS_CLASS),solaris)
    +RPCFLAGS = -K -1
    +endif
    +
    + + +
  • New ca_set_puser
  • + +

    Statements of the form: ca_puser(chid) = xyz; should to be + changed to: ca_set_puser(chid,xyz);
    +

    + + +
  • Extern C around includes
  • + +

    Remove any extern "C" braces around #includes of EPICS base header + files.
    + For example change

    + +
    +
    extern "C" {
    +#include "cadefs.h"
    +} /* end extern C */
    +
    + to + +
    + #include "cadefs.h"
    + +
  • Infrequently used R3.13 timestamp functions unbundled from base
  • + +

    Some infrequently used R3.13 timestamp functions and macro definitions + have been removed from EPICS base and now exist in a library, ts, created + and installed in the ar extension. The only ANL distributed extension that + uses these unbundled functions and macros is cau. The two R3.13 functions + tsStampToText and tsLocalTime along with the definitions TS_TEXT_MONDDYYYY + and TS_TEXT_MMDDYY have been retained in R3.14 for extension compatibility + purposes.
    + If your extension gets undefines for TS_* usage and ts* function calls when + built with R3.14, you must obtain and build the ar extension. Add an + #include for tsSubr.h to your extension source code and add library ts to + PROD_LIBS or USR_LIBS in your Makefile.Host as follows:

    + +
    +
    #include "tsSubr.h"
    +
    +ifdef BASE_3_14
    +PROD_LIBS += ts
    +ts_DIR = $(EPICS_EXTENSIONS_LIB)
    +endif
    +
    + +
  • No Field name length restriction
  • + +

    The length restriction to database record names no longer exists in R3.14 + so the the FLDNAME_SZ macro definition was removed.from dbDefs.h. + Extensions which still have a field name restriction should add the field + name size definition to their code until they are updated.

    + +
    +
    #ifndef FLDNAME_SZ
    +#define FLDNAME_SZ 4 /*Field Name Size*/
    +#endif
    +
    + +
  • Some EPICS base version macros removed
  • + +

    + The EPICS base version macros that start with "BASE_" have been removed + from epicsVersion.h. Extensions should now use only the existing macros + which start with "EPICS_".

    +
    + +
    +
    + BASE_VERSION => EPICS_VERSION + BASE_REVISION => EPICS_REVISION + BASE_MODIFICATION => EPICS_MODIFICATION + BASE_UPDATE_NAME => EPICS_UPDATE_NAME + BASE_UPDATE_LEVEL => EPICS_UPDATE_LEVEL + BASE_VERSION_STRING => EPICS_VERSION_STRING +
    +
    + + diff --git a/documentation/ConvertingR3.13AppsToR3.14.html b/documentation/ConvertingR3.13AppsToR3.14.html new file mode 100644 index 000000000..ec95db2f0 --- /dev/null +++ b/documentation/ConvertingR3.13AppsToR3.14.html @@ -0,0 +1,476 @@ + + + + + + + + + + +
    +

    Converting an EPICS R3.13 application to R3.14

    +
    + +

    This document describes how to convert a R3.13 vxWorks application so that it +builds with release R3.14. It describes procedures such that:

    +
      +
    • The application uses the configure rules which are new to R3.14.
    • +
    • The OSI (Operating System Independent) features of R3.14 are available, + i.e. iocCore products can be build for vxWorks as well as other + platforms, e.g. solaris and linux.
    • +
    + +

    Gnumake clean uninstall

    + +

    First do a gnumake clean uninstall in the application's root +directory to remove all files created by earlier builds.

    + +

    Create a new R3.14 application

    + +

    We will remove junkApp later.

    + +
    +
    mkdir top
    +cd top
    +/path/to/base-3.14/bin/host_arch/makeBaseApp.pl -t example junk
    +
    + +

    Copy all *App and iocBoot directories and files to the new top +directory

    + +
    +
    cd oldtop 
    +find *App iocBoot -print | cpio -pvmd /path/to/new/top
    +
    + +

    Modify top/configure/RELEASE

    + +

    Copy definitions of external modules excluding EPICS_BASE and +TEMPLATES_TOP from old application RELEASE file.
    +If sequence programs (*.st or *.stt files) exist in your application, add the +SNCSEQ location definition for the R3.14 sncseq external module

    + +
    +
    SNCSEQ = /path/to/sncseq
    +
    + +

    The R3.14 sncseq module must exist and be built with the same EPICS base +R3.14 release.

    + +

    Modify the Makefiles in top/*App directories.

    + +

    Change include $(TOP)/config/CONFIG_APP to include +$(TOP)/configure/CONFIG

    + +

    Change include $(TOP)/config/RULES_DIRS to include +$(TOP)/configure/RULES_DIRS

    + +

    Modify the Makefiles in top/*App/*Db directories.

    + +

    Remove existing Makefile.

    + +

    Rename Makefile.Host to Makefile

    + +

    Modify the Makefile as follows:

    + +

    Change TOP=../../.. to TOP=../..

    + +

    Change include $(TOP)/config/CONFIG_APP to include +$(TOP)/configure/CONFIG

    + +

    Change include $(TOP)/config/RULES.Dbto include +$(TOP)/configure/RULES

    + +

    Place all definitions between the include lines.

    + +

    Place any rules after the last include line.

    + +

    Modify the Makefiles in top/*App/src directories.

    + +

    This is the hardest step. The definitions in Makefile.Host and Makefile.Vx +must be manually converted to the new configure definitions.

    + +

    First replace Makefile with the Makefile from junkApp/src.

    + +
    +
    rm Makefile
    +cp ../../junkApp/src/Makefile .
    +
    + +

    We can remove the junkApp now (unless you have other App/src directories +still to convert):

    + +
    +
    rm -rf ../../junkApp
    +
    + +

    This new Makefile has comments explaining how to build the various host +and ioc products. Lets consider some examples

    +
      +
    • Host programs +

      Makefile.Host contains definitions like:

      +
      +
      PROD += caExample
      +caExample_SRCS += caExample.c
      +PROD_LIBS +=  ca Db Com
      +ca_DIR  = $(EPICS_BASE_LIB)
      +Db_DIR  = $(EPICS_BASE_LIB)
      +Com_DIR = $(EPICS_BASE_LIB)
      +
      +

      In Makefile these are:

      +
      +
      PROD_HOST += caExample
      +caExample_SRCS += caExample.c
      +caExample_LIBS += $(EPICS_BASE_HOST_LIBS)
      +
      +
    • +
    • Record Support - generate xxxRecord.h file +

      Makefile.Host (or perhaps Makefile.Vx) contains:

      +
      +
      RECTYPES += xxxRecord.h
      +
      +

      In Makefile this is:

      +
      +
      DBDINC += xxxRecord
      +
      +
    • +
    • Generating the .dbd file for all record/device/driver support +

      Makefile.Host (or perhaps Makefile.Vx) contains:

      +
      +
      DBDEXPAND = exampleInclude.dbd
      +DBDNAME = exampleApp.dbd
      +
      +

      In Makefile this is:

      +
      +
      DBD += example.dbd
      +
      +

      NOTES: Change exampleApp.dbd to example.dbd in all st.cmd files. Also + this definition assumes that file exampleInclude.dbd exists.

      +
    • +
    • Create the ioc application: +

      Makefile.Vx contains statements like:

      +
      +
      SRCS.c += ../xxxRecord.c
      +SRCS.c += ../devXxxSoft.c
      +
      +LIBOBJS += xxxRecord.o
      +LIBOBJS += devXxxSoft.o
      +LIBOBJS += sncExample.o
      +
      +include ../baseLIBOBJS
      +
      +LIBNAME = exampleLib
      +INSTALLS += iocCore seq
      +
      +

      In Makefile these become:

      +
      +
      LIBRARY_vxWorks += exampleIoc
      +exampleIoc_SRCS += xxxRecord.c
      +exampleIoc_SRCS += devXxxSoft.c
      +exampleIoc_LIBS += $(EPICS_BASE_IOC_LIBS)
      +
      +PROD_IOC_vxWorks = example
      +example_SRCS += sncExample.st
      +example_LIBS += exampleIoc
      +example_LIBS += seq pv
      +example_LIBS += $(EPICS_BASE_IOC_LIBS)
      +
      +# example_registerRecordDeviceDriver.cpp will be created from example.dbd
      +example_SRCS += example_registerRecordDeviceDriver.cpp
      +
      +#The following adds support from base/src/vxWorks
      +example_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
      +
      +
    • +
    + +

    After these changes are made the following files are no longer needed: +baseLIBOBJS, Makefile.Host, and Makefile.Vx

    + +

    File base.dbd no longer needed

    + +

    You now can add the line

    + +
    +
    include "base.dbd"
    +
    + +

    to your appnameInclude.dbd file and remove the file +nameApp/src/base.dbd from your src directory. The base.dbd from base/dbd +will be used instead. If you only want to load a subset of the record +definitions from base you can keep your own copy of base.dbd, but you should +copy the one from your R3.14 base and edit that rather than trying to re-use the +R3.13 version from your old application.

    + +

    Record support

    + +

    Add the following line after all existing #includes

    + +
    +
    #include "epicsExport.h"
    +
    + +

    The structure rset is now a typedef so change

    + +
    +
    struct rset recordnameRSET = { ... };
    +
    + +

    to

    + +
    +
    rset recordnameRSET = { ... };
    +
    + +

    and add the following line after the rset recordnameRSET = { ... +}; definition.

    + +
    +
    epicsExportAddress(rset,recordnameRSET);
    +
    + +

    Device support

    + +

    Add the following line after all existing #includes

    + +
    +
    #include "epicsExport.h"
    +
    + +

    and add the following line after the dset definition struct { ... } +devname = { ... };

    + +
    +
    epicsExportAddress(dset,devname);
    +
    + +

    Driver support

    + +

    Add the following line after all existing #includes

    + +
    +
    #include "epicsExport.h"
    +
    + +

    and add the following line after the drvet drvname definition

    + +
    +
    epicsExportAddress(drvet,drvname);
    +
    + +

    Registration code changed

    + +

    Registration code for application specific functions, e.g. subroutine record +init and process functions, must be changed as follows

    + +
      +
    1. Include the following header files after all existing #includes: +
      +
      #include "registryFunction.h"
      +#include "epicsExport.h"
      +
    2. + +
    3. Make the application specific functions static functions, e.g. +
      +
      static long mySubInit(subRecord *precord)
      +static long mySubProcess(subRecord *precord)
      +
    4. + +
    5. Add an epicsExportFunction statement for each of the functions to be + registered, e.g. +
      +
      epicsExportFunction(mySubInit);
      +epicsExportFunction(mySubProcess);
      +
    6. + +
    7. Add a function statement for each of the functions to be registered in a + .dbd file that is included in the application, e.g. +
      +
      function("mySubInit")
      +function("mySubProcess")
      +
    8. +
    + +

    Modify the Makefiles in top/iocBoot directory.

    + +

    Change include $(TOP)/config/CONFIG_APP to include +$(TOP)/configure/CONFIG

    + +

    If they do not already exist, add the lines

    + +
    +
    DIRS += $(wildcard *ioc*)
    +DIRS += $(wildcard as*)
    +
    + +

    Change include $(TOP)/config/RULES.iocBoot to include +$(TOP)/configure/RULES_DIRS.

    + +

    Modify the Makefiles in top/iocBoot/ioc* directories.

    + +

    Change include $(TOP)/config/CONFIG_APP to include +$(TOP)/configure/CONFIG

    + +

    Change

    + +
    +
    ARCH = <old arch specification e.g. mv167>
    +
    + +

    to

    + +
    +
    ARCH = <new arch specification e.g. vxWorks-68040>
    +
    + +

    Change include $(TOP)/config/RULES.ioc to include +$(TOP)/configure/RULES.ioc

    + +

    If it exists remove the line

    + +
    +
    buildInstall: cdCommands
    +
    + +

    Add the line

    + +
    +
    TARGETS = cdCommands
    +
    + +

    before the include for RULES.ioc line.

    + +

    Modify st.cmd in top/iocBoot/ioc* directories.

    + +

    Remove the lines

    + +
    +
    ld < seq
    +ld < iocCore
    +
    + +

    Change ld < nameLib to + +

    +
    ld 0,0, "name.munch"
    + +

    (The ld command in vxWorks 5.5.2 doesn't clean up its standard + input properly, so we now recommend passing the filename to it directly + instead.)

    + +

    Change cd appbin to cd topbin

    + +

    Change the statement:

    + +
    +
    dbLoadDatabase("../../dbd/nameApp.dbd")
    +
    + +to + +
    +
    dbLoadDatabase("../../dbd/name.dbd")
    +name_registerRecordDeviceDriver(pdbbase)
    +
    + +

    where name is replaced with the name of your dbd file.

    + +

    recGbl calls

    + +

    If any source file makes calls to recGbl routines make sure it includes +recGbl.h. If it doesn't the compiler will issue warning messages and +the ioc may issue the message: undefined symbol: _recGblSetSevr.

    + +

    Record support changes

    + +

    The steppermotor, scan, and pid records are no longer in base. If these +records are not used in your application, comment out references to them in +base.dbd. If these record types are used at your site, they should be +downloaded and built with base R3.14 by your EPICS administrator. To update +the R3.14 location of these record types in your application you must add +appropriate module definitions to your application's config/RELEASE file and +add LIBOBJS definitions to the src Makefile.

    + +

    For example add

    + +
    +
    PID = /path/to/modules/pid
    +
    + +

    to config/RELEASE and add

    + +
    +
    LIBOBJS += $(PID_BIN)/pidRecord.o
    +
    + +

    to your application src/Makefile.

    + +

    You should consider changing any existing old steppermotor records to the +new EPICS motor record module supported by Beamline Controls and Data +Acquisition at APS.

    + +

    RecDynLink.o and devPtSoft changes

    + +

    recDynLink.o and devPtSoft.o are no longer in EPICS base and now exist as +separate EPICS modules.You must now add the appropriate module full path +definitions to your application config/RELEASE file, and change +LIBOBJS location definition $(EPICS_BASE_BIN) to the module +definition bin directory in your application src directory files. See +Hardware support changes below for instructions.

    + +

    Hardware support changes

    + +

    All hardware support (dev, drv and dbd files) except soft support has been +unbundled from base R3.14. This support includes the files symb.dbd, +drvHp1404a.o, drvEpvxiMsg.o, and drvEpvxi.o. If these are not used by your +application, comment out references to them in base.dbd.

    + +

    Hardware support now exists as separate EPICS modules. The hardware +support for your site should be downloaded and built with base R3.14 by your +EPICS administrator. You must now add the appropriate module full path +definitions to your application config/RELEASE file, and change +LIBOBJS location from $(EPICS_BASE_BIN) to the module bin +directory in your application src directory files.

    + +

    For example, remove

    + +
    +
    LIBOBJS += $(EPICS_BASE_BIN)/symb
    +
    + +

    from baseLIBOBJS and add

    + +
    +
    LIBOBJS += $(SYMB_BIN)/symb
    +
    + +

    to your application src/Makefile, and add the line

    + +
    +
    SYMB = <full path definition for the built module SYMB>
    +
    + +

    into your application config/RELEASE file.

    + +

    dbLoadtemplate tool changes

    + +

    The host tool dbLoadTemplate has been replace by a new EPICS extension, +msi, which should be downloaded and built with base R3.14 by your EPICS +administrator. dbLoadTemplate is still supported on iocs. If, in your +application, db files are created from template and substitution files you +should add the definition

    + +
    +
    MSI = <full path name to msi executable>
    +
    + +

    to your application config/RELEASE file.

    + + +

    Optional top/configure/CONFIG_SITE changes.

    + +

    Review and optionally modify site build settings.

    + + diff --git a/documentation/KnownProblems.html b/documentation/KnownProblems.html new file mode 100644 index 000000000..9a15e68c8 --- /dev/null +++ b/documentation/KnownProblems.html @@ -0,0 +1,20 @@ + + + + + + Known Problems in R3.14.12 + + + +

    EPICS Base R3.14.12: Known Problems

    + +
      + +
    • None yet.
    • + +
    + + + diff --git a/documentation/README.1st b/documentation/README.1st new file mode 100644 index 000000000..6d1142fdb --- /dev/null +++ b/documentation/README.1st @@ -0,0 +1,441 @@ + Installation Instructions + + EPICS Base Release 3.14.* + + -------------------------------------------------------------------------- + + Table of Contents + + * What is EPICS base? + * What is new in this release? + * Copyright + * Supported platforms + * Supported compilers + * Software requirements + * Host system storage requirements + * Documentation + * WWW pages + * Directory Structure + * Build related components + * Building EPICS base (Unix and Win32) + * Example application and extension + * Multiple host platforms + + -------------------------------------------------------------------------- + + What is EPICS base? + + The Experimental Physics and Industrial Control Systems (EPICS) is an + extensible set of software components and tools with which application + developers can create a control system. This control system can be used + to control accelerators, detectors, telescopes, or other scientific + experimental equipment. EPICS base is the set of core software, i.e. the + components of EPICS without which EPICS would not function. EPICS base + allows an arbitrary number of target systems, IOCs (input/output + controllers), and host systems, OPIs (operator interfaces) of various + types. + + What is new in this release? + + Please check the RELEASE_NOTES file in the distribution for description + of changes and release migration details. + + Copyright + + Please review the LICENSE file included in the distribution for legal + terms of usage. + + Supported platforms + + The list of platforms supported by this version of EPICS base is given + in the configure/CONFIG_SITE file. If you are trying to build EPICS Base + on an unlisted host or for a different target machine you must have the + proper host/target cross compiler and header files, and you will have to + create and add the appropriate new configure files to the + base/configure/os/directory. You can start by copying existing + configuration files in the configure/os directory and then make changes + for your new platforms. + + Supported compilers + + This version of EPICS base has been built and tested using the host + vendor's C and C++ compilers, as well as the GNU gcc and g++ compilers. + The GNU cross-compilers work for all cross-compiled targets. You may + need the C and C++ compilers to be in your search path to do EPICS + builds; check the definitions of CC and CCC in + base/configure/os/CONFIG.. if you have problems. + + Software requirements + + GNU make + You must use GNU make, gnumake, for any EPICS builds. Set your path so + that a gnumake version 3.81 or later is available. + + Perl + You must have Perl version 5.8.1 or later installed. The EPICS + configuration files do not specify the perl full pathname, so the perl + executable must be found through your normal search path. + + Unzip and tar (Winzip on WIN32 systems) + You must have tools available to unzip and untar the EPICS base + distribution file. + + Target systems + EPICS supports IOCs running on embedded platforms such as VxWorks and + RTEMS built using a cross-compiler, and also supports soft IOCs running + as processes on the host platform. + + vxWorks + You must have vxWorks installed if any of your target systems are + vxWorks systems. This provides the cross-compiler and header files + needed to build for these target systems. The absolute path to and + version number of the vxWorks installation is normally specified in the + base/configure/os/CONFIG_SITE.Common.vxWorksCommon file. Consult the + EPICS web pages about [1]vxWorks 5.x and [2]vxWorks 6.x and the + vxWorks documentation for information about configuring your vxWorks + operating system for use with EPICS. + + RTEMS + For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or + later. + + GNU readline or Tecla library + GNU readline and Tecla librararies can be used by the IOC shell to + provide command line editing and command line history recall and edit. + GNU readline (or Tecla library) must be installed on your target system + when COMMANDLINE_LIBRARY is set to READLINE (or TECLA) for that target. + EPICS (EPICS shell) is the default specified in CONFIG_COMMON. A + READLINE override is defined for linux-x86 in the EPICS distribution. + Comment out COMMANDLINE_LIBRARY=READLINE in + configure/os/CONFIG_SITE.Common.linux-x86 if readline is not installed + on linux-x86. Command-line editing and history will then be those + supplied by the os. On vxWorks the ledLib command-line input library is + used instead. + + Host system storage requirements + + The GNU zipped tar file is approximately 1.5 MB in size. The unzipped + untarred distribution source tree is approximately 9.0 MB. The build + created files for each host take approximately 37 MB and the build + created files for each cross target take approximately 15 MB. + + Documentation + + EPICS documentation is available through the [3]EPICS website at + Argonne. + + Release specific documenataion can also be found in the + base/documentation directory of the distribution. + + Directory Structure + + Distribution directory structure: + + base Root directory of the base distribution + base/config R3.13 compatibility build configuration files + base/config/tools Perl and shell scripts used in the R3.13 build + base/configure Operating system independent build config files + base/configure/os Operating system dependent build config files + base/configure/tools Perl and shell scripts used in the build + base/documentation Distributation documentation + base/src All epics base source code in subdirectories + base/src/RTEMS Code to configure RTEMS for EPICS + base/src/as Access security + base/src/bpt Break point table + base/src/ca Channel access + base/src/cap5 Channel Access client interface for Perl 5 + base/src/cas Channel access server library and examples + base/src/catools Channel access tools caget, cainfo, camonitor, caput + base/src/db Database access + base/src/db/test Database access tests + base/src/dbStatic Static database access + base/src/dbtools Database dbLoadTemplate tools + base/src/dev Device support (camacDev, softDev, and testDev) + base/src/excas Example channel access server + base/src/gdd General data descriptor + base/src/libCom General purpose library code in subdirectories + base/src/libCom/bucketLib Hash bucket + base/src/libCom/calc Algebraic expression interpreter + base/src/libCom/cppStd Support for C++ standard template library + base/src/libCom/cvtFast Fast number to string conversion + base/src/libCom/cxxTemplates C++ templates and templates tests + base/src/libCom/dbmf Memory management for frequent alloc/free + base/src/libCom/ellLib EPICS double linked list + base/src/libCom/env Default EPICS environment settings + base/src/libCom/error Error handling definitions and routines + base/src/libCom/fdmgr File descriptor manager + base/src/libCom/freeList Memory management using free lists + base/src/libCom/gpHash General purpose hash table + base/src/libCom/logClient Logging client + base/src/libCom/macLib Macro substitution handler + base/src/libCom/misc Miscellaneous utilities + base/src/libCom/osi Operating system independent API + base/src/libCom/osi/os Operating system dependant code in subdirectories + base/src/libCom/ring Methods for creating and using ring buffers + base/src/libCom/taskwd Task watchdog + base/src/libCom/test Test tools (timer, semBinary, semMutex,fdmgr, ...) + base/src/libCom/timer Timer + base/src/libCom/tsDefs R3.13 time stamp definitions and routines + base/src/makeBaseApp Perl tool+templates to create ioc app dvl tree + base/src/makeBaseExt Perl tool+templates to create extension dvl tree + base/src/misc Miscellaneous (coreRelease, iocInit, asSub*) + base/src/rec Record support + base/src/registry EPICS support function registry + base/src/rsrv Channel access ioc resource server library + base/src/softIoc Example softIoc + base/src/tools Perl scripts used during the builds + base/src/toolsComm Code for the build tools antelope and e_flex + base/src/util Utilities (ca_test, iocLogServer, startCArepeater) + base/src/vxWorks R3.13 compatibility code specific to vxWorks + base/startup Scripts for setting up path and environment + + Install directories created by the build: + + bin Installed scripts and executables in subdirs + cfg Installed build configuration files + db Installed data bases + dbd Installed data base definitions + doc Installed documentation files + html Installed html documentation + include Installed header files + include/os Installed os specific header files in subdirs + javalib Installed java class and jar files + lib Installed libraries in arch subdirectories + lib/perl Installed perl modules + templates Installed templates + + Build related components + + base/documentation/README* files + + README.1st Instructions for setup and building epics base + README.html html version of README.1st + README.WIN32 Microsoft WIN32 specific instructions + README.cxxTemplates Information about C++ templates in EPICS base + README.niCpu030 NI cpu030 specific instructions + README.darwin Installation notes for Mac OS X (Darwin) + RELEASE_NOTES.html Notes on release changes + KnownProblems.html List of known problems and workarounds + Converting*To*.html Release specific conversion instructions + Building*With*.html Release specific build instructions + + base/startup directory - contains scripts to set environment and path + + EpicsHostArch C shell script to set EPICS_HOST_ARCH env variable + EpicsHostArch.pl Perl script to set EPICS_HOST_ARCH env variable + Site.profile bourne shell script to set path and env variables + Site.cshrc c shell script to set path and env variables + borland.bat WIN32 bat file to set borland path and env variables + cygwin.bat WIN32 bat file to set cygwin path and env variables + win32.bat WIN32 bat file to set path and env variables + win32-debug.bat WIN32 debug bat file to set debug path and env variables + + base/configure directory - contains build definitions and rules + + CONFIG Includes configure files and allows variable overrides + CONFIG.CrossCommon Cross build definitions + CONFIG.gnuCommon Gnu compiler build definitions for all archs + CONFIG_ADDONS Definitions for and DEFAULT options + CONFIG_BASE EPICS base tool and location definitions + CONFIG_BASE_VERSION Definitions for EPICS base version number + CONFIG_COMMON Definitions common to all builds + CONFIG_ENV Definitions of EPICS environment variables + CONFIG_SITE Site specific make defintions + CONFIG_SITE_ENV Site defaults for EPICS environment variables + MAKEFILE Installs CONFIG* RULES* creates + CONFIG_APP_INCLUDE + RELEASE Location of external products + RULES Includes appropriate rules file + RULES.Db Rules for database and database definition files + RULES.ioc Rules for application iocBoot/ioc* directory + RULES_ARCHS Definitions and rules for building architectures + RULES_BUILD Build and install rules and definitions + RULES_DIRS Definitions and rules for building subdirectories + RULES_JAVA Definitions and rules for java jars and classes + RULES_TOP Rules specific to a dir (uninstall and tar) + Sample.Makefile Sample makefile with comments + + base/configure/os directory - contains os-arch specific definitions + + CONFIG.. Specific host-target build definitions + CONFIG.Common. Specific target definitions for all hosts + CONFIG..Common Specific host definitions for all targets + CONFIG.UnixCommon.Common Definitions for Unix hosts and all targets + CONFIG.Common.UnixCommon Definitions for Unix targets and all hosts + CONFIG.Common.vxWorksCommon Specific host definitions for all vx targets + CONFIG_COMPAT R3.13 arch compatibility definitions + CONFIG_SITE.. Site specific host-target definitions + CONFIG_SITE.Common. Site specific target defs for all hosts + CONFIG_SITE..Common Site specific host defs for all targets + + base/src/tools directory - contains Perl scripts used for the build + + Makefile Makefile for installing the scripts into cfg dir + convertRelease.pl Performs consistancy checks on RELEASE files + cvsclean.pl Remove all .#* files in directory tree + dos2unix.pl Converts text file from DOS CR/LF to unix ISO + expandvars.pl Tool to expand @VAR@ variables while copying a file + filterWarnings.pl Filters warning messages during HP builds + fullpathName.pl Returns fullpath name of directory arg + installEpics.pl Installs built files into install directories + makeIncludeDbd.pl Creates *Include.dbd file from filename args files + makeMakefile.pl Creates a Makefile in O. dirs + makeTestfile.pl Generates a test harness $target.t file + mkmf.pl Generates dependencies from include stmnts + munch.pl Creates a ctdt.c file for vxWorks targets + replaceVAR.pl Changes CapFast VAR(xxx) to $(xxx) notation + useManifestTool.pl Use MS VC++ version to set usage of Manifest Tool + + Building EPICS base (Unix and Win32) + + Unpack file + + Unzip and untar the distribution file. Use WinZip on Windows systems. + + Set environment variables + + Files in the base/startup directory have been provided to help set + required path and other environment variables. + + EPICS_HOST_ARCH + Before you can build or use EPICS R3.14, the environment variable + EPICS_HOST_ARCH must be defined. A perl script EpicsHostArch.pl in the + base/startup directory has been provided to help set EPICS_HOST_ARCH. + You should have EPICS_HOST_ARCH set to your host operating system + followed by a dash and then your host architecture, e.g. + solaris-sparc. If you are not using the OS vendor's c/c++ compiler for + host builds, you will need another dash followed by the alternate + compiler name (e.g. "-gnu" for GNU c/c++ compilers on a solaris host + or "-borland" for Borland c/c++ compilers on a WIN32 host). See + configure/CONFIG_SITE for a list of supported EPICS_HOST_ARCH values. + + PERLLIB + On WIN32, some versions of Perl require that the environment variable + PERLLIB be set to . + + PATH + As already mentioned, you must have the perl executable and you may + need C and C++ compilers in your search path. For building base you + also must have echo in your search path. For Unix host builds you also + need ln, cpp, cp, rm, mv, and mkdir in your search path and /bin/chmod + must exist. On some Unix systems you may also need ar and ranlib in + your path, and the C compiler may require as and ld in your path. On + solaris systems you need uname in your path. + + LD_LIBRARY_PATH + It is no longer necessary to have LD_LIBRARY_PATH include EPICS + directories on a Unix type system. R3.14 shared libraries and + executables will contain the full path name to libraries they require. + However, if you move the EPICS directories from their build-time + location then in order for libraries to be found at runtime + LD_LIBRARY_PATH must include the full pathname to + $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking executables. + Building shared libraries is now the default setting for all Unix type + hosts. NOTE: You will still need LD_LIBRARY_PATH for R3.13 extension + shared libraries even if the R3.13 extensions are built with R3.14 + base. + + Win32 PATH + On WIN32 systems, for R3.14.7 and later, it is no longer necessary to + add fullpathname to $(INSTALL_LOCATION)/bin/$(EPICS_HOST_ARCH) to your + path for finding dlls during EPICS builds. The win32 configure files + in base now add this directory to the path definition. + + Do site-specific build configuration + + Site configuration + To configure EPICS, you may want to modify the default definitions in + the following files: + + configure/CONFIG_SITE Build choices. Specify target archs. + configure/CONFIG_SITE_ENV Environment variable defaults + configure/RELEASE TORNADO2 full path location + + Host configuration + To configure each host system, you may override the default + definitions by adding a new file in the configure/os directory with + override definitions. The new file should have the same name as the + distribution file to be overridden except with CONFIG in the name + changed to CONFIG_SITE. + + configure/os/CONFIG.. Host build settings + configure/os/CONFIG..Common Host common build settings + + Target configuration + To configure each target system, you may override the default + definitions by adding a new file in the configure/os directory with + override definitions. The new file should have the same name as the + distribution file to be overridden except with CONFIG in the name + replaced by CONFIG_SITE. This step is necessary even if the host + system is the only target system. + + configure/os/CONFIG.Common. Target common settings + configure/os/CONFIG.. Host-target settings + + R3.13 compatibility configuration + To configure EPICS base for building with R3.13 extensions and ioc + applications , you must modify the default definitions in the + base/config/CONFIG_SITE* files to agree with definitions you made in + base/configure and base/configure/os files. + + Build EPICS base + + After configuring the build you should be able to build EPICS base by + issuing the following commands in the distribution's root directory + (base): + + gnumake clean uninstall + gnumake + + The command "gnumake clean uninstall" will remove all files and + directories generated by a previous build. The command "gnumake" will + build and install everything for the configured host and targets. + + It is recommended that you do a "gnumake clean uninstall" at the root + directory of an EPICS directory structure before each complete rebuild + to ensure that all components will be rebuilt. + + Example application and extension + + A perl tool, makeBaseApp.pl is included in the distribution file. This + script will create a sample application that can be built and then + executed to try out this release of base. + + Instructions for building and executing the 3.14 example application can + be found in the section "Example Application" of Chapter 2, "Getting + Started", in the "IOC Application Developer's Guide" for this release. + The "Example IOC Application" section briefly explains how to create and + build an example application in a user created directory. It also + explains how to run the example application on a vxWorks ioc or as a + process on the host system. By running the example application as a + host-based IOC, you will be able to quickly implement a complete EPICS + system and be able to run channel access clients on the host system. + + A perl script, makeBaseExt.pl, is included in the distribution file. + This script will create a sample extension that can be built and + executed. The makeBaseApp.pl and makeBaseExt.pl scripts are installed + into the install location bin/ directory during the base + build. + + Multiple host platforms + + You can build using a single EPICS directory structure on multiple host + systems and for multiple cross target systems. The intermediate and + binary files generated by the build will be created in separate + subdirectories and installed into the appropriate separate host/target + install directories. EPICS executables and perl scripts are installed + into the $(INSTALL_LOCATION)/bin/ directories. Libraries are + installed into $(INSTALL_LOCATION)/lib/. The default definition + for $(INSTALL_LOCATION) is $(TOP) which is the root directory in the + distribution directory structure, base. Created object files are stored + in O. source subdirectories, This allows objects for multiple + cross target architectures to be maintained at the same time. To build + EPICS base for a specific host/target combination you must have the + proper host/target C/C++ cross compiler and target header files and the + base/configure/os directory must have the appropriate configure files. + +References + + 1. http://www.aps.anl.gov/epics/base/tornado.php + 2. http://www.aps.anl.gov/epics/base/vxWorks6.php + 3. http://www.aps.anl.gov/epics/ diff --git a/documentation/README.MS_Windows b/documentation/README.MS_Windows new file mode 100644 index 000000000..415ff2596 --- /dev/null +++ b/documentation/README.MS_Windows @@ -0,0 +1,224 @@ +WARNING: THIS FILE IS OLD +========================= + +Some of the information in this file is likely to be out of date. It has not been +updated since 2005, so things like the minimum required versions are now wrong. + + +Compiling EPICS and Building IOC Applications on MS Windows +----------------------------------------------------------- + +Original port of EPICS base build system to Windows was done by Kay-Uwe Kasemir 11/96 + +Please mail questions, comments, corrections, additional examples, etc to Jeff Hill + at johill###at###lanl.gov (replace ###at### with @ of course) + +0) what you will get +-------------------- + +This port of EPICS to windows allows you to build for two host architectures: + +win32-x86 - This is the native port to windows. Probably faster, more efficent, and with + more precise time stamps. The OS dependent layers are carefully optimized + for EPICS. There is prioritized schedualing of threads. + +cygwin-x86 - This is the POSIX port of EPICS layered onto cygwin which is layered again + onto win32. We have less experience with this version, but it is in use. I + suspect that Mark Rivers has the most experience with it. Mutexes may be + slower, and time stamps are probably less precise. There were problems with + interrupting blocking system calls during cleanup in past version that may + be resolved in more recent versions of cygwin. + +Once you have completed a host and or target build you can run all of the base components on +windows. This includes the client library (ca.dll, Com.dll), soft IOCs, portable server etc. + +1) what you will need +--------------------- + +Depending on how you set the EPICS_HOST ARCH environment variable, you will need + +o win32-x86 - MS Visual C/C++ (probably version 6 is the earliest version that can be used) + (I understand that there are also available free versions of this compiler) +o win32-x86-borland - Borland C free compiler and linker (I use Borland C++ 5.5.1 successfuly) +o win32-x86-mingw - MingW standalone GNU developers tools www.mingw.org +o win32-x86-cygwin - Cygwin hosted developers tools www.cygwin.com +o cigwin-x86 - Cygwin hosted developers tools targeting Cygwin GNU based posix www.cygwin.com + +You can also cross compiler for Tornado (and possibly RTEMS) if you have it (them) installed on +Windows. To build only for win32-x86 and not cross compile for IOC development type +"make win32-x86" or in your configure/CONFIG_SITE file set "CROSS_COMPILER_HOST_ARCHS=" + +And some tools: + gnu make - www.gnu.org (need 3.78.1 or later) + perl - www.perl.org (need 5.0 or later) + +The gnu make and perl executables are also downloadable from www.cygwin.com + +The perl interpreter and gnu make are also available 'on the net' +as sources which compile with MS Visual C++. + +2) set environment variables + +(Setting env. vars. is different: for NT/W2K/WXP, use Settings/System, +for Win95 use autoexec.bat) + +When setting paths in the EPICS CONFIG files for win32-x86 the following +are hints in case you have trouble. You should not need to worry about +this unless you type a path into one of the EPICS config files that +includes a "\". In most situations gnu make, windows NT/W2K/WXP, the MS compiler, +and the MS linker will accept "/" and this will result in less trouble. + + ** Note that that each "\" in any path variables you set + must be replaced with a "\\" (this is because GNU make treats + all "\" characters as line continuation) + + *and* Note that that each space in any file name or + path name variable you set must be replaced with + a "\ " (this is because GNU make treats all " " separated + input as independent tokens in the input stream. + + ** win32-x86 will generally allow "/" and "\" interchangeably in file paths, + but the DOS shell only accepts "\". + + ** Certain command line utilities such as the MS linker are known to in rare + situations confuse "/" in a path with command line options, and it may + be necessary to replace a "/" in a path that configured with "\\", but + the bulk of our experience indicates that this is not the necessary. + +Your path should include (in additon to ms system directories): +- The ms system directories +- The EPICS-binaries we are building base/bin/win32-x86 + (really where INSTALL_LOCATION specifies) +- The developer tool set binaries +- perl +- GNU make + +Check with e.g.: echo %Path% + +On NT/W2K/XP, "Path" is defined by the operating system, on Win95, it's "PATH" instead. + +Of course, Tornado should be installed properly with these env. variables set: + +WIND_BASE=c:\Tornado (required for cross development only) +WIND_HOST_TYPE=x86-win32 (required for cross development only) + +This way the EPICS makesystem can locate Tornado without any changes to the files +in base/config. So for pc486 the settings in CONFIG_SITE where you specify the +location of VxWorks are ignored, this information is taken from WIND_BASE and +_HOST_TYPE!! + +If building with MSVC one ususally runs one of the following from a .cmd file: +call "c:\program files\Microsoft Visual Studio\VC98\Bin\Vcvars32.bat" +call "C:\Program Files\Microsoft Visual Studio .NET\Vc7\bin\Vcvars32.bat" +call "C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\Vcvars32.bat" + +The EPICS build system requires EPICS_HOST_ARCH environment variable +Select host arch to build: + EPICS_HOST_ARCH=win32-x86 + EPICS_HOST_ARCH=win32-x86-borland + EPICS_HOST_ARCH=win32-x86-mingw + EPICS_HOST_ARCH=win32-x86-cygwin + EPICS_HOST_ARCH=cygwin-x86 + +Set the "TMP" environment variable if you need to specify where +temporary files are created. + +Directory Used For Temporary Files Conditions +---------------------------------- ---------- +Directory specified by TMP TMP environment variable is set, + and directory specified by TMP exists. +dir argument to _tempnam TMP environment variable is not set, or + directory specified by TMP does not exist. +P_tmpdir in STDIO.H dir argument is NULL, or dir is name of + nonexistent directory. +Current working directory P_tmpdir does not exist. + +On my system I see in stdio.h that _P_tmpdir is "/". Here is a common +setting for "TMP" (the C:\TEMP directory must exist). + +TMP=C:\TEMP + +3) building EPICS +----------------- + +Prepare apx. 2 ltr. Tee and type: + + cd /base + make (use gnu make) + +Watch for errors and send them to me. + +Known problems: +* Sometimes there are clock synchronization problems when mounting UNIX +file systems onto windows which results in warnings/errors like + "file has modification date in the future" + for newly created things. + Very seldom this is fatal, so you have to + stop gnumake and restart it. + +4) Creating EPICS IOC applications under win32-x86 + +o create application development folder + +o start a DOS window and change your working directory to the folder + created above (with the DOS "cd" command) + +o to create an example application type: + "perl c:\epics\bin\win32\makeBaseApp.pl -b c:\\epics -e + + ** Note that that each "\" above in any path arguments to makeBaseApp.pl + must be replaced with a "\\" (this is because GNU make treats + all "\" characters as line continuation) + + ** Note that that each space in any file name or + path name argument to makeBaseApp.pl must be replaced with + a "\ " (this is because GNU make treats all " " separated + input as independent tokens in the input stream. + + ** Note that c:\epics above must be replaced by the path + to your epics source installation (or where INSTALL_LOCATION + specifies) + +o General information on EPICS IOC application development can be found in + the "EPICS IOC Application Developers Guide". To see all of the options + supported by makeBaseApp.pl type "perl c:\epics\bin\win32\makeBaseApp.pl" + +5) EPICS GNU make makefiles can be executed from within a Visual C++ "makefile" +style project. This allows EPICS programs to be developed directly inside of +the visual C++ environment. To do this create a "makefile" project and place your +gnu make command in the build configuration (accessed from the project/settings menu). +You will also need to add GNU make and /bin/win32 into the Visual C++ +executable search path (from the tools/options menu). +In visual C++ it is possible to double click on the compiler +error messages generated within an EPICS "makefile" style project and have visual +C++ immediately position the cursor on the corresponding line in the source. I +have found that this works correctly with Makefile projects if the project is in a +directory just below the source code. The following build command works well +in a visual C++ make file project: "kill caRepeater.exe&make -C ..". Be careful +not to introduce additional spaces around the &. The kill.exe command is in the +NT resource kit. + +6) Issues that you should be aware of if you are building code with MSVC that +calls EPICS, but you are not using the EPICS build system. + +6a) You will need to include header files from the following paths. + \base\include + \base\include\os\win32 +6b) You will need to link with the following path in effect. + \base\lib\win32-x86. +6c) If the visual C++ /Za option is not used then you will also need to define + __STDC__ to be zero on the command line so that EPICS headers will know that + a ANSI standard C compiler is in use. +6d) If you link with EPICS object libraries then specify /MT or /MTd + depending on whether EPICS base and your code are built for debugging. + This specifies the multithreaded operating environment required by EPICS. + This will also not define _DLL and therefore the EPICS header files will + not specify that sharable libraries are being called. EPICS object library + names follow the convention "xxxObj.lib". +6e) If you link with EPICS shareable libraries (with DLLs) then you must + use /MDd or /MD depending on whether EPICS base and your code are + built for debugging. This specifies the multithreaded operating environment + required by EPICS. This will also define _DLL and therefore the EPICS header + files will specify an optimized calling convention for shareable libraries. + EPICS shareable libraries (DLL) names follow the convention "xxx.lib" + and "xxx.dll". diff --git a/documentation/README.cris b/documentation/README.cris new file mode 100644 index 000000000..c44f31bb0 --- /dev/null +++ b/documentation/README.cris @@ -0,0 +1,67 @@ +cross compiling EPICS and + building IOC Applications for cris architectures + (linux-cris_v10, linux-cris_v32) +====================================================================== + +Please mail questions, comments, corrections, etc. ... +to P.Zumbruch@gsi.de +November 2007 + +Tools needed +------------ + + o Axis SDK + - Overview: + http://developer.axis.com/wiki/doku.php?id=axis:sdk + - Download: + http://www.axis.com/products/dev_sdk/download_dist.php + - Install HOWTO: + http://developer.axis.com/wiki/doku.php?id=axis:software_distribution_install_howto + o Axis GNU gcc release for cross compiling + - Download: + http://www.axis.com/products/dev_sdk/download_compiler.php + - Install HOWTO: + http://developer.axis.com/wiki/doku.php?id=axis:compiler_install + +Environment +----------- + + o CRIS_CROSS_COMPILER + - path to top directory of cris cross compiler, + where binaries are in sub directory bin/ + - if not set, the make process will stop at place + UNDEFINED_ENV__CRIS_CROSS_COMPILER + o AXIS_TOP_DIR?=UNDEFINED_ENV__AXIS_TOP_DIR + - path to axis SDK top directory + - if not set compile and link commands will contain references to + UNDEFINED_ENV__AXIS_TOP_DIR + - to set the necessary variables, execute + . ./init_env + in the top directory of the SDK provided here. + o CRIS_COMPILER_DEBUG + - if defined symbols won't be stripped, + resulting in comparably large files + +Building +-------- + + o Edit the CONFIG_SITE files + - CONFIG_SITE.linux-x86.Common: + for CROSS_COMPILER_TARGET_ARCHS += linux-cris_v10 + for CROSS_COMPILER_TARGET_ARCHS += linux-cris_v32 + - optionally CONFIG_SITE.linux-x86.linux-cris + for setting CRIS_CROSS_COMPILER + - optionally create CONFIG_SITE.linux-x86.linux-cris_v10 + - optionally create CONFIG_SITE.linux-x86.linux-cris_v32 + o "make". + +Shared Libraries +---------------- + +Generating shared libraries is not supported. + + +Please feel free to contact me if you +encounter serious problems. + +Peter diff --git a/documentation/README.darwin.html b/documentation/README.darwin.html new file mode 100644 index 000000000..67e60dd92 --- /dev/null +++ b/documentation/README.darwin.html @@ -0,0 +1,186 @@ + + +Installation notes for EPICS on Mac OS X (Darwin) + + + + +

    Building EPICS base

    +
      +
    • +To build base: +
        +
      1. +Set the EPICS_HOST_ARCH environment variable to darwin-ppc, darwin-x86 or darwin-ppcx86. +The scripts in the +base/startup directory can automate this. For example, here's part +of my Bash login script (~/.bash_login): +
        +#
        +# EPICS
        +#
        +EPICS_BASE="${HOME}/src/EPICS/base"
        +EPICS_EXTENSIONS="${HOME}/src/EPICS/extensions"
        +. "${EPICS_BASE}"/startup/Site.profile
        +
        +
      2. +
      3. +cd to the EPICS base top-level source directory. +
      4. +
      5. +Uncomment the appropriate line in the relevent +EPICS_BASE/configure/os/CONFIG_SITE.Common.darwin-xxx file for your EPICS_HOST_ARCH value. +Newer versions of OS X (e.g. Snow Leopard) may include only 64 bit versions of some OS libraries, +so should only have the x86_64 ARCH_CLASS. +
      6. +
      7. +Run make. +
      8. +
      + +
    • + +
    • +As distributed, EPICS on Mac OS X uses the readline command line input +routines. IOC applications are more pleasant to interact with if +either the readline or libtecla library is used. The easiest +way to get either or both of these libraries on to your system is to +download and install them using the either the DarwinPorts +distribution or the Fink package manager. If you don't want to install +the readline library, set the COMMANDLINE_LIBRARY variable in one of +the CONFIG_SITE files to EPICS. +

      +Information on DarwinPorts is available from +the DarwinPorts +project page. +DarwinPorts binary packages are available from +here. +

      +Fink may be downloaded from +the Source Forge. +

    • + +
    • +If broadcasts are not seen locally, try adding "localhost" (127.0.0.1) +to the EPICS_CA_ADDR_LIST. +
    • +
    + +

    Building EPICS extensions

    +

    +Many extensions build and run properly on OS X. To build and run medm, first +obtain the X11 run-time and developer packages from Apple and the OpenMotif3 +package from Fink. + +

    Objective-C and AppleScript

    +

    +Code written in Objective-C can be included in host or IOC applications. +Here are a couple of short Objective-C examples which can be used to send +AppleScript events to other applications on the OS X machine. + +

    +/*
    + * exampleAppleScriptRecord.m 
    + *
    + * Simple Objective-C/AppleScript subroutine record
    + *
    + * To use this record in an application:
    + *
    + * 1) Make the following changes to the application Makefile:
    + *    - Add exampleAppleScriptRecord.m to the application SRCS.
    + *    - Add -framework Foundation to the application LDFLAGS.
    + * 2) Add the following line to the application database description:
    + *      registrar(registerExampleAppleScript)
    + * 3) Add a record to the application database:
    + *      record(sub,"setVolume")
    + *      {
    + *          field(SNAM,"exampleAppleScriptProcess")
    + *      }
    + */
    +#import <Foundation/Foundation.h>
    +#include <registryFunction.h>
    +#include <subRecord.h>
    +#include <alarm.h>
    +#include <errlog.h>
    +#include <recGbl.h>
    +#include <epicsExport.h>
    +
    +/*
    + * Shim between EPICS and NSAppleScript class.
    + */
    +static long
    +exampleAppleScriptProcess(struct subRecord *psub)
    + {
    +    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    +    NSDictionary *err;
    +    NSAppleScript *nsa;
    +    
    +    nsa = [[NSAppleScript alloc] initWithSource:[NSString stringWithFormat:
    +                @"tell application \"Finder\" to set volume %g\n", psub->a]];
    +    if ([nsa executeAndReturnError:&err] == nil) {
    +        errlogPrintf("Failed to run AppleScript: %s\n",
    +                        [[err objectForKey:NSAppleScriptErrorMessage] cString]);
    +        recGblSetSevr(psub, SOFT_ALARM, INVALID_ALARM);
    +    }
    +    [nsa release];
    +    [pool release];
    +    return 0;
    +}
    +
    +static registryFunctionRef subRef[] = {
    +    {"exampleAppleScriptProcess",(REGISTRYFUNCTION)exampleAppleScriptProcess}
    +};
    +
    +static void registerExampleAppleScript(void)
    +{
    +    registryFunctionRefAdd(subRef,NELEMENTS(subRef));
    +}
    +epicsExportRegistrar(registerExampleAppleScript);
    +
    +
    +==============================================================================
    +/*
    + * runAppleScript.m 
    + *
    + * Simple Objective-C/AppleScript shim to allow EPICS application to
    + * send arbitrary AppleScript messages to other applications.
    + *
    + * To use this subroutine in an application make the following
    + * changes to the application Makefile:
    + * - Add runAppleScript.m to the application SRCS.
    + * - Add -framework Foundation to the application LDFLAGS.
    + */
    +#import <Foundation/Foundation.h>
    +#include <errlog.h>
    +
    +int
    +runAppleScript(const char *format, ...)
    +{
    +    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    +    NSString *script;
    +    NSMutableDictionary *err;
    +    NSAppleScript *nsa;
    +    va_list args;
    +    int ret = 0;
    +    
    +    va_start(args, format);
    +    script = [[NSString alloc] initWithFormat:
    +                            [NSString stringWithCString:format] arguments:args];
    +    va_end(args);
    +    err = [NSMutableDictionary dictionaryWithCapacity:10];
    +    nsa = [[NSAppleScript alloc] initWithSource:script];
    +    if ([nsa executeAndReturnError:&err] == nil) {
    +        errlogPrintf("Failed to run AppleScript: %s\n",
    +                        [[err objectForKey:NSAppleScriptErrorMessage] cString]);
    +        ret = -1;
    +    }
    +    [script release];
    +    [nsa release];
    +    [pool release];
    +    return ret;
    +}
    +
    +
    +Revision-Id: anj@aps.anl.gov-20101013162117-lno274eeoc48s03e + + diff --git a/documentation/README.html b/documentation/README.html new file mode 100644 index 000000000..f84dbfca5 --- /dev/null +++ b/documentation/README.html @@ -0,0 +1,464 @@ + + + +README - EPICS Base Installation Instructions + + +
    +

    Installation Instructions

    +

    EPICS Base Release 3.14.*


    +
    +
    +

    Table of Contents

    + +
    + +

    What is EPICS base?

    +
    The Experimental Physics and Industrial Control Systems + (EPICS) is an extensible set of software components and tools with + which application developers can create a control system. This control + system can be used to control accelerators, detectors, telescopes, or + other scientific experimental equipment. EPICS base is the set of core + software, i.e. the components of EPICS without which EPICS would not + function. EPICS base allows an arbitrary number of target systems, IOCs + (input/output controllers), and host systems, OPIs (operator + interfaces) of various types.
    + +

    What is new in this release?

    +
    Please check the RELEASE_NOTES file in the distribution for + description of changes and release migration details.
    + +

    Copyright

    +
    Please review the LICENSE file included in the + distribution for legal terms of usage.
    + +

    Supported platforms

    + +
    The list of platforms supported by this version of EPICS base + is given in the configure/CONFIG_SITE file. If you are trying to build + EPICS Base on an unlisted host or for a different target machine you + must have the proper host/target cross compiler and header files, and + you will have to create and add the appropriate new configure files to + the base/configure/os/directory. You can start by copying existing + configuration files in the configure/os directory and then make changes + for your new platforms.
    + +

    Supported compilers

    + +
    This version of EPICS base has been built and tested using the host + vendor's C and C++ compilers, as well as the GNU gcc and g++ compilers. The GNU + cross-compilers work for all cross-compiled targets. You may need the C and C++ + compilers to be in your search path to do EPICS builds; check the definitions + of CC and CCC in base/configure/os/CONFIG.<host>.<host> if you have + problems.
    + +

    Software requirements

    + +
    GNU make
    + You must use GNU make, gnumake, for any EPICS builds. Set your path + so that a gnumake version 3.81 or later is available. + +

    Perl
    + You must have Perl version 5.8.1 or later installed. The EPICS configuration + files do not specify the perl full pathname, so the perl executable must + be found through your normal search path.

    + +

    Unzip and tar (Winzip on WIN32 systems)
    + You must have tools available to unzip and untar the EPICS base + distribution file.

    + +

    Target systems
    + EPICS supports IOCs running on embedded platforms such as VxWorks + and RTEMS built using a cross-compiler, and also supports soft IOCs running + as processes on the host platform.

    + +

    vxWorks
    + You must have vxWorks installed if any of your target systems are + vxWorks systems. This provides the cross-compiler and header files + needed to build for these target systems. The absolute path to and version + number of the vxWorks installation is normally specified in the + base/configure/os/CONFIG_SITE.Common.vxWorksCommon file. Consult the EPICS web + pages about vxWorks + 5.x and vxWorks + 6.x and the vxWorks documentation for information about configuring your + vxWorks operating system for use with EPICS.

    + +

    RTEMS
    + For RTEMS targets, you need RTEMS core and toolset version 4.9.2 or later.

    + +

    GNU readline or Tecla library
    + GNU readline and Tecla librararies can be used by the IOC shell to + provide command line editing and command line history recall and edit. + GNU readline (or Tecla library) must be installed on your target system + when COMMANDLINE_LIBRARY is set to READLINE (or TECLA) for that target. + EPICS (EPICS shell) is the default specified in CONFIG_COMMON. A + READLINE override is defined for linux-x86 in the EPICS distribution. + Comment out COMMANDLINE_LIBRARY=READLINE in + configure/os/CONFIG_SITE.Common.linux-x86 if readline is not installed + on linux-x86. Command-line editing and history will then be those + supplied by the os. On vxWorks the ledLib command-line input library is + used instead.

    +
    + +

    Host system storage requirements

    +
    The GNU zipped tar file is approximately 1.5 MB in size. The + unzipped untarred distribution source tree is approximately 9.0 MB. The + build created files for each host take approximately 37 MB and the + build created files for each cross target take approximately 15 MB.
    + +

    Documentation

    +
    EPICS documentation is available through the + EPICS website at Argonne. +

    Release specific documenataion can also be found in the base/documentation + directory of the distribution.

    + +

    Directory Structure

    +

    Distribution directory structure:

    + +
    +        base                         Root directory of the base distribution
    +        base/config                  R3.13 compatibility build configuration files
    +        base/config/tools            Perl and shell scripts used in the R3.13 build
    +        base/configure               Operating system independent build config files
    +        base/configure/os            Operating system dependent build config files
    +        base/configure/tools         Perl and shell scripts used in the build
    +        base/documentation           Distributation documentation
    +        base/src                     All epics base source code in subdirectories
    +        base/src/RTEMS               Code to configure RTEMS for EPICS
    +        base/src/as                  Access security
    +        base/src/bpt                 Break point table
    +        base/src/ca                  Channel access
    +        base/src/cap5                Channel Access client interface for Perl 5
    +        base/src/cas                 Channel access server library and examples
    +        base/src/catools             Channel access tools caget, cainfo, camonitor, caput
    +        base/src/db                  Database access
    +        base/src/db/test             Database access tests
    +        base/src/dbStatic            Static database access
    +        base/src/dbtools             Database dbLoadTemplate tools
    +        base/src/dev                 Device support (camacDev, softDev, and testDev)
    +        base/src/excas               Example channel access server
    +        base/src/gdd                 General data descriptor
    +        base/src/libCom              General purpose library code in subdirectories
    +        base/src/libCom/bucketLib    Hash bucket
    +        base/src/libCom/calc         Algebraic expression interpreter
    +        base/src/libCom/cppStd       Support for C++ standard template library
    +        base/src/libCom/cvtFast      Fast number to string conversion
    +        base/src/libCom/cxxTemplates C++ templates and templates tests
    +        base/src/libCom/dbmf         Memory management for frequent alloc/free
    +        base/src/libCom/ellLib       EPICS double linked list
    +        base/src/libCom/env          Default EPICS environment settings
    +        base/src/libCom/error        Error handling definitions and routines
    +        base/src/libCom/fdmgr        File descriptor manager
    +        base/src/libCom/freeList     Memory management using free lists
    +        base/src/libCom/gpHash       General purpose hash table
    +        base/src/libCom/logClient    Logging client
    +        base/src/libCom/macLib       Macro substitution handler
    +        base/src/libCom/misc         Miscellaneous utilities
    +        base/src/libCom/osi          Operating system independent API
    +        base/src/libCom/osi/os       Operating system dependant code in subdirectories
    +        base/src/libCom/ring         Methods for creating and using ring buffers
    +        base/src/libCom/taskwd       Task watchdog
    +        base/src/libCom/test         Test tools (timer, semBinary, semMutex,fdmgr, ...)
    +        base/src/libCom/timer        Timer
    +        base/src/libCom/tsDefs       R3.13 time stamp definitions and routines
    +        base/src/makeBaseApp         Perl tool+templates to create ioc app dvl tree
    +        base/src/makeBaseExt         Perl tool+templates to create extension dvl tree
    +        base/src/misc                Miscellaneous (coreRelease, iocInit, asSub*)
    +        base/src/rec                 Record support
    +        base/src/registry            EPICS support function registry
    +        base/src/rsrv                Channel access ioc resource server library
    +        base/src/softIoc             Example softIoc
    +        base/src/tools               Perl scripts used during the builds
    +        base/src/toolsComm           Code for the build tools antelope and e_flex
    +        base/src/util                Utilities (ca_test, iocLogServer, startCArepeater)
    +        base/src/vxWorks             R3.13 compatibility code specific to vxWorks
    +        base/startup                 Scripts for setting up path and environment
    +
    + +

    Install directories created by the build:

    +
    +        bin                     Installed scripts and executables in subdirs
    +        cfg                     Installed build configuration files
    +        db                      Installed data bases
    +        dbd                     Installed data base definitions
    +        doc                     Installed documentation files
    +        html                    Installed html documentation
    +        include                 Installed header files
    +        include/os              Installed os specific header files in subdirs
    +        javalib                 Installed java class and jar files
    +        lib                     Installed libraries in arch subdirectories
    +        lib/perl                Installed perl modules
    +        templates               Installed templates
    +
    +
    + +

    Build related components

    +
    + +

    base/documentation/README* files

    +
    +        README.1st           Instructions for setup and building epics base
    +        README.html          html version of README.1st
    +        README.WIN32         Microsoft WIN32 specific instructions
    +        README.cxxTemplates  Information about C++ templates in EPICS base
    +        README.niCpu030      NI cpu030 specific instructions
    +        README.darwin        Installation notes for Mac OS X (Darwin)
    +        RELEASE_NOTES.html   Notes on release changes
    +        KnownProblems.html   List of known problems and workarounds
    +        Converting*To*.html  Release specific conversion instructions
    +        Building*With*.html  Release specific build instructions
    +
    + +

    base/startup directory - contains scripts to set environment and path

    +
    +        EpicsHostArch       C shell script to set EPICS_HOST_ARCH env variable
    +        EpicsHostArch.pl    Perl script to set EPICS_HOST_ARCH env variable
    +        Site.profile        bourne shell script to set path and env variables
    +        Site.cshrc          c shell script to set path and env variables
    +        borland.bat         WIN32 bat file to set borland path and env variables
    +        cygwin.bat          WIN32 bat file to set cygwin path and env variables
    +        win32.bat           WIN32 bat file to set path and env variables
    +        win32-debug.bat     WIN32 debug bat file to set debug path and env variables
    +
    + +

    base/configure directory - contains build definitions and rules

    +
    +        CONFIG                Includes configure files and allows variable overrides
    +        CONFIG.CrossCommon    Cross build definitions
    +        CONFIG.gnuCommon      Gnu compiler build definitions for all archs
    +        CONFIG_ADDONS         Definitions for <osclass> and DEFAULT options
    +        CONFIG_BASE           EPICS base tool and location definitions
    +        CONFIG_BASE_VERSION   Definitions for EPICS base version number
    +        CONFIG_COMMON         Definitions common to all builds
    +        CONFIG_ENV            Definitions of EPICS environment variables
    +        CONFIG_SITE           Site specific make defintions
    +        CONFIG_SITE_ENV       Site defaults for EPICS environment variables
    +        MAKEFILE              Installs CONFIG* RULES* creates
    +        CONFIG_APP_INCLUDE
    +        RELEASE               Location of external products
    +        RULES                 Includes appropriate rules file
    +        RULES.Db              Rules for database and database definition files
    +        RULES.ioc             Rules for application iocBoot/ioc* directory
    +        RULES_ARCHS           Definitions and rules for building architectures
    +        RULES_BUILD           Build and install rules and definitions
    +        RULES_DIRS            Definitions and rules for building subdirectories
    +        RULES_JAVA            Definitions and rules for java jars and classes
    +        RULES_TOP             Rules specific to a <top> dir (uninstall and tar)
    +        Sample.Makefile       Sample makefile with comments
    +
    + +

    base/configure/os directory - contains os-arch specific definitions

    +
    +        CONFIG.<host>.<target>      Specific host-target build definitions
    +        CONFIG.Common.<target>      Specific target definitions for all hosts
    +        CONFIG.<host>.Common        Specific host definitions for all targets
    +        CONFIG.UnixCommon.Common    Definitions for Unix hosts and all targets
    +        CONFIG.Common.UnixCommon    Definitions for Unix targets and all hosts
    +        CONFIG.Common.vxWorksCommon Specific host definitions for all vx targets
    +        CONFIG_COMPAT               R3.13 arch compatibility definitions
    +        CONFIG_SITE.<host>.<target> Site specific host-target definitions
    +        CONFIG_SITE.Common.<target> Site specific target defs for all hosts
    +        CONFIG_SITE.<host>.Common   Site specific host defs for all targets
    +
    + +

    base/src/tools directory - contains Perl scripts used for the build

    +
    +        Makefile                Makefile for installing the scripts into cfg dir
    +        convertRelease.pl       Performs consistancy checks on RELEASE files
    +        cvsclean.pl             Remove all .#* files in directory tree
    +        dos2unix.pl             Converts text file from DOS CR/LF to unix ISO
    +        expandvars.pl           Tool to expand @VAR@ variables while copying a file
    +        filterWarnings.pl       Filters warning messages during HP builds
    +        fullpathName.pl         Returns fullpath name of directory arg
    +        installEpics.pl         Installs built files into install directories
    +        makeIncludeDbd.pl       Creates *Include.dbd file from filename args files
    +        makeMakefile.pl         Creates a Makefile in O.<arch> dirs
    +        makeTestfile.pl         Generates a test harness $target.t file
    +        mkmf.pl                 Generates dependencies from include stmnts
    +        munch.pl                Creates a ctdt.c file for vxWorks targets
    +        replaceVAR.pl           Changes CapFast VAR(xxx) to $(xxx) notation
    +        useManifestTool.pl      Use MS VC++ version to set usage of Manifest Tool
    +
    +
    + +

    Building EPICS base (Unix and Win32)

    +
    + +

    Unpack file

    +
    +Unzip and untar the distribution file. Use WinZip on Windows + systems. +
    + +

    Set environment variables

    +
    +Files in the base/startup directory have been provided to + help set required path and other environment variables. + +

    EPICS_HOST_ARCH
    + Before you can build or use EPICS R3.14, the environment variable + EPICS_HOST_ARCH must be defined. A perl script EpicsHostArch.pl in the + base/startup directory has been provided to help set EPICS_HOST_ARCH. + You should have EPICS_HOST_ARCH set to your host operating system + followed by a dash and then your host architecture, e.g. solaris-sparc. + If you are not using the OS vendor's c/c++ compiler for host builds, + you will need another dash followed by the alternate compiler name + (e.g. "-gnu" for GNU c/c++ compilers on a solaris host or "-borland" + for Borland c/c++ compilers on a WIN32 host). See configure/CONFIG_SITE + for a list of supported EPICS_HOST_ARCH values.

    + +

    PERLLIB
    + On WIN32, some versions of Perl require that the environment + variable PERLLIB be set to <perl directory location>.

    + +

    PATH
    + As already mentioned, you must have the perl executable and you may + need C and C++ compilers in your search path. For building base you + also must have echo in your search path. For Unix host builds you also + need ln, cpp, cp, rm, mv, and mkdir in your search path and /bin/chmod + must exist. On some Unix systems you may also need ar and ranlib in + your path, and the C compiler may require as and ld in your path. On + solaris systems you need uname in your path.

    + +

    LD_LIBRARY_PATH
    + It is no longer necessary to have LD_LIBRARY_PATH include EPICS + directories on a Unix type system. R3.14 shared libraries and + executables will contain the full path name to libraries they require. + However, if you move the EPICS directories from their build-time + location then in order for libraries to be found at runtime + LD_LIBRARY_PATH must include the full pathname to + $(INSTALL_LOCATION)/lib/$(EPICS_HOST_ARCH) when invoking executables. + Building shared libraries is now the default setting for all Unix type + hosts. NOTE: You will still need LD_LIBRARY_PATH for R3.13 extension + shared libraries even if the R3.13 extensions are built with R3.14 + base.

    + +

    Win32 PATH
    + On WIN32 systems, for R3.14.7 and later, it is no longer necessary + to add fullpathname to $(INSTALL_LOCATION)/bin/$(EPICS_HOST_ARCH) to + your path for finding dlls during EPICS builds. The win32 configure + files in base now add this directory to the path definition.

    +
    + +

    Do site-specific build configuration

    +
    + +Site configuration
    + To configure EPICS, you may want to modify the default definitions + in the following files: +
    +        configure/CONFIG_SITE      Build choices. Specify target archs.
    +        configure/CONFIG_SITE_ENV  Environment variable defaults
    +        configure/RELEASE          TORNADO2 full path location
    +
    + + Host configuration
    + To configure each host system, you may override the default + definitions by adding a new file in the configure/os directory with + override definitions. The new file should have the same name as the + distribution file to be overridden except with CONFIG in the name + changed to CONFIG_SITE. + +
    +        configure/os/CONFIG.<host>.<host>      Host build settings
    +        configure/os/CONFIG.<host>.Common      Host common build settings
    +
    + +Target configuration
    + To configure each target system, you may override the default + definitions by adding a new file in the configure/os directory with + override definitions. The new file should have the same name as the + distribution file to be overridden except with CONFIG in the name + replaced by CONFIG_SITE. This step is necessary even if the host system + is the only target system. +
    +        configure/os/CONFIG.Common.<target>     Target common settings
    +        configure/os/CONFIG.<host>.<target>     Host-target settings
    +
    + +R3.13 compatibility configuration
    + To configure EPICS base for building with R3.13 extensions and ioc + applications , you must modify the default definitions in the + base/config/CONFIG_SITE* files to agree with definitions you made in + base/configure and base/configure/os files. +
    + +

    Build EPICS base

    +
    After configuring the build you should be able to build + EPICS base by issuing the following commands in the distribution's root + directory (base): +
    +        gnumake clean uninstall
    +        gnumake
    +
    + + The command "gnumake clean uninstall" + will remove all files and directories generated by a previous build. + The command "gnumake" will build and install everything for the + configured host and targets. + +

    It is recommended that you do a "gnumake clean uninstall" at the + root directory of an EPICS directory structure before each complete + rebuild to ensure that all components will be rebuilt. +

    +
    + +

    Example application and extension

    +
    A perl tool, makeBaseApp.pl is included in the distribution + file. This script will create a sample application that can be built + and then executed to try out this release of base. + +

    + Instructions for building and executing the 3.14 example application + can be found in the section "Example Application" of Chapter 2, + "Getting Started", in the "IOC Application Developer's Guide" for this + release. The "Example IOC Application" section briefly explains how to + create and build an example application in a user created <top> + directory. It also explains how to run the example application on a + vxWorks ioc or as a process on the host system. + By running the example application as a host-based IOC, you will be + able to quickly implement a complete EPICS system and be able to run channel + access clients on the host system. + +

    + A perl script, + makeBaseExt.pl, is included in the distribution file. This script will + create a sample extension that can be built and executed. The + makeBaseApp.pl and makeBaseExt.pl scripts are installed into the + install location bin/<hostarch> directory during the base build. +

    + +

    Multiple host platforms

    +
    You can build using a single EPICS directory structure on + multiple host systems and for multiple cross target systems. The + intermediate and binary files generated by the build will be created in + separate subdirectories and installed into the appropriate separate + host/target install directories. EPICS executables and perl scripts are + installed into the $(INSTALL_LOCATION)/bin/<arch> directories. + Libraries are installed into $(INSTALL_LOCATION)/lib/<arch>. + The default definition for $(INSTALL_LOCATION) is $(TOP) + which is the root directory in the distribution directory structure, + base. Created object files are stored in O.<arch> source + subdirectories, This allows objects for multiple cross target + architectures to be maintained at the same time. To build EPICS base + for a specific host/target combination you must have the proper + host/target C/C++ cross compiler and target header files and the + base/configure/os directory must have the appropriate configure files. +
    + + diff --git a/documentation/README.niCpu030 b/documentation/README.niCpu030 new file mode 100644 index 000000000..0bd292bd8 --- /dev/null +++ b/documentation/README.niCpu030 @@ -0,0 +1,34 @@ + +The CPU030 may need to have the nivxi path set correctly: + From the vxWorks shell type "vxitedit" (you may need to + loading the NI vxitedit package first) + take option 2 + take option 3 + type list + type modify 0 + type in the correct path when promped + (the path should end in nivxi + and should traverse the niCpu030 + directories shipped with the 030 + ie something of the form "???/config/niCPU030/nivxi" + type save + type exit + . + . + . + + see "Getting Started with Your VXIcpu-030" from NI + +You may may need to setup front panel to backplane trigger +routing: + +To take a TTL input and map it to VXI backplane ECL trigger 0 +type in (to the vxWorks shell): + +epvxiRouteTriggerECL(, 1, 0) + +where specifies the card with the +front panel trigger connection. + +johill@lanl.gov + diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html new file mode 100644 index 000000000..d1f3eae29 --- /dev/null +++ b/documentation/RELEASE_NOTES.html @@ -0,0 +1,3384 @@ + + + + + EPICS Base R3.14.12 Release Notes + + + +

    EPICS Base Release 3.14.12

    + + +

    Changes between 3.14.11 and 3.14.12

    + + +

    Launchpad Bugs Resolved

    + +

    The following are links to bugs in the Launchpad bug tracker that have been +fixed in this release:

    + + + +

    Support for Cygwin 1.7

    + +

    Base can now built with shared libraries on Cygwin 1.7.x, although only with +the gcc-3 compiler. Cygwin 1.5.x only works when compiled without shared +libraries (set STATIC_BUILD=YES and SHARED_LIBRARIES=NO in +configure/CONFIG_SITE). There is code in Base now that depends on the +Cygwin version, so make sure that you rebuild Base if you upgrade your Cygwin +installation.

    + +

    epicsEvent

    + +

    epicsEvent is now explicitly required to act as a simple binary semaphore. +A single epicsEventSignal call must awaken just one thread when multiple +threads are waiting for the event. A test that checks for this has been +added to epicsEventTest and the Application Developer's Guide has been updated +to describe this requirement.

    + +

    Enable array puts to subArray records

    + +

    It is now possible to put an array into the VAL field of a soft channel +subArray record whose INP field is empty. Processing the record then causes the +sub-array extraction process to be done on the current VAL array. This can be +used to feed a sequence of values into some other record (set INDX=1 and +NELM=MALM and read one element out of the VAL field each time).

    + +

    Added windows-x64 target

    + +

    64-bit binaries for Microsoft Windows platforms can now be built using the +target architecture windows-x64, which is also a valid cross-build target for a +win32-x86 host. We do not currently support or recommend trying to use 64-bit +builds on MinGW or Cygwin.

    + +

    Deleted osf-alpha targets

    + +

    Kazuro Furukawa has been unable to compile EPICS Base on this target for the +last few releases and it's not really needed any more, so the targets have been +deleted from this release.

    + +

    Expanded CALC expressions

    + +

    The calc and calcout records can now accept infix expressions up to 79 +characters long, although you have to use long string support to read or write +any that are more than 39 characters. The postfix interpreter now sports an +integer literal token as well as the double literal one, which reduces the +maximum size of a postfix buffer needed for any specific size of infix buffer by +a factor of 2/3.

    + +

    Added Apple iOS target architectures

    + +

    Tom Pelaia II and Mark Engbretson contributed build configuration files to +cross-compile Base for Apple's iOS operating system as used in the iPhone and +iPad. The target architecture for these is ios-arm, and there is also an ios-x86 +target which compiles for the iPhone Simulator running on darwin-x86.

    + +

    To build these for iOS, uncomment the CROSS_COMPILER_TARGET_ARCHS +definition in configure/os/CONFIG_SITE.darwin-x86.Common and check the +settings in configure/os/CONFIG_SITE.Common.iosCommon.

    + +

    Dynamic arrays over CA

    + +

    Dynamic array sizing was developed by Michael Abbott at the 2010 EPICS +Codeathon. It permits a CA client to fetch only the currently valid elements of +an array by specifying a COUNT of zero to either of the ca_array_get_callback() +or ca_create_subscription() routines. It has never before been legal to pass a +COUNT of zero to the ca_array_get_callback() routine, but this development does +introduce a subtle change to the published API of the ca_create_subscription() +routine.

    + +

    In previous releases a COUNT of zero for a subscription meant use the +ca_element_count() for the channel, but from this release it can return fewer +elements (never more), at the behest of the server. The number of elements can +vary with subsequent array update events, so a client that uses this technique +must use the count field of the event_handler_args in its callback function each +time it is called to obtain the correct element count from the server. Note that +the ca_element_count() value for a channel is only updated at connection time, +and supplies the maximum number of elements that the server array variable can +hold.

    + +

    Dynamic arrays are currently only supported by the CA client library and IOC +server RSRV, the Perl CA library and the catools programs. The portable CAS does +not understand them, and database links that connect over CA do not attempt to +use them either. CA clients that try to use this functionality with a server +that does not support it will receive the same full-sized zero-filled arrays +that previous releases supported.

    + +

    CA over TCP connections

    + +

    Merged the CA-over-TCP changes developed by Ralph and Jeff at the 2008 EPICS +Codeathon for the CA client library and both servers (CAS and RSRV). This adds +a new CA configuration variable CA_NAME_SERVERS which is used by the +client library as a list of host names or addresses of CA servers to be +connected to and queried over TCP sockets. This can be used to tunnel CA +connections over ssh. See the CA reference manual for more details.

    + +

    Record Types aai and aao

    + +
      +
    • Fixed bug in memory allocation that caused crashes when linking other +records to aai or aao.
    • +
    • Fixed bug where NELM was modified instead of NORD.
    • +
    • Added Soft Channel device support.
    • +
    • Added SIOL link and proper simulation mode for Soft Channel support.
    • +
    • Added MPST, APST and HASH fields for monitor on change support like in +waveform record.
    • +
    + +

    New capr.pl script

    + +

    This Perl program attempts to replicate the behaviour of the IOC command +'dbpr' over Channel Access, printing the fields and values of the named record. +Run capr.pl -h for usage information.

    + +

    devLib cleanup

    + +
      +
    • Add VME connect/disconnect IRQ calls to the "virtual os" table
    • +
    • It is now possible to compile all devLib code on targets without runtime +support
    • +
    • All internal functions are made static. Some were not before.
    • +
    • Move VME calls to devLibVME.h. devLib.h contains general +defintions and error codes.
    • +
    • For compatibility devLib.h includes devLibVME.h unless the +macro NO_DEVLIB_COMPAT is defined.
    • +
    • The "virtual os" table was renamed from pdevLibVirtualOS to +pdevLibVME reflecting the fact that other bus types will need seperate +tables.
    • +
    • The "virtual os" table API has been moved to a different header file, +devLibVMEImpl.h.
    • +
    + +

    DTYP and INP/OUT order

    + +

    The fields DTYP and INP/OUT can now be specified in any order in a database +instance (.db) file.

    + +

    Rewrite epicsThreadOnce()

    + +

    Michael Davidsaver suggested a better implementation of epicsThreadOnce() +which makes it possible for all architectures to detect recursive initialization +functions. This change also renames the routines to remove the original macro, +and fixes some questionable usage in places.

    + +

    Many Record Types

    + +

    Record types were not setting many 'last value' fields at initialization, +assuming that the VAL field would always hold the default value and thus not +giving correct initial monitor behavior in some circumstances.

    + +

    Compress Record Type

    + +

    Fixed a crash when ALG (algorithm) was changed to Average at runtime.

    + +

    configure/RELEASE Enhancements

    + +

    Variable definitions in a configure/RELEASE file may now use the +:= or ?= assignment syntax. Release file processing now +prints a warning if an include file can't be not found (use -include if +this is expected) or if an undefined macro is used, and aborts if a circular +definition is expanded. The variable EPICS_HOST_ARCH can now be used, +but target-architecture-specific values must be set in a target-specific +configure/RELEASE file such as RELEASE.Common.linux-x86.

    + +

    VxWorks 6.7 and 6.8 support

    + +

    Added build support for the latest Wind River releases. This does not +include the ability to run on SMP vxWorks systems though, those will need some +more extensive modifications.

    + +

    RTEMS 4.10 support

    + +

    Added support for the latest release of RTEMS. There is no longer a +distinction between executive and malloc memory pools. The new mount() API +is used.

    + +

    Dependency file generation

    + +

    Changes to the way in which we generate header dependency files, which are +now named with a .d suffix instead of .depends. We now use +the compiler to generate these where possible, and create them in the +O.$(T_A) directory.

    + +

    Installed file permissions

    + +

    We now install libraries and other non-executable files with mode 444 rather +than 644.

    + +

    CA Perl5: strings and array subscriptions

    + +

    In some circumstances a string received from CA might not have been properly +zero-terminated; this has now been fixed. CA::create_subscription() +would not accept a zero for the subscription array length; this is now +permitted, in the underlying CA API this means use the native length from the +server.

    + +

    Linking Readline on Linux

    + +

    Different Linux distributions have different requirements when linking +readline; some need -lcurses, others -lncurses and others +don't require either. The configure/os/CONFIG_SITE.Common.linux-* +files now make this configuration easy to choose between different settings for +the COMMANDLINE_LIBRARY variable. If your build fails as it's trying +to create the antelope binary in the Base directory +src/toolsComm/antelope/O.arch this is almost certainly the +problem. You will need to do a make rebuild from the top of Base after +fixing the setting of COMMANDLINE_LIBRARY.

    + +

    CA Command Line Tools Changes

    + +

    Three new options were added to the caget and camonitor +utilities, to allow printing float values (rounded) as hex, octal or binary.

    + + +

    Changes between 3.14.10 and 3.14.11

    + +

    Time provider on Win32

    + +

    A race condition meant that sometimes EPICS programs (including the internal +programs such as flex and antelope) built for Win32 architectures would not +start properly on Windows systems that have multiple CPUs. This race has been +fixed.

    + +

    Build system dependency change

    + +

    In order to get GNU make parallel execution (-j option) to work proprely for +multiple target architectures, a new dependency had to be added. Now all +cross-compiled builds depend on their host build. This means that when a +make crosstargetarch command is issued, the EPICS_HOST_ARCH +target build will be executed first, followed by the crosstargetarch +build. Builds done in an O.arch directory will still only build the +arch targets however.

    + +

    Channel Access changes

    + +

    Mantis 361 fix - ca_add_fd_registration users might not receive select +wakeup

    + +

    Mantis 359 fix - ca client library flow control mode related issues

    + +

    Mantis 357 fix - high throughput multithreaded ca client appl thread could +be trapped in lib.

    + +
      +
    • Discovered during code review. Not seen in practice, but possible
    • +
    + +

    Mantis 285 fix - CA Documentation doesn't distinguish sync groups from +ca_put_callback()

    + +

    Mantis 346 fix - deleting the chid passed in from within put cb handler +causes failure

    + +

    Mantis 284 fix - channel clear protocol warning messages from PCAS

    + +

    Mantis 237 fix - SEGV from simple CA client during context destroy

    + +

    Mantis 242 fix - invalid DBR type not screened in client library when +doing a put

    + +

    Portable Channel Access Server changes

    + +

    These changes impact the Gateway (Proxy server) and other servers but not +the IOC. + +

    Mantis 360 fix - server is unresponsive for one of its clients, when +async io postponed and in flow control mode

    + +

    Mantis 358 fix - PCAS service snap-in has no way to determine if its a put, +or a put calback.

    + +

    Mantis 356 fix - medm display sometimes hangs until the motor stops when +controling motor through gw.

    + +

    Mantis 350 fix - Incoming data corruption under heavy CAS load.

    + +

    Mantis 340 fix - leak when performing a read and conversion fails.

    + +

    Mantis 348 fix - A call to 'assert (item.pList == this)' +failed in ../../../../src/cas/generic/st/ioBlocked.cc line 112

    + +

    Mantis 345 fix - Compilation warning: complaint about missing +gddDestructor

    + +

    Mantis 343 fix - gddScalar::new() operator is not fully thread safe

    + +

    Mantis 333 fix - resTable::removeAll() does not reset the item count

    + +

    Mantis 335 fix - excas fails in clearOutstandingReads - maybe requires an +R3.13 client

    + +

    Mantis 329 fix - GW hang, pthread_mutex_lock failed: error Invalid +argument message

    + +

    Mantis 352 fix - gateway hangs temporarily under heavy load on 8-core +64bit RHEL5

    + +
      +
    • High throughput performance appears to be much better now for both scalars +and large arrays, but more testing needed in operational gateways
    • +
    + +

    Timer Queue Library

    + +

    Mantis 336 fix - timer queue should have try / catch block around call to +user's expiration callback

    + +

    Mantis 332 fix - epicsTimerTest failure, windows vista 64, dual core +SMP system

    + +

    LibCom

    + +

    Mantis 328 fixed - orderly shutdown for soft IOC fails

    + +

    Application configure files

    + +

    The configuration directory files installed by makeBaseApp.pl have been +changing in recent releases to make them work more like the files in the Base +configuration directory. The CONFIG_APP file has gone, and its functionality is +now performed by the CONFIG file which should only be modified in exceptional +circumstances. The variables that used to be set in the CONFIG file now appear +in the new CONFIG_SITE file, and can be overridden for specific combinations of +host and target architectures by creating a file with name matching one of these +patterns:

    + +
      +
    • CONFIG_SITE.<host-arch>.Common
    • +
    • CONFIG_SITE.Common.<target-arch>
    • +
    • CONFIG_SITE.<host-arch>.<target-arch>
    • +
    + +

    Note that the setting for CHECK_RELEASE in the CONFIG_SITE files is +not compatible with previous releases of Base; if you are creating an +application that has to work with earlier releases, move the +CHECK_RELEASE setting back to the configure/Makefile where it used to +live.

    + +

    The RELEASE file(s) can now define the variable RULES if you wish +the application to use build rules from some module other than EPICS_BASE. The +rules must appear in a configure subdirectory just like they do in Base.

    + +

    Compile-time assertions

    + +

    A new macro has been added to epicsAssert.h which performs assertion checks +at compile-time. STATIC_ASSERT(expr) can only be used when +expr can be evaluated by the compiler, and will cause the +compilation to fail if it evaluates to false. The resulting compiler error +message might appear a little obscure, but it does provide some explanation and +contains the line where the failure was discovered. Future versions of the C++ +standard will probably contain a similar facility static_assert(expr, +message) but will require compiler support to be implemented.

    + +

    Several changes made to dbDefs.h

    + +

    The definitions for the macros YES, NO, NONE, +min() and max() have been deleted. YES and +NO have been replaced by menuYesNoYES and menuYesNoNO +from the menuYesNo.h file where they were used in several record types. The +other macros were not being used anywhere in Base, sncseq or Asyn.

    + +

    The macro LOCAL that was a synonym for static is now +deprecated and will be deleted in R3.15, please adjust your code to use the +latter keyword. All uses of the READONLY macro from shareLib.h must +now be replaced by the keyword const as the macro has been deleted.

    + +

    The dbDefs.h file was unnecessarily including various other libCom header +files which may have to be manually added to out-of-tree source files that +relied on this. The general rule for header files is that a header should only +include other headers that are needed for its own inclusion in any source file. +The #include statements that might need to be added are:

    + +
      +
    • #include <stdarg.h>
    • +
    • #include "errlog.h"
    • +
    • #include "errMdef.h"
    • +
    • #include "ellLib.h"
    • +
    • #include "epicsTypes.h"
    • +
    + +

    A new macro CONTAINER(pointer, type, member) has been added which +calculates and returns a pointer to the parent structure when given a pointer to +a member, the structure type and the name of the member in that structure. On +GNU compilers the type of the pointer is checked to ensure that it matches the +member, so code using this macro should be built using gcc for additional +confidence.

    + +

    Long-deprecated errSymFind() function deleted

    + +

    This functionality was replaced by errSymLookup() many releases ago.

    + +

    Perl CA library shutdown

    + +

    The Perl CA library has been modified to properly flush the Channel Access +I/O queues when a program using the library exits.

    + +

    bi, bo, mbbi, mbbo ENUM string fields

    + +

    The existing Channel Access protocol transports 26 bytes for each ENUM +string, but the fields used to hold ENUM strings in the discrete record types +were configured to be 16 (mbbi/mbbo) or 20 (bi/bo) bytes long. These have all +been increased to match the CA limit.

    + +

    Use of SIGALRM disabled

    + +

    Previous releases that supported HPUX required that EPICS trap and be able to +generate the SIGALRM signal in order to break out of a blocking socket system +call on those systems (Posix only). There were two API routines that had to be +called by any code using sockets to properly support this. Since we no longer +support HPUX this code is no longer required, and as it breaks the Posix timer +interface on Linux it has been disabled in this release. The API routines now do +nothing on all platforms, and will be removed before the 3.15 Release of +Base.

    + +

    Universal Binaries on Darwin

    + +

    It is now possible to configure Base on Darwin to build universal binaries +containing an installation-specific choice of 32- and 64-bit CPU architectures. +This is controlled through the definition of the ARCH_CLASS variable in the +configure/os/CONFIG_SITE.Common.<arch> file for the particular target +architecture you are using (darwin-ppc, darwin-x86 or darwin-ppcx86). There are +comments and example settings in those files. Note that to my knowledge EPICS +Base has not been thoroughly tested on the 64-bit darwin architectures.

    + +

    HPUX, SGI and VMS files deleted

    + +

    We have not supported or tested EPICS Base on HPUX for several releases; the +support files for this and other unsupported operating systems have been +removed.

    + +

    PINI Processing and PHAS values

    + +

    The PINI field now has its own menu type with four more choices, +RUN, RUNNING, PAUSE and PAUSED. The earlier +NO and YES values for this field remain as before and cause +the same processing as before. The new values cause records to be processed +during the announcement of the new initHookAtIocRun, +initHookAfterIocRunning, initHookAtIocPause and +initHookAfterIocPaused states respectively.

    + +

    Records with PINI=YES will be processed during the iocBuild +stage of iocInit, follwed by records with PINI=RUN during the +iocRun stage and records with PINI=RUNNING during the +initHookAfterIocRunning stage. If an iocPause command is +given, any records with PINI=PAUSE will be processed at the +initHookAtIocPause stage followed by records with PINI=PAUSED +will be processed at the initHookAfterIocPaused stage. A subsequent +iocRun command causes the PINI=RUN and PINI=RUNNING +records to be processed again.

    + +

    The PHAS field now controls the order in which records are processed as a +result of the PINI mechanism; within each stage, lower values of PHAS are +scanned before higher ones.

    + +

    Additional initHook states

    + +

    Several new initHook states have been added which are announced while the IOC +is being paused or restarted. The original states remain unchanged, although a +couple of the later ones are deprecated where using the new states makes more +sense. The enum that defines all the states is now:

    + +
    +
    typedef enum {
    +    initHookAtIocBuild = 0,         /* Start of iocBuild/iocInit commands */
    +    initHookAtBeginning,
    +    initHookAfterCallbackInit,
    +    initHookAfterCaLinkInit,
    +    initHookAfterInitDrvSup,
    +    initHookAfterInitRecSup,
    +    initHookAfterInitDevSup,
    +    initHookAfterInitDatabase,
    +    initHookAfterFinishDevSup,
    +    initHookAfterScanInit,
    +    initHookAfterInitialProcess,
    +    initHookAfterCaServerInit,
    +    initHookAfterIocBuilt,          /* End of iocBuild command */
    +
    +    initHookAtIocRun,               /* Start of iocRun command */
    +    initHookAfterDatabaseRunning,
    +    initHookAfterCaServerRunning,
    +    initHookAfterIocRunning,        /* End of iocRun/iocInit commands */
    +
    +    initHookAtIocPause,             /* Start of iocPause command */
    +    initHookAfterCaServerPaused,
    +    initHookAfterDatabasePaused,
    +    initHookAfterIocPaused,         /* End of iocPause command */
    +
    +/* Deprecated states, provided for backwards compatibility.
    + * These states are announced at the same point they were before,
    + * but will not be repeated if the IOC gets paused and restarted.
    + */
    +    initHookAfterInterruptAccept,   /* After initHookAfterDatabaseRunning */
    +    initHookAtEnd,                  /* Before initHookAfterIocRunning */
    +} initHookState;
    +
    + +

    The two deprecated states will only ever be announced once, whereas the newer +ones will repeat as often as the iocPause and iocRun commands +are executed.

    + +

    Soft Channel ai device support and SMOO

    + +

    The (probably unwritten) rules for ai device support have always required +that any device support which sets the VAL field and returns "2" should also +perform any other operations normally performed by the ai's convert() routine. +In practice very few devices did this, but there are some which makes it +impossible to move that smoothing function into the body of the record. This +change adds the support for SMOO filtering to the soft channel device support +for the ai record. The filter is short-circuited when a link recovers from a +failure.

    + +

    New link flags for alarm severity/status inheritance

    + +

    Two new options have been introduced as alternatives to the existing NMS and +MS flags: MSI (Maximize Severity INVALID) and MSS (Maximize Severity and +Status).

    + +

    When a link is flagged MSI the receiving record inherits the target's alarm +severity just as it does with the MS flag, but only if that severity is +INVALID.

    + +

    When a link is flagged MSS the receiving record inherits the target's alarm +status as well as its severity, thereby preserving the alarm status through MSS +chains instead of setting that status to LINK.

    + +

    Analog records get_alarm_double() semantics changed

    + +

    The get_alarm_double() routine in several record types has been changed to +make it easier for CA client applications to display the correct warning and +alarm limits for a record.

    + +

    Originally these routines would always return the current values from the +associated alarm limit fields, but now they will return epicsNAN (not-a-number) +instead if the severity field of the corresponding limit is NO_ALARM. This +allows CA clients to suppress the display of unused limits.

    + +

    New math constants epicsNAN and epicsINF

    + +

    Two new math constants have been added to epicsMath.h: epicsNAN +(not a number) and epicsINF (infinity). + +

    New event type DBE_PROPERTY

    + +

    A new event type (flag in the Channel Access event mask) has been added +to support subscriptions that get events whenever a property of the PV +changes. This will allow clients to get notified on changes of control +limits, graphical limits, state strings etc.

    + +

    The CA commandline tool camonitor and the CA Perl interface support the new +event type. As a first working example, the mbbi and mbbo records have been +extended to send a DBE_PROPERTY event when their status strings are modified. +A more general mechanism to specify sending DBE_PROPERTY events through the DBD +file will appear in 3.15.

    + +

    Client application developers are encouraged to start using DBE_PROPERTY +subscriptions to fetch channel attribute data.

    + +

    Channel Access command line tool changes

    + +

    The caget/caput/camonitor programs in src/catools now use '\' escape +sequences for non-printable characters.

    + +

    They provide a new option -F to set an output field separator +to be used instead of the default space character.

    + +

    New functions for escaping non-printables in epicsString.h

    + +

    The existing routines used to escape non-printable characters have been +replaced by a new set of functions that are prototyped in the epicsString.h +header file:

    + +
    int epicsStrnRawFromEscaped(char *outbuf, size_t outsize, const char *inbuf, size_t inlen);
    +epicsShareFunc int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, const char *inbuf, size_t inlen);
    +epicsShareFunc size_t epicsStrnEscapedFromRawSize(const char *inbuf, size_t inlen);
    + +

    Both conversion functions take the output buffer (and its size), and the +input buffer (and its size) as argument. They will convert non-printable +characters from/to their '\'-escaped versions. The third function scans a raw +input string and returns the number of characters needed for the escaped +version.

    + +

    The existing function interfaces will be kept for compatibility, but their +further use is deprecated.

    + +

    epicsRingBytes

    + +

    Partial puts are not supported. +An attempt to put more bytes than currently free will be rejected.

    + +

    Long string support

    + +

    The IOC now provides support for strings longer than 40 characters through +Channel Access in a manner that is fully backwards compatible with older +versions of the CA library, although not all CA clients can make use of this at +the moment.

    + +

    Adding the suffix '$' to the field name of an IOC PV name (through either +Channel Access or Database Access) causes the native type of that field to be +reported as an array of DBF_CHAR, as long as the field type is DBF_STRING, +DBF_INLINK, DBF_OUTLINK or DBF_FWDLINK. It is then possible to use a DBF_CHAR +array to read, write and monitor values from that field, and the 40 character +string length limit imposed by the DBF_STRING type is replaced by the amount of +storage allocated for the string on the IOC (for link fields the limit is +related to the maximum length of a record name).

    + +

    The caget/caput/camonitor programs in src/catools can now use long strings by +adding the command-line option -S which causes them to handle an array +of DBF_CHAR as a string. Both MEDM and EDM can already present such DBF_CHAR +arrays as strings in their text widgets (although you do have to configure the +widget to format it as text), so this solution already works with some CA +clients, although by no means all clients support it yet.

    + +

    Darwin no longer uses _environ

    + +

    The Darwin version of epicsEnvShow (in src/libCom/osi/os/Darwin/osdEnv.c) +now uses _NSGetEnviron() to get the pointer to the environment string +table.

    + +

    gpHash argument type changed

    + +

    Out-of-tree users of libCom's gpHash routines should change the type of their +pointer to their gpHash table to avoid compiler warnings. The first argument to +all of the gph...() routines used to be a void * (or a +pointer to one for gphInit()) but is now a struct +gphPvt * (struct gphPvt ** for gphInit) for +better type safety. The definition of the structure has not been made public, +although a declaration is provided in gpHash.h.

    + +

    New hash functions in epicsString.h

    + +

    The existing routines used to hash strings have been replaced by a new +pair of functions that are prototyped in the epicsString.h header file:

    + +
    unsigned int epicsStrHash(const char *str, unsigned int seed);
    +unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed);
    + +

    The seed argument should normally be zero, but can be used to chain +several hash calculations together to create a single value from multiple +strings or memory buffers. The resulting value should be masked with the +appropriate number of bits for the desired hash width. These functions both +use the same algorithm, and on most CPUs should be faster that the previous +hash functions used in Base. Use epicsStrHash() on nil-terminated strings, +epicsMemHash() if the data might contain zero bytes.

    + +

    Support for dynamic loading

    + +

    The existing OSI epicsFindSymbol API has been expanded to support dynamic +loading of binary files, on architectures where the facilities necessary to +implement this are provided (currently Linux, Solaris, Darwin and vxWorks). A +new but optional command is provided for iocsh which calls the new +epicsLoadLibrary() function. In order to include this command in an +IOC, you must include the dlload.dbd file; the command will then be +installed when the IOC's ioc_registerRecordDeviceDriver routine is +run.

    + +

    To dynamically load a new routine for use with the sub or aSub record +types, you also have to register the subroutines at runtime. You can use the +EPICS build system and the registerRecordDeviceDriver.pl script to write the +necessary code for you, you just have to create a .dbd file that declares the +functions in the library and add the derived .cpp file to the library +sources. The product of your build should be a LOADABLE_LIBRARY to +ensure that the correct linker commands are used.

    + +

    After a shared library is loaded into the IOC using the new iocsh +dlload command you will usually have to run the +lib_registerRecordDeviceDriver routine to register the components +that were mentioned in the .dbd file. After registration the functions can be +found as normal by setting the SNAM field of your sub or aSub record +instances. Once loaded, shared libraries cannot be unloaded again as there is +no way to be sure that some other part of the IOC doesn't still have a +pointer to something inside the module.

    + +

    Components provided by a shared library can include functions for sub and +aSub records, iocsh commands, time providers and sequence programs. Record +and device support code may be possible, but there are complications in +creating the .dbd file so we don't recommend it yet. It is possible to load +libraries after iocInit, but the code is not re-entrant so should only be +used from the main thread. Adding new record types or device support will +not work after iocInit.

    + +

    Perl5 CA library

    + +

    The CA::put and CA::put_callback methods now use the +native type of the PV to determine which data type to use in all cases; +earlier versions looked at the Perl data type for single-valued PVs, but this +was truncating some double values into integers.

    + +

    generalTime and epicsTime updates

    + +

    Allow time providers to supply timestamps in interrupt context. A pair of +new API routines in epicsTime.h epicsTimeGetCurrentInt() and +epicsTimeGetEventInt() will check the most recently successful +current-time or event-time provider and forward requests to the routine +registered by that provider, if any. The priority list is not traversed, so +if the latest provider has not registered an interrupt-safe routine by +calling generalTimeAddIntCurrentProvider() or +generalTimeAddIntEventProvider() as appropriate, these API routines +will return failure. The resulting timestamp is not protected against +backwards movement either.

    + +

    Added a routine generalTimeHighestCurrentName() which returns the +name of the highest prority registered current time provider. Comparing this +with the name returned by generalTimeCurrentProviderName() permits +an alarm to be generated if an IOC is not able to use the time provider it +was designed to use. The General Time stringin device support keyword +@TOPTCP has been added to make this name available in a database +record.

    + +

    The following routines have been renamed, but the old name may still be +used since a macro with the old name has been added:

    + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    Old NameNew Name
    generalTimeCurrentTpRegistergeneralTimeRegisterCurrentProvider
    generalTimeEventTpRegistergeneralTimeRegisterEventProvider
    generalTimeCurrentTpNamegeneralTimeCurrentProviderName
    generalTimeEventTpNamegeneralTimeEventProviderName
    +
    + +

    The routine generalTimeCreateSyncTimer() was not useful in +practice due to time provider initialization issues, nor was its +implementation protected properly for use on SMP systems so it has been +deleted. The deprecated and empty synchronize() method from the +epicsTime class has also gone.

    + +

    RTEMS Release

    + +

    RTEMS release 4.9.2 or newer is required. EPICS now configures RTEMS to use +unified executive/malloc memory pools if available (RTEMS 4.10 and up).

    +

    RTEMS

    + +

    RTEMS stack sizes have been reduced. This allows more clients in machines +with limited memory.

    + +

    RTEMS file descriptor allocation has been increased.

    + +

    Added record aliases

    + +

    Record definitions can declare their own alias names along with the field +values, or a separate alias statement can also be used:

    + +
    +
    record(ai,"canonicalName")
    +{
    +   alias("firstAlias")
    +}
    +alias("canonicalName","secondAlias")
    +
    + +

    Aliases can only be created for records that have already been loaded by +the IOC, although they don't have to appear in the same .db file. A CA client +can discover that a record name is an alias by fetching its .NAME field, +which will always return the canonical name for the record. These new +routines in the dbStaticLib library handle aliases:

    + +
    +
    long dbCreateAlias(DBENTRY *pdbentry, const char *paliasName);
    +int dbIsAlias(DBENTRY *pdbentry);
    +int dbGetNAliases(DBENTRY *pdbentry)
    +long dbDeleteAliases(DBENTRY *pdbentry);
    +
    + +

    Aliases are located using the existing record instance API. Use +dbIsAlias() to test whether a record is actually an alias or not. +Aliasees can be deleted like record instances using dbDeleteRecord() +which will not delete the underlying record if the DBENTRY refers to the +alias (deleting a record instance does delete all its aliases though). The +dbDeleteAliases() routine deletes all aliases of the selected +record, and will return an error if used on an alias.

    + +

    Any out-of-tree tools that scan through all the records in the database +using dbFirstRecord() and dbNextRecord() may need modifying +to avoid duplicate processing of aliased record instances. Also note that +the set of info items for a record instance are not shared with its aliases, +which each have their own info item namespace.

    + +

    Added support for RTEMS-mvme2700

    + +

    Supplied by Matt Rippa <mrippa@gemini.edu>. Should work for +MVME2400, too.

    + +

    CA Commandline tools: priority support

    + +

    A new option -p was added to all Channel Access command line +tools to allow specifying the CA priority.

    + +

    Linux: Use libncurses

    + +

    Changed linux builds to link against libncurses instead of libcurses +(suggested by Peter Zumbruch).

    + + +

    Changes between 3.14.9 and 3.14.10

    + +

    GCC_EXEC_PREFIX references removed from configuration files

    + +

    Definition of GCC_EXEC_PREFIX removed from CONFIG.CrossCommon and +unexport of GCC_EXEC_PREFIX removed from vxWorks and RTEMS builds.

    + +

    RTEMS Release

    + +

    RTEMS release 4.9 or newer is required. If you are using the RTEMS NFS +time provider you need to use RTEMS 4.9.1 or newer.

    + +

    RTEMS epicsEventWaitWithTimeout

    + +

    Correctly return epicsEventWaitTimeout when event is not pending and +timeout value is 0.0 seconds.

    + +

    epicsRingPointer, epicsRingBytes

    + +

    Fixed a race condition exposed by compilers with more agressive +optimization.

    + +

    camonitor timestamp support

    + +

    The camonitor program now supports the ability to display both +server- and client-side timestamps, simultaneously if requested. The old +command-line options -r -i and -I that controlled +the timestamp display have been replaced with a new -t option that +takes additional argument letters to control which timestamp sources and +output format should be used.

    + +

    See the Command Line Tools section of the Channel Access reference manual +or run camonitor -h for a summary of the options.

    + +

    New "stdio" stringout device support

    + +

    A new device support has been added that allows a stringout record to +output one-line messages to stdout, stderr or the errlog subsystem. Use +DTYP="stdio" and set the OUT field to one of "@stdout", "@stderr" or +"@errlog" to control the message destination. A newline is appended to the +contents of the record's VAL field when printing.

    + +

    General Time subsystem

    + +

    The way in which EPICS gets the time has been significantly revised since +R3.14.9 with the introduction of the General Time subsystem. Two kinds of +pluggable time providers are now supported, which return either the current +time or the latest timestamp of a numbered Time Event from a hardware event +system. All IOCs must have at least one Current Time provider, but Event Time +providers are optional. The General Time subsystem guarantees that the +timestamps returned never step backwards, even when switching between time +providers.

    + +

    A Current Time Provider reports the current wall-clock time if it is able +to when asked; if it can't it says so, and the time subsystem will then ask +the next provider in its list and so on until someone replies with the time. +Event Time providers are handled similarly, except that they are asked for +the timestamp associated with a particular Time Event number rather than the +current wall-clock time. The registered time providers can be listed using +the IOC command generalTimeReport(int interest) and some time +providers also have their own separate report commands.

    + +

    Different target architectures come with different default Current Time +providers in Base. The Unix-like architectures rely on the underlying +operating system clock, which normally involves running something like ntpd +as a separate process. The real-time operating systems vxWorks and RTEMS +install two Current Time providers; the native operating system clock at the +lowest priority (this provider includes a task to periodically reset the +operating system clock from a higher priority time provider), and an NTP time +provider which synchronizes the underlying operating sytem tick timer with an +NTP server. Microsoft Windows targets using the Win32 API use a +Windows-specific time provider which contains a built-in PLL.

    + +

    There are no Event Time providers included with Base except for an +optional "Last Resort" Event provider which can be installed if a site wants +to be sure that every Time Event number will have a recent timestamp +associated with it even if the hardware event system goes down. The Last +Resort Event Time provider returns the current time for every Time Event +number. To install this provider, run the command +installLastResortEventProvider from your IOC startup script.

    + +

    Additional information about General Time and the time providers included +with Base can be found in the IOC Application Developers Guide for R3.14.10, +sections 9.7 and 20.6. General Time was originally written by David H. +Thompson and Sheng Peng at ORNL, subsequently worked on by Babak Kalantari +and Timo Korhonen of PSI, and merged into Base by Peter Denison from Diamond. +Andrew Johnson provided input into the design at various points and made +various code revisions after the merge.

    + +

    New ioc/dllPath.bat file for Win32

    + +

    When creating an IOC with one of the win32-x86 target architectures, a +file dllPath.bat is now generated in the iocBoot/iocxxx directory +which can be run to adjust the PATH variable to include all of the support +application bin directories which might contain DLLs that the IOC uses. The +PATH also includes the IOC application's own bin directory, which simplifies +starting up the IOC as well.

    + +

    New epicsEndian.h header

    + +

    Any C or C++ code can #include "epicsEndian.h" which defines four +macros. The main one is EPICS_BYTE_ORDER and is defined to be either +EPICS_ENDIAN_LITTLE or EPICS_ENDIAN_BIG (these are numeric constants 1234 and +4321 respectively). The fourth macro is called EPICS_FLOAT_WORD_ORDER and is +needed because some ARM systems use mixed-endian format floats.

    + +

    Note that just knowing the CPU's endianness doesn't tell you the complete +story about the byte order that your hardware registers will present to the +CPU; byte swapping is often performed automatically by PCI to VME bridge +devices and by other kinds of bus converter, so "portable" drivers aren't +always.

    + +

    Array Subroutine Record added (aSub)

    + +

    A heavily modified version of Andy Foster's genSub record type has been +added, called the aSub (Array Subroutine) record type. The new name is so +that genSub records can continue to be used in IOCs that need those features +that have changed or been removed in creating the aSub record type. The main +differences between genSub and aSub are:

    +
      +
    • The fields UFA..UFU, UFVA..UFVU, TOVA..TOVU + and VERS have been removed.
    • +
    • The INPA..INPU and OUTA..OUTU link fields can now be + changed at runtime.
    • +
    • A new set of fields NEA..NEU and NEVA..NEVU have been + added which hold the current element count for the associated input and + ouput fields. These count fields ensure that putting an array to the + A..U or VALA..VALU fields cannot permanently shorten + the number of elements that the array fields can hold.
    • +
    • The default value for the FTA..FTU and FTVA..FTVU + fields has been changed from "STRING" to "DOUBLE", to reduce the memory + footprint of unneeded fields.
    • +
    • aSub subroutines can be asynchronous, using the usual method of setting + prec->pact to TRUE before returning and arranging for the + record's process() routine to be called to signal completion. + The output links are not written until the asynchronous completion + callback, and input links are not read at asnychronous completion.
    • +
    + +

    epicsTimeEventDeviceTime support for soft channel input records

    + +

    The Soft Channel device support for all input record types now supports +the fetching of the timestamp along with the data when the record's TSE field +is set to epicsTimeEventDeviceTime (-2). This works for both DB and CA links. +However the timestamp will only be fetched if the record's TSEL link is not +set, so you can't point TSEL to another record to read the -2 value into TSE +(that's because to make this work properly would require that the TSEL link +be read twice every time the record processes; once in the soft channel +device support, and again when the record type calls +recGblGetTimeStamp().

    + +

    devLib: CR/CSR Support added

    + +

    Thanks to Eric Bjorklund for providing a patch to devLib that gives access +to the CR/CSR address space (for those on a VME64 CPU with appropriate +support from your BSP).

    + +

    ascaStats, dbcaStats, seqcaStats

    + +

    Query routines have been added that count CA client connections made by +Access Security rules and Database Channel Access links. A similar routine +will be added to version 2.0.12 of the sequencer for counting sequence +program CA links:

    + +
    +
    void ascaStats(int *pchans, int *pdiscon);
    +void dbcaStats(int *pchans, int *pdiscon);
    +void seqcaStats(int *pchans, int *pdiscon);
    +
    + +

    The pchans parameter should point to integer storage which will be set to +the total number of channels open, while the value at the pdiscon pointer +will be set to the number of channels currently disconnected. Prototypes for +these routines can be found in the header files asCa.h and dbCaTest.h +(seqCom.h for the sequencer).

    + +

    Messages from errlog

    + +

    J. Lewis Muir provided patches to ensure that all messages printed on the +IOC's console by the errlog subsystem are sent to the stderr output stream +rather than to stdout.

    + +

    ipToAsciiProxy

    + +

    This thread calls upon vendor libraries which may use significant amounts +of stack. Account for this by providing the ipToAsciiProxy thread with an +epicsThreadStackBig stack.

    + +

    iocBuild, iocRun and iocPause

    + +

    These three new commands are mainly intended for use with DESY's redundant +IOC software but they might have some uses for others too. iocBuild +allow an IOC to be initialized and set up ready for a quick start without +actually making it live; a subsequent iocRun will bring it to the +same state as an iocInit would have. Once an IOC has been started +(using either iocInit or iocBuild + iocRun) the +iocPause command can be used to freeze it, disconnecting its PVs and +stopping all scan activity. The iocRun command restarts the IOC +from this state.

    + +

    While this might seem like a useful thing to be able to do, we have not +tested it on IOCs using real-world I/O, and it is not unlikely that pausing +an operational IOC could cause irremdial havok to any device support, +sequence programs and other software which is not expecting it, so use with +care and make sure you test it first. An IOC that is kept paused for more +than a minute or two could fill up its network buffers and become impossible +to restart without rebooting.

    + +

    IOC Support on 64-bit archs

    + +

    A fairly significant number of changes have been that were necessary to +allow the IOC code to run properly on 64-bit CPU architectures where a +long is 64 bits wide. This was not as simple as replacing every +instance of the typename long with epicsInt32 because that +would have broken a lot of external code unnecessarily. The generated +record.h file now uses the typenames from epicsTypes.h to declare record +fields, thus field sizes are the same on both 32- and 64-bit platforms (on +64-bit, a DBF_LONG does not map to long but to +int). This change does not affect status return values, which are +still implemented using the native long type for the platform.

    + +

    Conversion of empty strings to character types

    + +

    While changing the conversion routines in db/dbConvert.c and +db/dbFastLinkConv.c to support 64-bit architectures as described above, it +was noticed that an empty string converts to the value 0 for all types other +than DBF_CHAR and DBF_UCHAR where it converts to the ASCII +character '0', value 0x30. Since these types are usually used for +storing small integers or boolean values rather than ASCII characters, it was +decided that this conversion is wrong so it has been changed to match the +other numeric conversions.

    + +

    epicsShareAPI deprecated inside IOC

    + +

    APIs that are intended for use inside the IOC, the epicsShareAPI +attribute is slowly being removed. This keyword is only used on MS Windows +where it indicates to the compiler that the function should use Pascal +calling conventions rather than C ones, and was necessary to be able to call +such functions from MS Visual Basic. APIs for use by client code (CA and +libCom) will generally retain the attribute if they already had it.

    + +

    Record types ANSIfied

    + +

    Thanks to John Hammonds at the EPICS Codeathon, the record type +implementations have been converted from K&R to ANSI C prototypes.

    + +

    Added Perl5 CA library

    + +

    Base now provides a CA client library interface for Perl5 scripts in +src/cap5 and includes as examples some implementations of the +catools programs written in Perl. Documentation on how to use the Perl +library is available in base/html/CA.html after the build completes. +This library cannot currently be built on Windows targets.

    + +

    IOC ignores SIGHUP

    + +

    iocInit() installs an signal handler for ignoring SIGHUP. This fixes a +problem that appeared with soft IOCs run as a procServ child on some Linux +systems, where a disconnecting CA client would cause the soft IOC to get a +SIGHUP and exit.

    + +

    Build System Reorganization

    + +

    Several changes have been made to the build system, although these changes +should not affect the contents of Makefiles or any applications +using Base. They do however require that the version of GNU Make used be 3.81 +or later. These changes are briefly:

    +
      +
    • A new tool is provided that expands out @VAR@ macros. By + default it knows the value of @TOP@, @ARCH@ and any + paths defined in the application's configure/RELEASE file such + as @EPICS_BASE@, but additional macros can be defined in the + Makefile that uses it by adding to the EXPAND_VARS + variable like the example following, which creates an @EXE@ + macro that expands out to .exe on windows targets and to nothing + on other platforms:

      +
        EXPAND_VARS += EXE=$(EXE)
      +

      Files that contain @VAR@ macros to be substituted must have + an at sign @ as the last character of their name and be + listed in the EXPAND variable of their Makefile. The + expanded file will have the same name as the original with the + @ suffix removed, and is then available for compiling or + installing using any other build mechanism.

      +
    • +
    • Support has been added for installing Perl library modules. The + Makefile variable PERL_MODULES can be set to a list of + names of files to be installed into the $(TOP)/lib/perl + directory. The above macro expansion facility can then be used in perl + programs that use these libraries to set the perl search path to include + that directory. The syntax for this is as follows:

      +
        use lib '@TOP@/lib/perl';
      +  use MyModule;
      +

      The filenames listed in PERL_MODULES can include subdirectory + path components and the build system will preserve these in the installed + result.

      +
    • +
    • The Perl scripts that were in configure/tools are now found in + the new src/tools directory, and get installed into the + appropriate bin/hostarch directory at build time. Some of + these scripts are no longer required and have been removed, and others + are being modified to make them more modular, extracting common routines + into perl library modules.
    • +
    • The generated files that were created by running make in the + configure directory are no longer required, having been replaced + by additional mechanisms inside the build system files. This removes a + common source of build problems.
    • +
    + +

    Access security configuration files

    + +

    Rules and macros were added for creating an *.acf file, access security +configuration file, from an *.acs file using the C preprocessor. An .acs file +has the same format as an .acf file with the addition of '#include +"<filename>"' and '#define <macroname> <value>' lines. The +C preprocessor includes the #include files and performs the macro +substitutions.

    + +

    Changes to subArray record error behaviour

    + +

    In previous versions the INDX field of a subArray record was set to zero +if the array that it reads through its INP field became empty. From this +release the INDX field will only be modified by the record's process() +routine if INDX is greater than MALM. In the event that the no data is read +through the INP link, the subArray's UDF field will be set and a UDF/INVALID +alarm will be generated.

    + +

    scanOnce(precord) argument

    + +

    The argument to scanOnce() is a pointer to the record to be +scanned. This used to be a void * pointer, but is now a +dbCommon * pointer. Record types written in C that call +scanOnce() will still compile without having to change the source +code (although a comiler warning may be generated), but any record types +implemented in C++ will have to be fixed to cast the record instance pointer +to a dbCommon * instead.

    + +

    New Architectures

    + +

    The following target architectures have been added to this release, +although the core developers lack the ability to test all of them:

    +
      +
    • linux-cris +
        +
      • linux-cris_v10
      • +
      • linux-cris_v32
      • +
      +
    • +
    • linux-xscale_be (tested with MOXA UC-7408 Plus)
    • +
    + +

    Added compile line header search directories

    + +

    The compile line list of directories to search for header files now +includes O.Common and the existing os subdirectories of SRC_DIRS +<src_dir>/os/<OS_CLASS>, <src_dir>/os/posix, and +<src_dir>/os/default.

    + +

    Parallel make now supported

    + +

    The -j jobs option is now supported for users of GNU Make +version 3.80 or later. The -j option speeds up building by allowing +multiple jobs (build commands) to be run in parallel; this will have the most +effect on hosts with multiple symmetric processors, but can also speed up +uni-processor builds. Builds still work as before when run without +-j, but using the -j option with an earlier version of GNU +make will fail since this relies on the $(eval) function which was +introduced in GNU Make 3.80.

    + +

    The -j option has been tested with this version of EPICS Base, +but it may not work for Extensions or IOC applications unless their +dependancies are specified correctly.

    + +

    New DIRS directory dependancy specifications are required to determine the +directory build order with -j. For example if we have 2 directories, +src and configure, and the configure directory must be built before the src +directory, the Makefile should contain:

    + +
    +
     DIRS = configure src
    + src_DEPEND_DIRS = configure
    +
    + +

    The directory dependancy specifications are only needed when -j +is given to make. Without the -j the order of directories in the +DIRS definition determines the build order as before.

    + +

    Breakpoint tables

    + +

    Some sites may be using breakpoint tables that are not monotonic in one +direction. In R3.14.9 both axes of a breaktable had to be monotonic; a table +will not be loaded if either the raw or engineering values change direction. +There are situations where this check is too strict however, so a new +variable named dbBptNotMonotonic has been added that disables this +check if its value is non-zero.

    + +

    Breakpoint tables are often included in an IOC's .dbd file, but they can +also be loaded from a .db file (although VDCT will probably not understand +them). Since there is no way to set the above variable inside the +dbExpand program that expands out an IOC's .dbd file (which also +rejects non-monotonic breaktables), all applications that use such tables +will have to load those tables separately, using either +dbLoadDatabase or dbLoadRecords.

    + +

    RTEMS on processors with MOTLOAD bootstrap

    + +

    Added support for more 'Global Environment Variables'. Documented in the +EPICS/RTEMS tutorial.

    + +

    RTEMS on processors with PPCBUG bootstrap

    + +

    Set NTP server as well as nameserver and log server from server bootstrap +settting.

    + +

    RTEMS compiler flags

    + +

    The -ansi flag has been removed from CONFORM_FLAGS_STRICT and +CONFORM_CXXFLAGS_STRICT -- there are many useful library functions whose +prototypes are disabled when -ansi is used.

    + +

    SEL record (Mantis #295)

    + +

    The value in the SELN field was not being checked against its limit, +potentially causing a crash on some architectures.

    + +

    Calc expressions

    +
    +
    VAL keyword
    +
    The keyword VAL is now supported in CALC expressions. In a + calc or calcout record it returns the current contents of the VAL field + (which can be written to by a CA put, so it might not be the + result from last time the expression was evaluated). In Access Security + expressions it returns the result of the previous evaluation of the + rule expression. In other uses of the calcPerform engine, the result + may not be well-defined.
    +
    MIN, MAX, FINITE, ISNAN + functions
    +
    Thanks to Benjamin Franksen these functions can now accept any number + of arguments, thus MAX(A,B,C,D,E,F,G,H,I,J,K,L) and + MIN(A)are now legal expressions. The FINITE function + returns a non-zero value as long as none of its arguments are NaN or + Inf values, while ISNAN returns non-zero if any of its + arguments are NaN values.
    +
    + +

    softIoc now starts shell by default

    + +

    To prevent an interactive shell from being started, give a -S +(upper case) option on the softIoc command line. The old -s (lower +case) option is still accepted, but now does nothing.

    + +

    Error messages from dbLoadRecords

    + +

    Some error messages output by the dbLoadRecords parser should be easier to +understand, having been reworded to report what was actually the problem +rather than just which routine found the error...

    + +

    Command registration for iocsh

    + +

    The iocsh command registration data and routines have been moved out of +the src/iocsh directory (which now no longer exists) into a file for +each library. At the same time, the iocsh implementation code has been moved +into libCom. There is no libiocsh.a library created any more, so any +applications (such as the sequencer) which explicitly list iocsh in +a xxx_LIBS definition in their Makefile(s) will need to be modified +to have the library name removed. In most cases though the library will have +been included using the EPICS_BASE_IOC_LIBS or +EPICS_BASE_HOST_LIBS variables that are set in Base, and thus no +changes will be needed.

    + +

    Changes between 3.14.8.2 and 3.14.9

    + +

    Cygwin Builds

    + +

    The cygwin make version 3.81-1 does not handle MS-DOS path names and thus +does not work for EPICS builds with RELEASE file definitions. From the +discussion on the cygwin mailing list it looks like this will be fixed in +make version 3.82. In the meantime make version 3.80 or a fixed make can be +downloaded from this +website.

    + +

    New Architectures

    + +

    The following target architectures have been added to this release, +although the core developers lack the ability to test all of them:

    +
      +
    • darwin-ppcx86
    • +
    • darwin-x86
    • +
    • freebsd-x86
    • +
    • freebsd-x86_64
    • +
    • interix-x86
    • +
    • linux-arm
    • +
    • osf-alpha
    • +
    • osf-alpha-gnu
    • +
    • vxWorks-mpc8540
    • +
    • vxWorks-ppc604_altivec
    • +
    + +

    vxWorks compiler optimization level

    + +

    Wind River Systems do not support optimization levels beyond -O2 +for vxWorks applications compiled using gcc, so optimized vxWorks builds are +now set to -O2 only (we currently use -O3 everywhere +else).

    + +

    cas

    + +

    There was a bug in the portable channel access server library that +prevented the PV Gateway from being able to handle and pass on alarm +acknowledgements from the EPICS Alarm Handler ALH. This has been fixed in +this release and should only require that the gatewey be recomplied against +this version of Base.

    + +

    dbLoadTemplate

    + +

    The parser for the substitutions file accepted by dbLoadTemplate() has +been revised, and is now stricter than it used to be — unmatched +characters that were accepted and discarded without warning before will now +generate errors. The quote characters at the two ends of a quoted string +value must now match, although either single or double quotes can be used. +Escaped characters inside a quoted string should pass safely through to the +underlying dbLoadRecord() command. The characters permitted in a bareword +parameter (i.e. a filename or value that is not inside quotes) have been +reduced from the overly wide set allowed before; the permitted characters now +comprise: a-z A-Z 0-9 _ - + : . / \ [ ] < > ;

    + +

    Escaped characters in record fields

    + +

    Field value strings loaded from a database file by dbLoadRecords() can now +use C89-compatible escaped character codes such as \", \', \t, \n, \064 and +\x7e. The parser also now checks for and reports strings that have a newline +character in them as an error - if you want a newline in a field, use the \n +escaped version. These escapes only apply to the value part of a field() +entry in .db file though; no other strings have escape codes translated.

    + +

    libCom/test and db/test

    + +

    These test programs are no longer installed into the bin/arch +directory. Running all these programs on any host architecture is as simple +as typing make runtests in the base top level directory or in any +appropriate subdirectory. The runtests target uses Perl's Test::Harness +module to execute all of the test programs and summarize the result. It is +also possible to run individual tests as desired, by executing them from the +relevent O.<arch> directory; the program output is designed to be +comprehensible to humans.

    + +

    For vxWorks and RTEMS, a test harness has been created that will run all +of the test programs in a suitable order. At a vxWorks target shell, type:

    + +
    +
    ld </path/to/base/bin/vxWorks-arch/vxTestHarness.munch
    +cd "/path/to/writable/directory"
    +epicsRunLibComTests
    +
    + +

    On RTEMS, boot the bin/arch/rtemsTestHarness binary.

    + +

    You may wish to capture the output from running these to a file to more +easily check the results, since there is no wrapper program to collect and +summarize the results of the individual test programs.

    + +

    Breakpoint tables

    + +

    The handling of breakpoint table data has been reworked. It is now +possible to give table data in either ascending or descending order, and the +breakpoint data may have a negative slope such that the engineering values +increase while the raw values decrease and vice versa. The only restriction +on the data is that is must be monatonically increasing or decreasing, so you +can't use a breakpoint table for curves that have local minima or maxima. +This restriction is checked for when the breakpoint table is loaded.

    + +

    Support for vxWorks 6

    + +

    The build configuration support for vxWorks 6.x has changed significantly, +having been recombined with the older vxWorks 5.x support. The two target +architectures vxWorks6-mv2100 and vxWorks6-mv5100 have been +removed; IOCs built under vxWorks 6.x using these architectures must revert +to the original vxWorks-ppc603_long and vxWorks-ppc604_long +architectures instead. The configuration file +CONFIG_SITE.Common.vxWorksCommon now specifies the vxWorks version number, as +well as the filesystem path to the installation of vxWorks using the +WIND_BASE variable.

    + +

    postfix and calcPerform

    + +

    The calc expression parser and exective have undergone a signficant +overhaul, although the API and expression language supported are backwards +compatible with one minor exception, described below. Significant +improvements have been made to both the API and the expression language: +Multiple values can be calculated and assignments made in a calc expression; +Error reporting for humans is now supported by the expression parser, and +code can discover what inputs and outputs are needed and generated by an +expression.

    + +

    Assignment operations are now possible, using the new := operator +which must have an expression variable (A through L) to its +immediate left. Multiple expressions can be included in the calculation +string, separated by a semicolon ;, all but one of which must be +assignments. The value of the whole string is determined by the single +non-assignment expression, which may appear anywhere in the string. For +example, the following string causes a single CALC record to output the +successive values of a sine curve in 1 degree intervals: +sin(a); a:=a+d2r

    + +

    Previously any expression that performed an invalid operation which would +generate a NaN or Inf result would be stopped immediately and return an error +to the caller. Now it is possible to perform operations that generate NaN or +infinite results, and the results are returned as normal. The result of the +expression or the value assigned to a variable may thus be a NaN or an +infinity. To permit this to be checked within the expression, the new +operators finite(), isinf() and isnan() have been +added to the expression language. The literal values Inf and +NaN are also now supported in expressions.

    + +

    The only incompatible change to the expression language was to change the +NOT operator from performing a unary minus operation to an integer +bitwise not; the former meaning is illogical and as a result was probably not +used much, if at all — nobody complained when I discussed this on +tech-talk...

    + +

    The extended API and the expression language are now discussed in detail +in chapter 19 (libCom) of the R3.14.9 version of the IOC Application +Developer's Guide.

    + +

    calc and calcout record, asLib

    + +

    As a result of the above changes to the calc expression parser and +executive, both the calc and calcout records and the Access Security library +have been modified to take advantage of the new error diagnostics. The Access +Security library has been made to reject any expression that contains an +assignment operation, to prevent any possible security holes that might be +caused by this significant change to the expression rules.

    + +

    The calc and calcout records have had some subtle changes applied: To +mitigate the effects of the Inf/NaN behaviour change, the result of an +expression is checked for NaN, which will result in the UDF field being set +and an Undefined/INVALID_ALARM being raised as a result.

    + +

    An empty string is no longer a legal expression, therefor the CALC and +OCAL fields have been changed so that their default value is a literal +"0" string. Also any calcout record device support must now add the +line #include "postfix.h" before the +#include "calcoutRecord.h" line in the source code.

    + +

    recGblAlarmHook

    + +

    The recGblResetAlarms() routine (which is called by all record +types towards the end of record processing) now optionally calls a single +hook routine via the function pointer recGblAlarmHook after it has +updated a record's alarm status and severity. See regGbl.h for the hook +routine prototype definition, the routine is also given the previous values +of the record's status and severity. The hook routine must not block, since +this would hold up record processing.

    + +

    ai, bi, mbbi and mbbiDirect records

    + +

    These record types now support raw simulation mode. If the value of the +SIMM field is 2 (enum string "RAW"), the SIOL link value is placed into the +RVAL field and passed through the raw to engineering units conversion process +just like the real device support's raw value would be.

    + +

    dbpr

    + +

    The TIME field of a record is now displayed in a human readable format. +TIME used to have an interest level of 4 since it was output in hex and not +very easy to understand, but it has now been moved to interest level 2.

    + +

    configure/RELEASE*

    + +

    The convertRelease.pl parser now supports the use of +-include statements in configure/RELEASE files as well as +include statements.

    + +

    MIPS support

    + +

    Changes were needed to the configure/tools/munch.pl script to +support vxWorks-MIPS targets.

    + +

    epicsUnitTest

    + +

    A new facility is provided in libCom for use in generating automated test +programs. Many (but not all) of the test programs in libCom/test +have been converted to use this facility.

    + +

    dbStaticLib

    + +

    Off-by-one buffer overflow error fixed in dbFindField() which only seemed +to affect vxWorks-(intel) targets.

    + +

    RTEMS

    + +

    Create a POSIX-compliant TZ environment variable from EPICS_TIMEZONE. +Previous versions had an incorrect format which was ignored by tzset().

    + +

    Added space for user extensions. This provides the infrastructure for the +spy command.

    + +

    Fixed error in epicsThreadGetName for non-EPICS threads.

    + +

    Added hooks for application routines to supply special network +configuration parameters. The RTEMS startup code calls +epicsRtemsInitPreSetBootConfigFromNVRAM just before reading values from NVRAM +and epicsRtemsInitPostSetBootConfigFromNVRAM just afterwards. See +epicsRtemsInitHooks.h for prototypes and global variables.

    + +

    e_flex

    + +

    e_flex has been modified to accept DOS line endings as well as +Unix ones. The scan.c file was recreated using e_flex +itself and the flex.skel file on the modified +scan.l.DISTRIB source. initscan.c is not required or used +for the EPICS build, so it has been removed.

    + +

    devLib

    + +

    devLib is now built for all architectures, whereas before it was only +built on RTEMS and vxWorks. However for it to be usable there must be an +appropriate table of OS-specific routines provided. For those OSs that don't +implement these routines a default table is defined which will result in an +error on any attempt to use devLib routines, but the default table can be +overridden in an external library that provides an appropriate table. This +subtle change was implemented to allow use of the SIS 3100 PCI to VME bridge +from Linux, and needs no change to the other implementations of +devLibOSD.c.

    + +

    iocsh

    + +

    Added epicsThreadResume command.

    + +

    libCom

    + +

    mallocMustSucceed and callocMustSucceed accept 0-byte requests. Note that +these routines may return a NULL pointer in such cases.

    + +

    Mac OS X (Darwin)

    + +

    Added support for EPICS_HOST_ARCH=darwin-ppcx86 for building +libraries/applications which will run on both PowerPC and Intel x86 +targets.

    + +

    Added support for EPICS_HOST_ARCH=darwin-x86.

    + +

    Changes between 3.14.8.1 and 3.14.8.2

    + +

    epicsStrtod

    + +

    On architectures whose native version of strtod() actually works properly +(i.e. converts +/-Inf[inity] and NaN strings to their double equivalents) we +managed to break the use of this in the R3.14.8.1 release. This is now +fixed.

    + +

    Changes between 3.14.8 and 3.14.8.1

    + +

    Version Numbering

    + +

    This release adds a fourth level of version number, which we haven't used +since R3.13.1.1. The intention is to imply that R3.14.8.1 includes some small +but important changes since R3.14.8 but no signficant new behaviours or API +modifications. This fourth level has required us to modify the version number +system and the macros in the epicsVersion.h file slightly though. We have +taken the opportunity to introduce a new variable EPICS_SITE_VERSION +to the file configure/CONFIG_SITE that adds an optional site-specific version +number; sites that were achieving this by editing the +configure/CONFIG_BASE_VERSION file should switch to setting +EPICS_SITE_VERSION instead.

    + +

    CA

    + +

    Mantis entries fixed:

    + +

    232 - non-preemptive mode client relying on ca_poll does not always +reconnect

    + +

    Win32 symbol changes

    + +

    These changes are required to allow software outside of Base to be built +on win32 architectures.

    +
      +
    • win32 epicsShareAPI changes to libCom +

      The win32 Pascal calling convention (epicsShareAPI) has been removed + from the following header files in libCom/misc: adjustment.h, + cantProceed.h, epicsConvert.h, epicsStdlib.h, epicsString.h.

      +
    • +
    • epicsString +

      A new function epicsStrtok_r has been added because win32 does not + implement strtok_r.

      +
    • +
    • epicsStdlib +

      All function described in epicsStdlib.h are now implemented in + epicsStdlib.c

      +
    • +
    • win32 epicsShareExtern changes in dbStaticLib +

      The type name mapping array mapdbfType defined in dbFldTypes.h has had + the correct export modifier keywords added to it for use on win32.

      +
    • +
    + +

    Changes between 3.14.7 and 3.14.8

    + +

    New host targets

    + +

    Configure files are now available to support the win32-x86-mingw (MinGW +C++ compiler) and win32-x86-cygwin (WIN32 API with cygwin C++ compiler) host +targets.

    + +

    Configure files were also added for linux-x86_64 and solaris-sparc64 but +these files are for experimental purposes only and to show that we are +working on these targets. Currently these two 64 bit targets are not passing +our test suite, so they must not be used in production systems.

    + +

    Runtime Hardware Address Changes

    + +

    An Extended Device Support mechanism has been introduced which is designed +as a safe way to widen the API between iocCore and the device support +software it interfaces with. An extended device support can now be notified +of changes to a record's hardware address, and is given the chance to approve +or reject that change.

    + +

    As a result of introducing this notification mechanism, any device support +that was capable of handling runtime address changes in prior versions of +Base will have to be updated to provide the new interface, since the absence +of extended device support is now taken to mean that runtime address changes +are not understood by the device support. This requirement is not expected to +affect many EPICS sites.

    + +

    POSIX thread priority scheduling

    + +

    POSIX thread priority scheduling is now supported. There is a new user +option USE_POSIX_THREAD_PRIORITY_SCHEDULING in the CONFIG_SITE configure file +for using POSIX thread priority scheduling. For now the default value is NO. +This has only been tested on a few versions of linux. On linux, in order, to +use real time priorities the option must be set to YES and the ioc must be +started with root privilages.

    + +

    VX_DIR

    + +

    The definition VX_DIR was removed from configure/RELEASE and moved to +configure/os/CONFIG_SITE.Common.vxWorksCommon. The configure/RELEASE* files +should contain location definitions for EPICS modules only.

    + +

    RTEMS_BASE

    + +

    The definition RTEMS_BASE (and RTEMS_VERSION) were removed from +configure/RELEASE and moved to configure/os/CONFIG_SITE.Common.RTEMS. The +configure/RELEASE* files should contain location definitions for EPICS +modules only.

    + +

    event generator and event receiver record support

    +All apsEvent specific record support has been removed from base + +

    drvTS and apsEvent support

    + +

    The following files have been removed from base: drvTS.h drvTS.c, egDefs.h +egRecord.c egRecord.dbd egeventRecord.c egeventRecord.c egeventRecord.dbd +erDefs.h erDefs.h erRecord.c erRecord.dbd ereventDefs.h ereventRecord.c +ereventRecord.dbd

    + +

    These are removed from EPICS base. The version that previously came with +base was the version that worked with the APS event system. It is available +from APS. The version that works with the newer event systems that evolved +from the APS system is available from the Swiss Light Source.

    + +

    task watchdog

    + +

    It was possible for taskwd (task watchdog) to not detect when some of the +standard tasks failed. This is because they were passing their threadid +rather than using epicsThreadGetIdSelf. It was possible to call taskwdInsert +before the threadid was actually set.

    + +

    dbLock and dbBkpt

    + +

    dbLockGetLockId incorrectly always returned 0. dbBkpt (database breakpoint +facility) is the only code that needed this. This caused unknown errors if +the dbBkpt facility is used.

    + +

    gpHashLib.c

    + +

    The maximum size was initialized to 65636 instead of 65536.

    + +

    epicsExport.h

    + +

    Add additional cast to prevent 'strict aliaising' warnings.

    + +

    iocsh

    + +

    I/O redirection from vxWorks startup scripts now works.

    + +

    dbRecordsOnceOnly

    + +

    This new variable which can be controlled using the iocsh var +command (or by simple assignment in the vxWorks shell) allows users to change +the behaviour of dbLoadRecords() when it finds a duplicate record +definition. The default behaviour has always been to permit multiple record() +statements for the same record name when loading record instances, but by +setting dbRecordsOnceOnly to any non-zero value, duplicates will +instead generate an error message instead. It is expected that this will only +be used in special circumstances, to detect the presence of unintentional +duplicates where it is known that they should not exist.

    + +

    select record

    + +

    This record now sets and posts monitors on its SELN field indicating which +of the inputs was selected, independent of which selection mechanism was +selected via the SELM field. This makes it much more useful, especially when +the High, Low or Median mechanisms are chosen.

    + +

    macLib

    + +

    Macro expansions in any program using the macLib facility from libCom can +now include a default value which will be used if the macro named is not +defined at the moment of substitution. The syntax for this is +$(name=default) or ${name=default}. The default string can +itself contain other macros like this: $(name=$(default)). This +feature has actually been present since R3.14.6.

    + +

    errlog

    + +

    On a powerPC, during iocInit, a crash could occur. In particular the +SYNAPPS version of save_restore experienced crashes. This is now fixed.

    + +

    ca

    + +

    Mantis entries fixed.

    + +

    221 - should shutdown full duplex comm on udp sockets if not used

    + +

    192 - concurrency bug in channel access to local DB

    + +

    181 - ca_host_name() now returns the host name of the client, not the +host:port of the server

    + +

    161 - issues surrounding manipulation of CA contexts

    + +

    153 - CA (caput) client does not reconnect after server +suspend-continue-shutdown cycle

    + +

    111 - non-preemptive clients disconnect if ca_poll() isnt called +regularly

    + +

    portable ca server

    + +

    Mantis entries fixed.

    + +

    196 - portable server library intermittent hang on UNIX systems

    + +

    191 - corrupt value when doing a put through portable server (little +endian host)

    + +

    175 - example portable server array PV 'alan' does not have time stamps

    + +

    gdd (used by portable server)

    + +

    Mantis entries fixed.

    + +

    211 - GDD: aitConvertStringEnum16 does nothing if +pEnumStringTable==NULL

    + +

    RTEMS port

    + +

    Added support for setting NFS server/mount information from PPCBUG +argument strings.

    + +

    rtems_shutdown_executive is now called on IOC exit. On many BSPs this will +return control to the bootstrap PROM.

    + +

    Set POSIX TZ environment variable from NVRAM, or failing that, from +EPICS_TIMEZONE.

    + +

    Cleaned up support for obtaining network configuration from NVRAM.

    + +

    Added support for some additional boards.

    + +

    Set iocsh prompt from host name.

    + +

    Initialize in-memory filesystem from tar image following executable in +bootstrap flash memory. This allows for fully standalone IOCs -- no TFTP/NFS +server required.

    + +

    Set IOC_NAME and IOC_STARTUP_SCRIPT environment variables from bootstrap +parameters.

    + +

    vxWorks port

    + +

    Fixed mantis entry.

    + +

    179 -base does not build with vxWorks 6.0

    + +

    225 - On vxWorks epicsThreadCreate returned -1 instead of 0 if a thread +could not be created. This is fixed.

    + +

    OS X port

    + +

    Builds on Tiger.

    + +

    Readline now used by default.

    + +

    WIN32 port

    + +

    Fixed mantis entries.

    + +

    195 - explicitly unloading Com.dll causes crash

    + +

    230 - assert fail of caget, caput, etc under msvc 8

    + +

    231 - manifest files not installed under visual C++ 8

    + +

    POSIX port

    + +

    Fixed mantis entries.

    + +

    186 - failure after exit command if log client is running

    + +

    222 - osiSpawnDetachedProcess doesnt close open files in dupicate process +on POSIX

    + +

    Changes between 3.14.6 and 3.14.7

    + +

    selRecord

    + +

    The select record type has for a long time made use of a coule of magic +numbers (1e+30 and -1e+30) to mean "not a real value", which prevents the +record from working properly if one or other of these appears as an actual +data value. These have been changed to use +Inf and -Inf or NaN instead, so ++/-1e+30 may be used as a data value.

    + +

    ai, ao, dfanout and subroutine Records

    + +

    These record types have been modified to respond better to NaN values as +follows: if the VAL field contains a NaN value, the UDF field will be set and +an undefined value alarm will be triggered.

    + +

    epicsStdlib.h/epicsStrtod()

    + +

    epicsStdlib.h declares epicsStrtod() which provides a version of strtod +which handles NAN/INF on all architectures. All uses of strtod() in base have +been converted to use epicsStrtod().

    + +

    epicsStdlib.h also declares epicsScanFloat() and epicsScanDouble() which +replace calls to sscanf with routines which handle NAN/INF on all +architectures.

    + +

    epicsThreadCreate Stacksize Posix

    + +

    The posix implementation of epicsThreadCreate() now makes pthread calls to +set the stack size. The sizes returned by epicsThreadGetStackSize() for the +inputs epicsThreadStackSmall, epicsThreadStackMedium and epicsThreadStackBig +are 128K, 256K and 512K respectively on at least the architectures Linux, +Solaris, HPUX and Darwin (different values are used on vxWorks and RTEMS). +This allows creation of many more threads on most systems.

    + +

    dbNotify

    + +

    dbNotifyCancel now waits if the userCallback is active when dbNotifyCancel +is called. Previously it just returned. NOTE CAREFULLY. This means that the +userCallback must not free the putNotify structure.

    + +

    CA commandline tools

    + +

    caget and camonitor now have an additional "-s" option to explicitly +request server-side string conversion, which - in case of the regular CA +server - leads to "precision" info (e.g. the PREC field of an EPICS record) +being honoured.

    + +

    POSIX signals

    + +

    Signals are blocked in all but the main thread. Applications/drivers which +require signal delivery to a subthread will need to be modified.

    + +

    epicsExit

    + +

    Three new functions are implemented: epicsExit, +epicsExitCallAtExits, and epicsAtExit. These are similar to +exit and atexit, i.e. they provide the ability to register +a function to be called when the process exits. They are provided becase +neither vxWorks or win32 properly implement exit and +atexit. Note that they apply to an IOC stopping NOT to a thread +exiting.

    + +

    epicsStdio and epicsStdioRedirect

    + +

    In order to support iocsh redirection of stdin, stdout, +and stderr, epicsStdio.h defines the following new +functions: epicsGetStdin, epicsGetStdout, +epicsGetStderr, epicsSetStdin, epicsSetStdout, +epicsSetStderr, and epicsStdoutPrintf. +epicsStdioRedirect.h defines macros that redefine stdin, +stdout, stderr, and printf.

    + +

    Any code that includes epicsStdioRedirect.h will automatically +have it's stdio redirected. It has been added to many files in base. If code +called by dbior wants it's output redirected, it must also include this +file.

    + +

    IOC Test Facilities

    + +

    Any command that previously had an argument for a report file name no +longer has the argument. The new iocsh redirection capability is now used. +For example the former command:

    +
        dbl "0" reportName
    +
    Is now:
    +
        dbl > reportName
    +Note that this does NOT work on the vxWorks shell only on iocsh. On the +vxWorks shell the following command can be given: +
        iocshCmd("dbl > reportName")
    + +

    errlog

    + +

    errlog now calls epicsAtExit and releases all resources when +epicsExitCallAtExits is called.

    + +

    libCom

    + +

    epicsStrGlobMatch() routine added.

    + +

    iocsh

    + +

    Input/output redirection added.

    + +

    iocshCmd routine added (callable from vxWorks shell).

    + +

    help command uses globbing.

    + +

    calcoutRecord

    + +

    The ODLY (Output Delay) was not handled properly. This is fixed.

    + +

    compressRecord

    + +

    make sure reset gets called when size of INP array changes.

    + +

    dbAccess DBR_ENUM_STRS for field DTYP

    + +

    Data Base Request Option DBR_ENUM_STRS for the DTYP field of soft records +can cause an IOC to crash.

    + +

    RTEMS

    + +

    RTEMS implementation of epicsMessageQueuePending() now works.

    + +

    Added support for MVME2100 BSP.

    + +

    Added support for building RTEMS bootable images.

    + +

    iocBoot/ioc* build change

    + +

    The cdCommands and envPaths files are now created in ioc* directory only +when the ARCH defined in the ioc*/Makefile is present in BUILD_ARCHS for the +build.

    + +

    Changes between 3.14.5 and 3.14.6

    + +

    CA command line tools complete

    + +

    The complete set of Channel Access command line tools (caget, caput, +camonitor, cainfo) is available as announced during the May 2004 Collab. +meeting. Documentation is part of the CA Reference Manual. Be aware of +possible name conflicts with existing local tools.

    + +

    IOC template file configure/RULES.iocBoot removed

    + +

    The directory name wildcards that were defined here have been moved to +iocBoot/Makefile, which as a result is no longer unique in having its own +configure/RULES file.

    + +

    APS Virtual Linac template removed

    + +

    This is really a demo and a complete EPICS IOC application, not a +template. It will be made available separately.

    + +

    EPICS_HOST_ARCH win32-x86-cygwin renamed to cygwin-x86

    + +

    The EPICS_HOST_ARCH win32-x86-cygwin was renamed cygwin-x86 to avoid +confusion about what OS interfaces are used on Windows: native win32 or +cygwin's emulation of POSIX. Now we have

    +
      +
    • win32-x86 Uses native win32 interfaces with MS compiler.
    • +
    • win32-x86-borland Uses native win32 interfaces with borland + compiler.
    • +
    • win32-x86-gnu Uses native win32 interfaces with cygwin gnu compiler. + (Not implemented yet.)
    • +
    • cygwin-x86 Uses cygwin POSIX interfaces with cygwin gnu compiler.
    • +
    + +

    EPICS_TS_NTP_INET

    + +

    The time server's IP address used by the vxWorks clock routines was not +reading the default value from the generated envData.c file but going +straight to the boot host if no environment variable by that name was set.

    + +

    CONFIG_ENV and CONFIG_SITE_ENV

    + +

    These files are now parsed by a program that recognizes and ignores +comment lines. Previous versions of this parser would extract settings from +these files even if they appear on a line starting with a '#' character, so +the last line containing a setting for any variable would give the value used +as the default. This was first noticed in R3.14.5 where a commented-out +setting for the EPICS_TIMEZONE parameter was added +after the uncommented version.

    + +

    db test shell commands

    + +

    Many of the commands crashed if given no arguments. They are now more +crash proof.

    + +

    db_access - conversion of double to float

    + +

    When a CA user asked for display or control limits as a float a 0 value +was returned as -1.17549435E-38. This is now fixed.

    + +

    New DBD rule

    + +

    A new dbd rule will create a <name>Include.dbd from files specified +in a <name>_DBD macro definition. An include line will be placed in the +<name>Include.dbd for each file specified in the <name>_DBD +definition. If a Makefile contains

    +
            DBD=xxx.dbd 
    +        xxx_DBD = f1.dbd f2.dbd f3.dbd 
    + +

    an xxxInclude.dbd file will be created containing the lines

    +
            include "f1.dbd"
    +        include "f2.dbd"
    +        include "f1.dbd"
    + +

    and dbExpand will be invoked to create the xxx.dbd file from the +xxxInclude.dbd.

    + +

    Solaris Builds

    + +

    Old solaris 6 specific compiler options have been removed.

    + +

    New make targets cvsclean and archclean

    + +

    The new top level Makefile only target, cvsclean, removes cvs .#* files in +all dirs of the top directory tree.

    + +

    The new archclean target is like the clean target except that O.Common +directories are not removed.

    + +

    epicsString

    + +

    Add epicsSnStrPrintEscaped.

    + +

    epicsExport

    + +

    epicsExportAddress(typ,obj) now generates an extern named pvar_typ_obj and +epicsExportRegistrar(func) an extern named pvar_func_obj. Previously both +just named the variable pobj.

    + +

    epicsRegisterFunction(name) in conjunction with the dbd 'function' keyword +can be used to register functions referred to by record subroutine name +fields.

    + +

    Access Security

    + +

    The access security configuration rules now accept quoted strings where +just names were allowed previously.

    + +

    All dump routines now have FP version.

    + +

    A new shell command "ascar(int level)" is now available. It produces a +report of the INP channel access connections. Level (0,1,2) produces (a +summary report, summary plus unconnected channels, summary plus report of all +channels)

    + +

    Channel Access Client Library

    +
      +
    • Fixed "subscription updates intermittently do not resume when + unresponsive circuit reconnects" bug +
        +
      • Scope: +

        This bug was introduced in R3.14.5 and does not exist in any other + release.

        +
      • +
      • Symptom: +

        Subscription updates intermittently do not resume depending on + circumstances when unresponsive circuit reconnects

        +
      • +
      • Additional Information: +

        A decision was made to add a change to EPICS R3.14.5 so that when + a TCP circuit is temporarily unresponsive the channel, but not the + circuit, is immediately disconnected. This change was determined to + be necessary to improve overall system robustness in the face of IOC + or network overload. Unfortunately, an error was made when installing + these changes. I am sorry about any inconvenience that this has + caused. Thanks to Ken Evans at the APS for discovering this + problem.

        +
      • +
      +
    • +
    • Fixed "ca_replace_access_rights_event() fails if passed a nill function + pointer" bug +
        +
      • Scope: +

        This bug probably exists in all R3.14 releases.

        +
      • +
      • Symptom: +

        Passing a nill function pointer to + ca_replace_access_rights_event() should install a noop handler, but + this currently causes a failure.

        +
      • +
      • Additional information: +

        Regression tests have been installed to detect this mistake.

        +
      • +
      +
    • +
    • Fixed "CA client library crash when clear channel request occurs in get + callback handler" bug +
        +
      • Scope: +

        This bug was introduced in R3.14.5 and does not exist in any other + release.

        +
      • +
      • Symptom: +

        CA client library crash when clear channel request occurs in get + callback handler

        +
      • +
      • Additional information: +

        When testing the striptool application, Ken Evans, discovered a + bug in the CA client library occurring when a clear channel request + occurs in get callback handler. Regression tests have been updated so + that this mistake will not slip through testing undetected in a + future release.

        +
      • +
      +
    • +
    • Fixed "Double server subscription install when subscription request + occurs in connection callback handler" bug +
        +
      • Scope: +

        This bug was introduced in R3.14.5 and does not exist in any other + release. Subscription request must be made from within connection + callback handler

        +
      • +
      • Symptom: +

        It has been discovered (by Ken Evans while testing the gateway) + that certain subscription requests were persisting in the gateway + after clients had deleted them. This bug causes additional resources + to be consumed, but does not result in a crash.

        +
      • +
      • Additional information +

        Additional debugging has revealed that the CA client library in + this situation inadvertently made the subscription request twice: + once at the users’ request, and later on again when the + library auto installed subscriptions for disconnected channels.

        +
      • +
      +
    • +
    • Fixed "failure when deleting channel in get callback handler" bug +
        +
      • Scope: +

        Probably introduced in a previous R3.14 release.

        +
      • +
      • Symptom: +

        An intermittent C++ exceptions during regression testing.

        +
      • +
      +
    • +
    +
      +
    • Behavior Changes +
        +
      • Process blocks attempting to exit if the application does not call + ca_context_destroy() +

        In EPICS release R3.14 the CA client library is implemented using + axillary threads. If the application does not call + ca_context_destroy() these threads will still be running, and + depending on operating system conventions the process may + not exit if the main thread exits, but axillary threads are + still running. Note that ca_context_destroy() is functionally + equivalent to the deprecated call ca_task_exit().

        +
      • +
      +
    • +
    + +

    Channel Access Portable Server (used by the CA gateway and others)

    +
      +
    • Fixed "assert fail when writing string through Portable CA Server" bug +
        +
      • Scope: +

        This bug is only present in the portable CA server and so it does + not impact IOC based applications. The bug is present in the + CA gateway and any portable CA server based application. This problem + may have been recently introduced when GDD was patched to properly + handle fixed sized strings.

        +
      • +
      • Symptom: +

        Failure, when writing large string through the portable CA server. + There appears to be a possibility of the wrong string being written + when a smaller string is used. You may see the following message.

        +

        A call to "assert (! this->pValue->unreference ())" failed + in ..\..\..\..\include\smartGDDPointer.h line 88.

        +
      • +
      • Additional Information: +

        Thanks to Stephanie Alison at SLAC for discovering the bug and to + Ken Evans at the APS for reminding me to fix it.

        +
      • +
      +
    • +
    + +

    Changes between 3.14.4 and 3.14.5

    + +

    dbtr

    + +

    Don't seg-fault if no argument is passed to dbtr.

    + +

    New build targets.

    + +

    New files have been created in configure/os to allow +CROSS_COMPILER_TARGET_ARCHS to include solaris-sparc-gnu and +solaris-sparc-debug when EPICS_HOST_ARCH is solaris-sparc. Also +CROSS_COMPILER_TARGET_ARCHS can now include linux-x86-debug when +EPICS_HOST_ARCH is linux-x86.

    + +

    New epicsString.h function

    + +

    A new function epicsStrnCaseCmp has been added. It is like strncmp except +that it ignores case.

    + +

    R3.13 compatability files

    + +

    R3.13 compatability files are no longer generated automatically during the +build. configure/CONFIG_SITE contains two new macros for building +compatibility files. They are set to NO but can be set to YES. The macros +are:

    +
      +
    • COMPAT_TOOLS_313 +

      This will install the compatibility files needed to build R3.13 + extensions built with this R3.14 base.

      +
    • +
    • COMPAT_313 +

      This will install the compatibility files needed to build R3.13 + extensions and IOC applications built with this R3.14 base.

      +
    • +
    + +

    APS Virtual LINAC Templates

    + +

    A new set of templates has been included in R3.14.5 to implement a Virtual +LINAC in an ioc using databases and sequence programs. The Virtual LINAC +simulates the generation and transmission of an electron beam down a LINAC. +Several steering coils, BPMs, and other typical accelerator components are +simulated to provide a realistic interaction between the operator and the +"LINAC". Since it is an entirely soft application, it will work on any +platform. An medm display is provided as the primary GUI. It can also be used +to experiment with other CA client tools.

    + +

    To install the templates, use the following commands:

    +
      mkdir 
    +  cd 
    +  <base>/bin/<arch>/makeBaseApp.pl -t vlinac vlinac
    +  <base>/bin/<arch>/makeBaseApp.pl -i -t vlinac vlinac
    + +

    For further information, see:

    +
      <top>/vlinacApp/misc/README
    +  <top>/vlinacApp/misc/Virtual_Linac_Info.pdf
    + +

    Stringin record time-stamp soft device support

    +Add simple device support for converting time to nicely-formatted string +using INP field as epicsTimeToStrftime format string: +
    record(stringin, "$(user)now")
    +{
    +    field(DESC, "Current time and date")
    +    field(DTYP, "Soft Timestamp")
    +    field(INP, "@%Y-%m-%d %H:%M:%S.%03f")
    +}
    + +

    Channel Access Portable Server (used by the CA gateway and others)

    +
      +
    • Fixed failure occurring if client disconnected while asynchronous PV + attach was outstanding, or, for enumerated native type process variables, + while enumerated string table cache asynchronous IO was outstanding. This + problem does not occur in IOCs because they are not yet based on the + portable server library. This bug was first detected by Ken Evans in the + production CA gateway at the APS.
    • +
    + +

    Channel Access Original Server (used in IOC)

    +
      +
    • A bug causing the server threads to become stuck in a state where they + process requests, but no longer send responses, if in the past the system + was experiencing network buffer starvation has been fixed. This problem + existed probably in all previous EPICS releases, but network buffer + starvation issues have become more prevalent starting with Tornado + 2.0.
    • +
    • Users noticed that UDP related output from the casr diagnostic was + easily confused with the information from casr for TCP circuits. This has + been fixed.
    • +
    + +

    Channel Access Client Library

    +
      +
    • Disconnect behavior is now more robust in response to congestion. When + a channel times out, the channel is disconnected, but not the circuit. + The circuit is only disconnected when the internal TCP/IP keepalive timer + fires or if teh IOC reboots with the same IP address. This will result in + less UDP search traffic during periods of congestion and also less TCP/IP + circuit thrashing. A side effect will be that if a user turns off a + vxWorks IOC, changes its IP address, and then reboots it, then the user + will need to wait out the full duration of the TCP/IP keepalive timer + before the client will reconnect. This is undoubtedly a negative side + effect, but it is felt that the improvements in robustness justify the + confusion resulting in the small number of situations that a vxWorks + IOC's IP address is changed.
    • +
    • In previous releases if a directory service returns the address of a CA + server that does not have the PV that is being sought then the + client library could end up sending search requests at a very high rate. + This problem has been fixed by placing all disconnected channels in a + queue implementing a short delay prior to there being ready again for + periodic name resolution requests.
    • +
    • In previous R3.14 releases the CA client library's search datagram + interval exponential backoff was flawed. CA's search datagram interval + exponential backoff should proceed following the sequence 30mS, 60mS, + 120mS, 240mS, and so on. Or a similar behavior with higher initial delays + resulting from a round trip time delay estimate greater than 30 mS. + However, instead the backoff delays were 30mS, 30mS, 30mS, 30mS, 30mS, + 30mS, 60mS, 120mS, 240mS, and on. This bug also impacted what CA does + when a channel disconnects or there is a beacon anomaly ( a new server + event). The intent was to start the search delay for disconnected + channels in these situations at 2 seconds but due to the above bug the + delay was more like 64mS. This bug appears only in earlier versions of + EPICS R3.14.
    • +
    • A bug has been found in the CA repeater supplied with EPICS R3.14.2 + through R3.14.4 (inclusive). The symptom will be clients running for more + than a few minuites do not connect to a newly introduced server. Fixed in + R3.14.5.
    • +
    + +

    dbCa

    + +

    Better error messages are now generated.

    + +

    dbCaPutLinkCallback is a new function. It provides the ability to +implement record/driver support that does not complete until a channel access +put callback has completed. See the Application Developer's Guide for +details.

    + +

    dbCaAddLinkCallback is a new function. The caller can provide a connect +and monitor callback. See the Application Developer's Guide for details.

    + +

    devXxSoftCallback

    + +

    Soft device that uses dbCaPutLinkCallback has been written for ao, bo, +calcout, longout, mbbo, mbboDirect, and stringout records. The dbd +definitions have been added to devSoft.dbd. In other to use the new support +the DTYP field is defined:

    +
        field(DTYP,"Async Soft Channel")
    + +

    calcoutRecord

    + +

    The CALC and OCAL fields now have a size of 40 so that they are the same +as the calcRecord.

    + +

    calcoutRecord now has associated device support. The default support will +act just like the old calcout. Support bis also available that uses +dbCaPutLinkCallback.

    + +

    mbbiRecord

    + +

    The fields ZRST,...,FFST are now special(SPC_MOD).

    + +

    mbboRecord

    + +

    The fields ZRST,...,FFST are now special(SPC_MOD). init_record now checks +to see if state strings or values are defined during pass 0. Previously if +another record had a DBR_STRING link to an mbboRecord it thought the field +was a USHORT instead of an ENUM.

    + +

    epicsString

    + +

    A new function has been added epicsStrPrintEscaped, which converts the +standard C escape characters to \xxx characters.

    + +

    IOC shell system command

    + +

    The 'system' command has been added to the IOC shell. To enable this +command, add registrar(iocshSystemCommand) to an application +database description file.

    + +
    +

    Changes between 3.14.3 and 3.14.4

    +
    + +

    sCalcPostfix

    + +

    This has been removed from base.

    + +

    Format string checking

    + +

    'printf-style' functions like errlogPrintf have their arguments verified +against their format string when compiled with gcc.

    + +

    IOC shell command-line editing on vxWorks

    + +

    The IOC shell now uses the vxWorks ledLib routines so command-line editing +is now the same in the IOC shell as it is in the vxWorks shell.

    + +

    CA client library crashes when the same PV name is on multiple +servers

    + +

    If the CA client library was searching for a PV name that was hosted on +more than one server a segmentation violation occurred when printing a +diagnostic message resulting in a failure of the CA client library. The bug +was introduced in R3.14.3. The code was tested on WIN32 prior to release, but +the problem has so far been reproduced only on Linux.

    + +

    Thanks to Ernest Williams at the SNS for discovering and helping to +diagnose the problem.

    + +

    Disconnection callback function called when CA channel known to be +disconnected

    + +

    If a CA circuit timed out during the connect sequence then the CA client +library called the applications's disconnect callback function indicating a +disconnect state transition when the channel was already known to be +disconnected. This has caused the sequencer to improperly maintain its +connected channel count. Other CA client side tools may also be impacted.

    + +

    Recent versions of vxWorks appear to experience a connect failure if the +vxWorks IP kernel reassigns the same ephemeral TCP port number as was +assigned during a previous lifetime. The IP kernel on the vxWorks system +hosting the CA server might have a stale entry for this ephemeral port that +has not yet timed out which prevents the client from connecting with the +ephemeral port assigned by the IP kernel. Eventually, after EPICS_CA_CONN_TMO +seconds, the TCP connect sequence is aborted and the client library closes +the socket, opens a new socket, receives a new ephemeral port assignment, and +successfully connects.

    + +

    Thanks to Mark Rivers for initially reporting the bug and energetically +assisting with identifying the cause.

    + +
    +

    Changes between 3.14.2 and 3.14.3

    +
    + +

    TPRO output

    + +

    The record processing trace output generated when the .TPRO +field of a record is non-zero now includes the name of the thread that is +actually doing the processing.

    + +

    calcRecord and calcoutRecord

    + +

    Previously if a dbGetLink failed on one of the input links, dbGetLink was +not called for the remaining links. Now it is.

    + +

    put notify

    + +

    put notify did not act properly if a record had disp=TRUE, i.e. if puts +are disabled. It now returns putNotifyPutDisabled.

    + +

    fastlock.h

    + +

    This is no longer supported

    + +

    devLib

    + +

    devLib is now supported on vxWorks and RTEMS. It has been moved from +src/vxWorks/src to src/libCom/osi. devLibVirtualOS has been extended to +support allocating A24 addresss and an init method.

    + +

    vxWorks dependent modules moved

    +The following have been moved from src/vxWorks/src to +src/libCom/osi/os/vxWorks: camacLib.h, drvTS.c, drvTS.h, epicsDynLink.c, +epicsDynLink.h, module_types.h, task_params.h, veclist.c. Brief documentation +has been added to the Application Developer's Guide. + +

    Close-On-Exec flag set for all sockets created in EPICS base

    + +

    On POSIX systems if a Channel Access application spawns off 3rd party +software with an exec() call then all open file desriptors are inherited +unless the close-on-exec flag is set for each file descriptor. A new wrapper +function was created so that all sockets created in EPICS base will have the +close-on-exec flag set on POSIX systems. The function which spawns the CA +repeater with exec() used to close all open files except stdin/stdout/stderr. +This step was no longer required and therefore was removed from the code.

    + +

    Temporary Files on Windows

    + +

    The tmpfile() function on windows requires that all temporary files be +stored in the root folder. The antelope (yacc) tool in base was calling +tmpfile() and this was causing problems at sites where win32 systems use +remotely mounted secure file systems. A wrapper function called "FILE * +epicsTempFile()" that creates a temporary file on WIN32 with a name epicsNNN +using one of the following paths was installed into libCom. It searches +starting with (1) below and stops when it finds a specified path that exists. +On POSIX systems, and systems that default to POSIX behavior, epicsTempFile() +simply calls tmpfile().

    +
      +
    1. where the TMP environment variable specifies
    2. +
    3. in c:\tmp
    4. +
    5. in the current working directory
    6. +
    + +

    envPaths file

    + +

    For operating systems other than vxWorks, there is now a target file +created in each iocBoot/ioc directory called +envPaths, which performs the same functions as the +cdCommands file in vxWorks but using environment variables. The +entries in envPaths are derived from the contents of the +application's configure/RELEASE file.

    + +

    Macros in database filenames

    + +

    Database (.db and .dbd) filenames passed to +dbLoadDatabase, dbLoadRecords and inside +dbLoadTemplate substitutions files will now have environment +variable macros expanded before opening. These are expressed using the +standard ${MACRO} syntax. Inside a template substitutions file +the filename must be enclosed in double quotation marks if macros are +used.

    + +

    registerRecordDeviceDriver output subroutine renamed

    + +

    The registration routine generated by the registerRecordDeviceDriver.pl +perl script now includes the name of the application, thus requiring a +one-line change to any IOC startup files produced with earlier R3.14 releases +of base. The actual name is taken from a second command line argument +supplied to the script by the modified make rules, and is derived from the +name of the fully expanded dbd file from which the necessary information is +extracted. The change needed to every startup script involves using this new +name in place of the old registerRecordDeviceDriver. Assuming +that your application's fully expanded dbd file is called +example.dbd you would modify the lines

    + +
    +
    dbLoadDatabase("dbd/example.dbd",0,0)
    +registerRecordDeviceDriver(pdbbase)
    +
    + +

    to become

    + +
    +
    dbLoadDatabase("dbd/example.dbd",0,0)
    +example_registerRecordDeviceDriver(pdbbase)
    +
    + +

    dbExpand -o outfile option

    + +

    A commandline option -o has been added to the dbExpand +program to allow the name of its output file to be specified. If there are +any errors in the input file(s) the output file will not be generated or +modified at all. The rules to expand DBD files have been changed to make use +of this.

    + +

    New keyword variable() supported in dbd files

    + +

    Database definition (.dbd) files can now contain declarations of simple +static variables, a facility intended for driver debugging purposes. These +variable(name) or variable(name,type) declarations +are preserved by dbExpand (type is int if omitted), +and will be converted by registerRecordDeviceDriver.pl into code that +registers them with iocsh. The variables themselves must be defined in some +existing C or C++ code and marked using the macro +epicsExportAddress(type,name). Only plain int and +double types are supported.

    + +

    macEnvExpand

    + +

    A facility for performing macro expansion using environment variables as +macro definitions has been added to libCom/macLib. The ioc shell now performs +macro expansion using this on all input lines (other than comments) before +printing and executing the line.

    + +

    iocsh var command

    + +

    For simple applications such as controlling the value of debugging flags. +Devlopers with more complex expression handling requirements should consider +use of the cexp package. The available variables are defined +by the new variable dbd file keyword.

    + +

    iocshArgPersistentString

    + +

    Tell iocsh to make a copy of the argument string before passing it to the +handler function.

    + +

    epicsStrDup

    + +

    Operating-system independent replacement for strdup().

    + +

    epicsMessageQueue

    + +

    The epicsMessageQueue API has been changed. All functions and methods to +receive a message now have an additional argument which specifies the size of +the receiver buffer. The receive functions/methods return -1 and the received +message is discarded if the received message will not fit in the buffer. See +the Application Developer's Guide (libCom OSI) for details.

    + +

    This is an incompatible change. All R3.14.2 applications which use +epicsMessageQueue must be modified before they can be compiled and used with +R3.14.3

    + +

    Error Message Logging

    + +

    A bug occurring only in Microsoft Windows port of the error message +logging client was fixed. The symptoms were problems getting a Microsoft +Windows based IOC to make entries in the log file.

    + +

    A bug occurring in the error message logging server where a partial +message arrives w/o a <CR> and then a <CR> from a previous +message was found in the input buffer was fixed. The problem must have +existed for a long time but probably was not occurring frequently. The +symptom was garbled output in the log file.

    + +

    An IP kernel deadlock vulnerability occurring when vxWorks's tNetTask +calls logMsg because of a transient mbuf starvation situation has been fixed. +The fix was to not call logFdAdd for the log client's socket and instead +create a specialized vxWorks device driver which calls errlogPrintf for each +incoming message and then call logFdAdd for a file descriptor opened with +that device driver. The desirable functional change being errlogPrintf's +capability to discard messages when it gets behind (because of a transient +mbuf starvation situation). The fix also means that any code that calls +errlogAddListener on vxWorks, e.g. CMLOG, will now receive the logMsg +messages.

    + +

    Channel Access Client Library Client Context Cleanup Race Condition

    + +

    The symptom is a CA client program that fails with a segmentation +violation on Linux shortly after calling ca_task_exit()or +ca_context_destroy(). A fix will show up in R3.14.3. Regression tests were +updated to detect this type of problem.

    + +

    Red Hat 7.3 Linux Process Rundown Bug

    + +

    There appears to be a bug in the Red Hat 7.3 process rundown where posix +thread support is defective when file scope destructors are being run. The +symptom was a hang during process exit. A workaround was installed.

    + +

    Multiple CA Servers on MAC OSX

    + +

    A patch was made to allow multiple CA servers on MAC OSX. OSX is a recent +branch off of BSD and therefore requires socket option SO_REUSEPORT.

    + +
    +

    Changes between 3.14.1 and 3.14.2

    +
    + +

    Build System

    + +

    Major changes have been made to the build system. The good news is that +the rules for support and ioc applications are now greatly simplified. The +bad news is that it does mean changes for existing 3.14.1 applications. +Please see:

    + +

    ConvertingR3.14.1AppsToR3.14.2

    + +

    for details. If you are using the function DBD keyword it +no longer exists. Please read this conversion document for details.

    + +

    Application Developer's Guide

    + +

    The old chapter "New Feature's for 3.14" has been replaced by a new +chapter "Getting Started". Please read it. It provides a simplified set of +rules that can be used to build most support and ioc applications. Many minor +changes have also been made.

    + +

    dbGetLink

    + +

    A bug in dbGetLink resulted in nRequest not being given the value 0 if the +link is a constant link. This in turn caused the waveform record to always +set NORD=NELEM. Thus if an application trys to write a waveform via the +steps:

    +
        prset->get_array_info(paddr,&no_elements,&offset);
    +    write nNew elements into array >>
    +    prset->put_array_info(paddr,nNew);
    + +

    This sets NORD = nNew. But because of the dbGetLink bug, the soft device +support attached to the waveform record sets NORD to NELM.

    + +

    This problem is fixed. The actual bug was in macros in dbAccessDefs.h

    + +

    Access Security

    + +

    The host names are now converted to lower case. This fixes +incompatibilities between various platforms.

    + +

    string records

    + +

    Both the stringin and stringout records have two new DBF_MENU fields: APST +and MPST. These control whether CA monitors are fired if the new VAL field +string is identical to the old one. The default (zero) menu value is "On +Change" with behaviour identical to before, set to "Always" if you want a +record to fire monitors every time the record is processed (analagous to +setting ADEL/MDEL=-1 for numeric record types).

    + +

    epicsMessageQueue

    + +

    A new facility that provides the capabilities of vxWorks msgQLib. See the +Application Developer's Guide (libCom OSI) for details.

    + +

    epicsStdio and errlogPrintf

    + +

    A new facility has been added to libCom described by epicsStdio.h. It +contains the functions epicsSnprintf and epicsVsnprintf. These are like the +C99 functions snprintf and vsnprintf, which are like sprintf and vsprintf +except that they accept a argument limiting the number of characters +written.

    + +

    The errlogPrintf facility has been modified to use this facility. Thus it +is not longer subject to a possible buffer overflow.

    + +

    scanPeriod

    + +

    This is a new function provided by the Database Scanning facility. Given +an index for the choices defined by menuScan.h, it returns the scan period in +seconds. The argument can just be the scan field of a database record. If the +index is not associated with a periodic scan rate, the value 0.0 is +returned.

    + +

    New epicsString.h function

    + +

    A new function epicsStrCaseCmp has been added. It is like strcmp except +that it ignores case.

    + +

    macLib

    + +

    macParseDefns did not check for handle==NULL. The documentation for +macParseDefns was not correct.

    + +
    +

    Changes between 3.14.0beta2 and 3.14.1

    +
    + +

    function - New Database Definition Keyword

    + +

    dbStaticLib and related programs now accept a new keyword in DBD files:

    +
    function(name)
    + +

    Where name is the name of a function with "C" linkage that is +included in the IOC binary. This function will be automatically registered +with the registry at the same time as the record/device/driver tables, and is +intended to make using subroutine records much easier on non-vxWorks +systems.. Prior R3.14 releases required there to be a static registration +routine for such subroutines.

    + +

    dbStaticLib has two additional routines to support this, dbDumpFunction() +and dbWriteFunctionFP(). dbDumpFunction has been added to the iocsh command +table.

    + +

    iocsh

    + +

    When executing commands from a script file, iocsh now echoes each command +to the terminal before execution. This makes it much easier to see where +errors are being reported.

    + +

    Solaris build requirement

    + +

    uname must be defined for builds on solaris hosts because it +is used to determine the solaris version.

    + +

    Linux build note

    + +

    Under linux-x86 only, when SHARED_LIBRARIES=YES it is now possible to have +one or more directory paths burned into products as run-time locations for +the shared libraries. In configure/os/CONFIG_SITE.Common.linux-x86 add any +such absolute paths to the new make variable SHRLIB_SEARCH_DIRS +(lib/<arch> will be automatically appended to each directory given).

    + +

    RULES.Db

    + +

    A *[nn].db file will be created from an +*.template and a *[nn].substitutions file ,where +nn has a value between 0 and 99.

    + +

    Support for 64 bit long

    + +

    Many changes were made to support architectures on which a long is a 64 +bit integer. The basic change was to change:

    +
      +
    • long => epicsInt32 for anything that might get transfered to/from + network buffers
    • +
    • unsigned long => epicsUInt32 for anything that might get transfered + to/from network buffers
    • +
    + +

    The changes include the following:

    +
      +
    • cvtFast
    • +
    • xxxRecord.h NOTE: In addition to the changes for long all enum fields + are now epicsEnum16
    • +
    • dbStaticLib
    • +
    • db_access
    • +
    • dbConvert and dbFastLinkConv
    • +
    + +

    Hardware Link Definitions

    + +

    The various parts of hardware link definitions now accept HEX values, +e.g.

    +
    field(INP,"L0 A1 C0 S0xa @")
    + +

    NOTES:

    +
      +
    • This may not be compatible with Database Configuration Tools
    • +
    • If records are written via dbStaticLib the falues will NOT be written + in HEX.
    • +
    + +

    dbDumpFldDes

    + +

    A macro has been defined so that client code can be written that is +compatible between 3.13 and 3.14.

    + +

    epicsMutex for posix

    +
      +
    • No longer supports epicsMutexLockWithTimeout. This was done to allow a + more efficient posix implementation.
    • +
    • If PTHREAD_MUTEX_RECURSIVE is provided then the implementation uses + only pthread_mutex. This is much faster (2 to 3 times as fast) as the + previous implementation.
    • +
    + +

    Mac OS X

    + +

    Now supported as development platform and as IOC.

    + +

    RTEMS

    + +

    Additional RTEMS-pc386 network drivers are available

    + +

    iocsh

    + +

    Configurable iocsh command-line editing support (none, readline, +libtecla)

    + +

    CA Reference Manual

    + +

    Many additions.

    + +

    CA Client Library

    + +

    Bugs related to connection speed when creating new channels and other +channels are not found fixed. Bugs related to proper schedualing in file +descriptor manager based clients fixed. Many other bugs were fixed. +Performance was significantly improved.

    + +

    Original CA Server Library (still employed in R3.14 by +iocCore)

    + +

    A bug was fixed where the server was in rare situations using excessive +CPU.

    + +

    Portable CA Server LIbrary

    + +

    Several bugs were fixed when performing integration testing with the +channel access gateway.

    + +

    GDD

    + +

    Many bugs and missing features fixed.

    + +
    +

    Changes since beta1

    +
    + +

    dbCommon.dbd

    + +

    Field UDF now has a promptgroup. This allows users to set UDF false via +DCTs.

    + +

    errlog

    + +

    errlog no longer contains an atexit that calls errlogFlush. This did not +work on all operating systems. cantProceed, iocsh, and ca_task_exit all call +errlogFlush. Other applications may also have to call if before +terminating.

    + +

    mbboRecord

    + +

    mbboRecord now implements method cvt_dbaddr for the VAL field. If no state +vales or state strings are defined then it sets field_type and dbr_field_type +to DBF_USHORT.

    + +

    timeStamp changes

    + +

    Changes have been made to:

    +
      +
    • Allow device support to set the time stamp (field TIME) of a + record.
    • +
    • Allow a record to receive it's time stamp from another record,
    • +
    + +

    epicsTime.h now has the definitions:

    +
    #define epicsTimeEventBestTime -1
    +#define epicsTimeEventDeviceTime -2
    + +

    These are values for the TSE field of dbCommon.

    +
      +
    • epicsTimeEventBestTime means that code supplying the + time stamp should get the most accurate time possible. Currently this + only has meaning on vxWorks and if drvTS is supplying the time via some + hardware timing system. It means get the latest time from the hardware + system rather than from the vxWorks tick time. drvTs previously accepted + a hardcoded value of -1.
    • +
    • epicsTimeEventDeviceTime means that recGblGetTimeStamp + doesn't modify the time field. This allows device support to supply the + time stamp.
    • +
    + +

    If the TSEL field refers to the TIME +field of a record then recGblGetTimeStamp sets +TIME equal to the time it gets from the record the +TSEL references. This works for both database and channel +access links. In this case field TSE is not used.

    +
     
    + +

    aiRecord and aoRecord: Setting eoff=egul

    + +

    Instead of init_record executing code like

    +
        if ((pai->linr == menuConvertLINEAR) && pdset->special_linconv) {
    +        pai->eoff = pai->egul;
    +    }
    + +

    It now executes:

    +
        if ((pai->eslo==1.0) && (pai->eoff==0.0)) {
    +        pai->eoff = pai->egul;
    +    }
    + +

    aoRecord has a similar change

    + +

    This was done so that old device support which does not implement +special_linconv still works.

    + +

    CA puts to disabled record

    + +

    If a CA client issues a put to a disabled record then, when the record is +ena bled, database puts to the record will not make the record process until +a CA pu t is again issued. This is fixed.

    + +

    TPRO - trace processing

    + +

    If dbProcess is called recursively by different tasks, it did not properly +handle TPRO. Consider the following database:

    +
    record(ao,"mrkao") {
    +    field(OUT,"mrkai CA")
    +    field(TPRO,"1")
    +}
    +record(ai,"mrkai") {
    +    field(TPRO,"1")
    +}
    + +

    If a channel access put is sent to mrkao, no message is issued when mrkai +is processed.

    + +

    This is now fixed.

    + +

    TSconfigure

    + +

    If in your st.cmd file you issue the command.

    + +

    TSconfigure(0,0,0,0,0,0,1)

    + +

    And set the TSE field of any record to a non zero value, then a crash will +occur when recGblGetTimeStamp is called.

    + +

    This is now fixed.

    + +

    calcoutRecord

    + +

    nsev not sevr must be checked to decide if dbPutLink should be called.

    + +

    dbCa

    + +

    Whenever a connection is made, a request to retrieve the control, display, +and alarm linits and the precision and units is automatically issued. +Previously this was only done if dbCaGetAttributes was called. This it is no +longer necessary to call dbCaGetAttributes.

    + +

    calcPerform

    + +

    This now returns a non zero value if the result is nan (not a number).

    + +

    Record Name Length

    + +

    The size of the name field has been expanded from 29 to 61, i.e. record +names can now have 60 characters.

    + +

    iocInit

    + +

    initialProcess is now called before interruptAccept. This means that +initial processing will be done before periodically scanned and I/O Inter +scanned records start processing.

    + +

    ellLib

    + +

    Casts have been removed that suppressed valuable error messages

    + +

    mbbiRecord

    + +

    All existing manipulations of UDF in process() are removed and udf is set +FALSE when the raw value is successfully read.

    + +

    selRecord

    + +

    In do_sel udf is not set false at the beginning. If selm has an invalid +value recGblSetSevr(psel,SOFT_ALARM,MAJOR_ALARM) is called.

    + +

    cdCommands file

    + +

    Fixed a bug and revised the use of the IOCS_APPL_TOP setting in an +application's <top>/configure/CONFIG file (which specifies the path to +<top> as seen by the IOC) to apply the same modifications to all paths +output in the cdCommands file.

    + +

    dbStaticLib

    + +

    All routines with Recdes of Fielddes in their name are obsolete and +removed. A new routine dbDumpField replaces dbDumpFldDes.

    + +
    +

    Changes since alpha2

    +
    +All changes for release 3.13.5 that also apply to 3.14 have been made. + +

    devAiSoftRaw and devAoSoftRaw

    + +

    A new state is defined for the LINR field. The name is "SLOPE", which +allows any device type to be used with manual settings of the EOFF and ESLO +fields. With this setting, the device support's special_linconv() routine is +only called when LINR=LINEAR.

    + +

    The RTEMS TFTP remote filesystem driver now supports a limited form of the +chdir() system call. One restriction is that all pathnames passed to chdir() +must end in a / character, so IOC shell commands to change directories must +be given as

    + +

    cd ../db/

    + +
    +

    EPICS Release base 3.14.0alpha2

    +
    +Since the alpha1 release some major changes were made to the build system, to +some of the libCom facilities, and to the iocsh facilities. + +

    The unbundled version of the sequencer has been build and tested with this +release. You must obtain a version of the sequencer that has been built +against alpha2.

    + +

    A verion of the HPlanGpib support has been built and tested with this +release. Again you must obtain a version that builds with alpha2.

    + +

    A new update to the Application Developer's Guide is available for this +release.

    + +

    Build changes

    +
      +
    • Operating system independant builds are now done in an O.Common + subdirectory and then installed instead of being performed directly in an + install directory.
    • +
    • Build definition names (e.g. RECTYPES, MENUS, DBDNAME, and BPTS) have + been changed to specify the name of the file to be created and installed + instead of the source file name.
    • +
    • All db and dbd related definitions and rules have been moved into + base/configure/RULES.Db file. The rules now allow multiple dbd files and + registerRecordDeviceDriver files to be created in a single Makefile.
    • +
    • "gnumake depends" no longer depends on a complete buildInstall.
    • +
    + +

    Converting alpha1 applications to alpha2

    +Build modifications in alpha2 require the following changes to existing R3.14 +applications. +
      +
    • Remove the now unused RULES files + +
      + ./configure/RULES.Db
      + ./configure/RULES.registerRecordDeviceDriver
      +
    • +
    • Delete the following line in ./configure/RULES + +
      + include $(TOP)/configure/RULES.registerRecordDeviceDriver
      +
    • +
    • In <top>/configure/Makefile change + +
      + @$(PERL) $(TOOLS)/makeConfigAppInclude.pl $(T_A) $@ $(TOP)
      + to + +
      + @$(PERL) $(TOOLS)/makeConfigAppInclude.pl $(EPICS_HOST_ARCH) $(T_A) $@ + $(TOP)
      + and add the line + +
      + depends: install
      + to the bottom of the Makefile.
    • +
    • In all *App/*Db/Makefiles change + +
      + include $(TOP)/configure/RULES.Db
      + to + +
      + include $(TOP)/configure/RULES
      +
    • +
    • In all *App/src/Makefile files change + +
      + DBDNAME = <name>App
      + to + +
      + DBD += <name>
      + and remove the line
      + + +
      + DBDEXPAND = <name>Include.dbd
      + NOTE: If any of your *App/*Db/Makefiles contain "DBDNAME =" lines you + should make these same changes in that *Db dirctory.
    • +
    • In all *App/src/Makefile Makefiles change + +
      + RECTYPES=<name>.h
      + to + +
      + DBDINC+=<name>
      + change + +
      + MENUS=<name>.h
      + to + +
      + DBDINC+=<name>
      + change + +
      + BPTS
      + to + +
      + DBD
      + change + +
      + INSTALLDB
      + to + +
      + DB
      + change + +
      + DBDINSTALL
      + to + +
      + DBD
      +
    • +
    • In all example *App/src/Makefile files change + +
      + example_SRCS_DEFAULT += registerRecordDeviceDriver.c
      + to + +
      + example_SRCS_DEFAULT += + <name>_registerRecordDeviceDriver.cpp
      + where <name> is the base name of a <name>.dbd file which was + created from a <name>Include.dbd file and which will be loaded in a + st.cmd or stcmd.host script (e.g. example).
    • +
    • In ./iocBoot/ioc<name>/st.cmd files change + +
      + dbLoadDatabase("dbd/exampleApp.dbd")
      + to + +
      + dbLoadDatabase("dbd/example.dbd")
      +
    • +
    • In <top>/iocBoot/ioc<name>/stcmd.host files change + +
      + dbLoadDatabase("../../dbd/exampleApp.dbd",0,0)
      + to + +
      + dbLoadDatabase("../../dbd/example.dbd",0,0)
      +
    • +
    + +

    EPICS_HOST_ARCH changes

    + +

    GNU compiler builds are now determined by the value of EPICS_HOST_ARCH and +are no longer specified in CONFIG_SITE. All references to the ANSI (ACC/GCC) +and CPLUSPLUS (CCC/G++) macros have been removed.

    + +

    libCom

    + +

    Most of the library routines and files starting with the prefix osi have +been changed to start with epics. Several also had major changes to their +user interface. See the latest version of the Application Developer's Guide +for details.

    + +
    +

    EPICS Release base 3.14.0alpha1 Notes

    +
    + +


    +

    + +

    This is the first release of 3.14. This is the first release that supports +iocCore on platforms besides vxWorks.

    + +

    iocCore is now supported on the following platforms:

    +
      +
    • vxWorks + +
      + Tornado II is required.
      +
    • +
    • RTEMS + +
      + An open source real time operating system. It has been tested on + MVME167 and MC68360 processors. RTEMS also supports + powerPC.
      +
    • +
    • solaris + +
      + Has been tested on solaris 2.6 and solaris 8 with Sun workshop 6.0 (C++ + 5.2). Sun workshop 5.0 (C++ 5.0) will not compile this version of + EPICS.
      +
    • +
    • Linux + +
      + Has been tested on Redhat x86 platforms.
      +
    • +
    • winNT + +
      + Testing has been done with visual C++ 6.0.
      +
    • +
    + +

    A new version of the Application Developers Guide is available. The +following gives links to the new Application Developer's Guide and to RTEMS +information.

    + +
    + http://www.aps.anl.gov/epics/base/R3-14/index.php
    + +

    Most of the Application Developer's Guide has only minor changes. The +following are new.

    +
      +
    • Chapter 2 describes the new features for 3.14.
    • +
    • Chapter 4 describes the build facility for 3.14
    • +
    • Chapters 19 and 20 describe libCom, which was not previously + documented.
    • +
    + +


    +It must be emphasized that this is an alpha release.

    +
      +
    • Please don't use it for existing operational systems
    • +
    • Don't build your operational CA clients with it.
    • +
    • The APIs for new components in libCom are still evolving so if you use + them be prepared for changes.
    • +
    • HPUX - No support currently because we could not find good support for + multithreading.
    • +
    + +

    Building Applications

    +
      +
    • For new applications see Chapters 2 (New Features) and Chapter 4 (Build + Facility) of the Application Developer's Guide.
    • +
    • For existing applications the old config rules are still supported. + Some changes, however, are needed. Documentation is being prepared and + will appear in these release notes sooon.
    • +
    + + diff --git a/documentation/ReleaseChecklist.html b/documentation/ReleaseChecklist.html new file mode 100644 index 000000000..22d01f354 --- /dev/null +++ b/documentation/ReleaseChecklist.html @@ -0,0 +1,382 @@ + + + + + + EPICS Release Procedures & Checklist + + + + +

    EPICS Base Release Procedures & Checklist

    + +
    +

    Date: Wed 2010-10-06 12:17:25 -0500
    + Authors: Andrew Johnson <anj@aps.anl.gov>
    + Revision-Id: anj@aps.anl.gov-20101006171725-nn6bobfy6jqqa3of

    +
    + +

    This document describes the procedures and provides a checklist of tasks +that should be performed when creating new releases of EPICS Base.

    + +

    The Release Process

    + +

    The version released on the Feature Freeze date is designated the first +pre-release, -pre1. The first Release Candidate -rc1 is the +first version that has undergone widespread testing and which has no known +problems in it that are slated to be fixed in this release. New versions should +be made at about weekly intervals during the testing and debugging period, and +will be designated as either pre-release versions or Release Candidates by the +Release Manager. A Release Candidate that has received widespread testing for a +week without any additional problems being discovered or significant changes +being committed can be designated as the final release version.

    + +

    Roles

    + +

    The following roles are required. The individuals named here have have been +responsible for these tasks in the past and are expected to continue in the +relevent roles unless the Release Manager designates otherwise:

    + +
    +
    Release Manager (Andrew Johnson)
    +
    Responsible for the release
    +
    Configuration Manager (Janet Anderson)
    +
    Responsible for committing version number updates and for + creating tarfiles
    +
    Platform Developers
    +
    Responsible for individual operating system platforms
    +
    Application Developers
    +
    Responsible for support modules that depend on EPICS Base.
    +
    Website Manager (Andrew Johnson)
    +
    Responsible for the EPICS website
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CheckWhoDescription
    Preparing for a release
     Release ManagerEmail all developers about the upcoming release and ask for a list + of remaining jobs that must be finished.
     All developersCheck the bug tracker for any outstanding items and handle + appropriately. All bugs that have been fixed should have been marked + as Fix Committed.
     Release ManagerSet the Feature Freeze date, by which time all Bazaar commits for + enhancements and new functionality should have been completed. After + this date, commits should only be made to fix problems that show up + during testing.
     Release Manager
    + & all developers
    Ensure that documentation will be updated before the release date: +
      +
    • Application Developers Guide
    • +
    • Release Notes
    • +
    • Known Problems
    • +
    • Other documents
    • +
    +
     Release ManagerReview and update this document for the upcoming release.
     Website ManagerCreate a release milestone on Launchpad if necessary and set the + expected release date. Note that pre-release and release-candidate + versions do not appear on Launchpad, only the final release.
    Creating pre-release and release-candidate versions
     Configuration ManagerEdit and commit changes to the EPICS version number file + configure/CONFIG_BASE_VERSION.
     Configuration ManagerTag the module in Bazaar, using these tag conventions: +
      +
    • + R3.14.12-pren + — pre-release tag +
    • +
    • + R3.14.12-rcn + — release candidate tag, note the rc is now + lower-case
    • +
    +
    + cd ~/base/mirror-3.14
    + bzr tag R3.14.12-rcn +
    +
     Configuration ManagerExport the tagged version into a tarfile. Note that this command + generates a gzipped tarfile directly from the repository: +
    + cd ~/base
    + bzr export --keywords=publish + --root=base-3.14.12-rcn + -r tag:R3.14.12-rcn + baseR3.14.12-rcn.tar.gz + mirror-3.14 +
    + This requires that the Bazaar keywords plugin is installed and + configured properly. +
     Configuration ManagerTest the tarfile by extracting its contents and building it on at + least one supported platform.
     Configuration ManagerCopy the tar file to the Base download area of the website.
     Website ManagerCreate or update a website subdirectory to hold the release + documentation, and copy in selected files from the base/documentation + and base/html directories of the tarfile.
     Website ManagerCreate or modify the webpage for the new release with links to the + release documents and tar file. Pre-release and release-candidate + versions should use the page and URL for the final release version + number.
     Website ManagerAdd the new tar file to the website Base download index page.
    Testing
     Platform DevelopersRun the built-in test programs on all available host platforms using +
    + make -s runtests +
     Platform DevelopersRun the CA client side regression tests on all available host + platforms.
     Platform DevelopersBuild and run the + epics-base-tests + suite on all available platforms.
     Platform DevelopersCheck that all makeBaseApp templates build and run properly, all + xxxApp and xxxBoot types and any internal options, e.g. + setting STATIC_BUILD=YES or using a different + INSTALL_LOCATION in configure/CONFIG_SITE.
     Platform DevelopersBuild the SNL Sequencer against this version of Base, and check that + the makeBaseApp example builds and runs correctly with it.
     Application DevelopersBuild external applications against this version of Base on all + available platforms and test as appropriate. Application code changes + may be necessary where the EPICS Base APIs have been modified.
     Release ManagerCheck that documentation has been updated: + +
    Release Approval
     Release ManagerObtain a positive Ok to release from all platform developers + once a Release Candidate version has gone a whole week without any + issues being reported.
    Creating the final release version
     Configuration ManagerEdit and commit changes to the EPICS version number file + configure/CONFIG_BASE_VERSION.
     Configuration ManagerTag the module in Bazaar: +
    + cd ~/base/mirror-3.14
    + bzr tag R3.14.12 +
    +
     Configuration ManagerExport the tagged version into a tarfile. Note that this command + generates a gzipped tarfile directly from the repository: +
    + cd ~/base
    + bzr export --keywords=publish + --root=base-3.14.12 + -r tag:R3.14.12 + baseR3.14.12.tar.gz + mirror-3.14 +
    + This requires that the Bazaar keywords plugin is installed and + configured properly. +
     Configuration ManagerTest the tar file by extracting its contents and building it on at + least one supported platform
    Publish and Announce it
     Configuration ManagerCopy the tar file to the Base download area of the website
     Website ManagerUpdate the website subdirectory that holds the release + documentation, and copy in the files from the base/documentation + directory of the tarfile.
     Website ManagerUpdate the webpage for the new release with links to the release + documents and tar file.
     Website ManagerAdd the new release tar file to the website Base download index + page.
     Website ManagerLink to the release webpage from other relevent areas of the + website - update front page and sidebars.
     Website ManagerUpload the release tar file to the Launchpad download area.
     Website ManagerAdd the new Version number to the Launchpad bug tracker.
     Website ManagerFind all Launchpad bug reports with the status Fix Committed which + have been fixed in this release and mark them Fix Released.
     Release ManagerEmail tech-talk
     Website ManagerAdd an entry to the website News page, linking to the new version + webpage.
    + + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000..78841d5a9 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,98 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# +# Revision-Id: anj@aps.anl.gov-20101012153723-0ft6f95pnz93ivxw +# + +TOP = .. +include $(TOP)/configure/CONFIG + +DIRS += tools + +DIRS += makeBaseApp +makeBaseApp_DEPEND_DIRS = tools + +DIRS += makeBaseExt +makeBaseExt_DEPEND_DIRS = tools + +DIRS += libCom +libCom_DEPEND_DIRS = tools + +DIRS += toolsComm +toolsComm_DEPEND_DIRS = libCom + +DIRS += ca +ca_DEPEND_DIRS = libCom + +DIRS += dbStatic +dbStatic_DEPEND_DIRS = toolsComm + +DIRS += registry +registry_DEPEND_DIRS = dbStatic + +DIRS += bpt +bpt_DEPEND_DIRS = dbStatic + +DIRS += db +db_DEPEND_DIRS = bpt ca + +DIRS += as +as_DEPEND_DIRS = db + +DIRS += util +util_DEPEND_DIRS = ca + +DIRS += dbtools +dbtools_DEPEND_DIRS = db + +DIRS += catools +catools_DEPEND_DIRS = ca dbStatic + +DIRS += rsrv +rsrv_DEPEND_DIRS = as + +DIRS += rec +rec_DEPEND_DIRS = as registry + +DIRS += misc +misc_DEPEND_DIRS = dbtools rsrv rec + +DIRS += dev +dev_DEPEND_DIRS = rec misc + +DIRS += vxWorks +vxWorks_DEPEND_DIRS = registry misc dbtools + +DIRS += RTEMS +RTEMS_DEPEND_DIRS = libCom + +DIRS += libCom/test +libCom/test_DEPEND_DIRS = ca RTEMS + +DIRS += db/test +db/test_DEPEND_DIRS = db RTEMS + +DIRS += softIoc +softIoc_DEPEND_DIRS = dev dbtools RTEMS + +DIRS += gdd +gdd_DEPEND_DIRS = ca + +DIRS += cas +cas_DEPEND_DIRS = gdd dbStatic + +DIRS += excas +excas_DEPEND_DIRS = cas as registry + +DIRS += cap5 +cap5_DEPEND_DIRS = ca dbStatic + + +include $(TOP)/configure/RULES_DIRS + diff --git a/src/RTEMS/Makefile b/src/RTEMS/Makefile new file mode 100644 index 000000000..85c177861 --- /dev/null +++ b/src/RTEMS/Makefile @@ -0,0 +1,21 @@ +#************************************************************************* +# 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 +# + +TOP = ../.. + +include $(TOP)/configure/CONFIG + +DIRS += base + +include $(TOP)/configure/RULES_DIRS + diff --git a/src/RTEMS/base/Makefile b/src/RTEMS/base/Makefile new file mode 100644 index 000000000..98d24509f --- /dev/null +++ b/src/RTEMS/base/Makefile @@ -0,0 +1,26 @@ +#************************************************************************* +# 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 + +INC += epicsRtemsInitHooks.h + +SRCS += rtems_init.c +SRCS += rtems_config.c +SRCS += rtems_netconfig.c +SRCS += rtems_util.c +SRCS += setBootConfigFromNVRAM.c +SRCS += epicsRtemsInitHookPre.c +SRCS += epicsRtemsInitHookPost.c + +LIBRARY_RTEMS = rtemsCom +LIBRARY_SRCS = $(SRCS) $(BUILD_ARCHS) + +include $(TOP)/configure/RULES diff --git a/src/RTEMS/base/epicsRtemsInitHookPost.c b/src/RTEMS/base/epicsRtemsInitHookPost.c new file mode 100644 index 000000000..7b6a54d27 --- /dev/null +++ b/src/RTEMS/base/epicsRtemsInitHookPost.c @@ -0,0 +1,19 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* 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. +\*************************************************************************/ + +/* + * Dummy version -- use if application does not provide its own version + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ +#include "epicsRtemsInitHooks.h" + +int +epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config) +{ + return 0; +} diff --git a/src/RTEMS/base/epicsRtemsInitHookPre.c b/src/RTEMS/base/epicsRtemsInitHookPre.c new file mode 100644 index 000000000..9cccc52e7 --- /dev/null +++ b/src/RTEMS/base/epicsRtemsInitHookPre.c @@ -0,0 +1,19 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* 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. +\*************************************************************************/ + +/* + * Dummy version -- use if application does not provide its own version + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ +#include "epicsRtemsInitHooks.h" + +int +epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config) +{ + return 0; +} diff --git a/src/RTEMS/base/epicsRtemsInitHooks.h b/src/RTEMS/base/epicsRtemsInitHooks.h new file mode 100644 index 000000000..8627a14e8 --- /dev/null +++ b/src/RTEMS/base/epicsRtemsInitHooks.h @@ -0,0 +1,24 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* 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. +\*************************************************************************/ + +/* + * Hooks into RTEMS startup code + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ +#include +#include + +extern char *env_nfsServer; +extern char *env_nfsPath; +extern char *env_nfsMountPoint; + +/* + * Return 0 for success, non-zero for failure (will cause panic) + */ +int epicsRtemsInitPreSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); +int epicsRtemsInitPostSetBootConfigFromNVRAM(struct rtems_bsdnet_config *config); diff --git a/src/RTEMS/base/rtems_config.c b/src/RTEMS/base/rtems_config.c new file mode 100644 index 000000000..ac3059fad --- /dev/null +++ b/src/RTEMS/base/rtems_config.c @@ -0,0 +1,72 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS configuration for EPICS + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * norume@aps.anl.gov + * (630) 252-4793 + */ + +#include + +/* + *********************************************************************** + * RTEMS CONFIGURATION * + *********************************************************************** + */ +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#if __RTEMS_MAJOR__>4 || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) +# define CONFIGURE_UNIFIED_WORK_AREAS +#else +# define CONFIGURE_EXECUTIVE_RAM_SIZE (2000*1024) +#endif + +#define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30) +#define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(500) +#define CONFIGURE_MAXIMUM_TIMERS rtems_resource_unlimited(20) +#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES rtems_resource_unlimited(5) +#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1 + +#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 150 +#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM +#define CONFIGURE_MAXIMUM_DRIVERS 8 + +#define CONFIGURE_MICROSECONDS_PER_TICK 20000 + +#define CONFIGURE_INIT_TASK_PRIORITY 80 + +#define CONFIGURE_MALLOC_STATISTICS 1 + +#define CONFIGURE_INIT +#define CONFIGURE_INIT_TASK_INITIAL_MODES (RTEMS_PREEMPT | \ + RTEMS_NO_TIMESLICE | \ + RTEMS_NO_ASR | \ + RTEMS_INTERRUPT_LEVEL(0)) +#define CONFIGURE_INIT_TASK_ATTRIBUTES (RTEMS_FLOATING_POINT | RTEMS_LOCAL) +#define CONFIGURE_INIT_TASK_STACK_SIZE (16*1024) +rtems_task Init (rtems_task_argument argument); + +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER + +#define CONFIGURE_FILESYSTEM_NFS +#define CONFIGURE_FILESYSTEM_IMFS + +/* + * This should be made BSP dependent, not CPU dependent but I know of no + * appropriate conditionals to use. + * The new general time support makes including the RTC driverr less important. + */ +#if !defined(mpc604) && !defined(__mc68040__) && !defined(__mcf5200__) && !defined(mpc7455) && !defined(__arm__) /* don't have RTC code */ +#define CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER +#endif + + +#include +#include diff --git a/src/RTEMS/base/rtems_init.c b/src/RTEMS/base/rtems_init.c new file mode 100644 index 000000000..16280e300 --- /dev/null +++ b/src/RTEMS/base/rtems_init.c @@ -0,0 +1,571 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS startup task for EPICS + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric.norum@usask.ca + * (306) 966-5394 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsTime.h" +#include "epicsExit.h" +#include "envDefs.h" +#include "errlog.h" +#include "logClient.h" +#include "osiUnistd.h" +#include "iocsh.h" +#include "osdTime.h" + +#include "epicsRtemsInitHooks.h" + +/* + * Prototypes for some functions not in header files + */ +void tzset(void); +int fileno(FILE *); +int main(int argc, char **argv); + +static void +logReset (void) +{ + void rtems_bsp_reset_cause(char *buf, size_t capacity) __attribute__((weak)); + void (*fp)(char *buf, size_t capacity) = rtems_bsp_reset_cause; + + if (fp) { + char buf[80]; + fp(buf, sizeof buf); + errlogPrintf ("Startup after %s.\n", buf); + } + else { + errlogPrintf ("Startup.\n"); + } +} + +/* + *********************************************************************** + * FATAL ERROR REPORTING * + *********************************************************************** + */ +/* + * Delay for a while, then terminate + */ +static void +delayedPanic (const char *msg) +{ + extern rtems_interval rtemsTicksPerSecond; + + rtems_task_wake_after (rtemsTicksPerSecond); + rtems_panic (msg); +} + +/* + * Log error and terminate + */ +void +LogFatal (const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + errlogVprintf (msg, ap); + va_end (ap); + delayedPanic (msg); +} + +/* + * Log RTEMS error and terminate + */ +void +LogRtemsFatal (const char *msg, rtems_status_code sc) +{ + errlogPrintf ("%s: %s\n", msg, rtems_status_text (sc)); + delayedPanic (msg); +} + +/* + * Log network error and terminate + */ +void +LogNetFatal (const char *msg, int err) +{ + errlogPrintf ("%s: %d\n", msg, err); + delayedPanic (msg); +} + +void * +mustMalloc(int size, const char *msg) +{ + void *p; + + if ((p = malloc (size)) == NULL) + LogFatal ("Can't allocate space for %s.\n", msg); + return p; +} + +/* + *********************************************************************** + * REMOTE FILE ACCESS * + *********************************************************************** + */ +#ifdef OMIT_NFS_SUPPORT +# include +#endif + +static int +initialize_local_filesystem(char **argv) +{ + extern char _DownloadLocation[] __attribute__((weak)); + extern char _FlashBase[] __attribute__((weak)); + extern char _FlashSize[] __attribute__((weak)); + + argv[0] = rtems_bsdnet_bootp_boot_file_name; + if (_FlashSize && (_DownloadLocation || _FlashBase)) { + extern char _edata[]; + size_t flashIndex = _edata - _DownloadLocation; + char *header = _FlashBase + flashIndex; + + if (memcmp(header + 257, "ustar ", 8) == 0) { + int fd; + printf ("***** Unpack in-memory file system (IMFS) *****\n"); + if (rtems_tarfs_load("/", (unsigned char *)header, (size_t)_FlashSize - flashIndex) != 0) { + printf("Can't unpack tar filesystem\n"); + return 0; + } + if ((fd = open(rtems_bsdnet_bootp_cmdline, 0)) >= 0) { + close(fd); + printf ("***** Found startup script (%s) in IMFS *****\n", rtems_bsdnet_bootp_cmdline); + argv[1] = rtems_bsdnet_bootp_cmdline; + return 1; + } + printf ("***** Startup script (%s) not in IMFS *****\n", rtems_bsdnet_bootp_cmdline); + } + } + return 0; +} + +#ifndef OMIT_NFS_SUPPORT +#if __RTEMS_MAJOR__>4 || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || \ + (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) +static int +nfsMount(char *uidhost, char *path, char *mntpoint) +{ + int devl = strlen(uidhost) + strlen(path) + 2; + char *dev; + int rval = -1; + + if ((dev = malloc(devl)) == NULL) { + fprintf(stderr,"nfsMount: out of memory\n"); + return -1; + } + sprintf(dev, "%s:%s", uidhost, path); + printf("Mount %s on %s\n", dev, mntpoint); + if (rtems_mkdir(mntpoint, S_IRWXU | S_IRWXG | S_IRWXO)) + printf("Warning -- unable to make directory \"%s\"\n", mntpoint); + if (mount(dev, mntpoint, RTEMS_FILESYSTEM_TYPE_NFS, + RTEMS_FILESYSTEM_READ_WRITE, NULL)) { + perror("mount failed"); + } + else { + rval = 0; + } + free(dev); + return rval; +} +#define NFS_INIT +#else +#define NFS_INIT rpcUdpInit(); nfsInit(0,0); +#endif +#endif + +static void +initialize_remote_filesystem(char **argv, int hasLocalFilesystem) +{ +#ifdef OMIT_NFS_SUPPORT + printf ("***** Initializing TFTP *****\n"); + rtems_bsdnet_initialize_tftp_filesystem (); + if (!hasLocalFilesystem) { + char *path; + int pathsize = 200; + int l; + + path = mustMalloc(pathsize, "Command path name "); + strcpy (path, "/TFTP/BOOTP_HOST/epics/"); + l = strlen (path); + if (gethostname (&path[l], pathsize - l - 10) || (path[l] == '\0')) + { + LogFatal ("Can't get host name"); + } + strcat (path, "/st.cmd"); + argv[1] = path; + } +#else + char *server_name; + char *server_path; + char *mount_point; + char *cp; + int l = 0; + + printf ("***** Initializing NFS *****\n"); + NFS_INIT + if (env_nfsServer && env_nfsPath && env_nfsMountPoint) { + server_name = env_nfsServer; + server_path = env_nfsPath; + mount_point = env_nfsMountPoint; + cp = mount_point; + while ((cp = strchr(cp+1, '/')) != NULL) { + *cp = '\0'; + if ((mkdir (mount_point, 0755) != 0) + && (errno != EEXIST)) + LogFatal("Can't create directory \"%s\": %s.\n", + mount_point, strerror(errno)); + *cp = '/'; + } + argv[1] = rtems_bsdnet_bootp_cmdline; + } + else if (hasLocalFilesystem) { + return; + } + else { + /* + * Use first component of nvram/bootp command line pathname + * to set up initial NFS mount. A "/tftpboot/" is prepended + * if the pathname does not begin with a '/'. This allows + * NFS and TFTP to have a similar view of the remote system. + */ + if (rtems_bsdnet_bootp_cmdline[0] == '/') + cp = rtems_bsdnet_bootp_cmdline + 1; + else + cp = rtems_bsdnet_bootp_cmdline; + cp = strchr(cp, '/'); + if ((cp == NULL) + || ((l = cp - rtems_bsdnet_bootp_cmdline) == 0)) + LogFatal("\"%s\" is not a valid command pathname.\n", rtems_bsdnet_bootp_cmdline); + cp = mustMalloc(l + 20, "NFS mount paths"); + server_path = cp; + if (rtems_bsdnet_bootp_cmdline[0] == '/') { + mount_point = server_path; + strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l); + mount_point[l] = '\0'; + argv[1] = rtems_bsdnet_bootp_cmdline; + } + else { + char *abspath = mustMalloc(strlen(rtems_bsdnet_bootp_cmdline)+2,"Absolute command path"); + strcpy(server_path, "/tftpboot/"); + mount_point = server_path + strlen(server_path); + strncpy(mount_point, rtems_bsdnet_bootp_cmdline, l); + mount_point[l] = '\0'; + mount_point--; + strcpy(abspath, "/"); + strcat(abspath, rtems_bsdnet_bootp_cmdline); + argv[1] = abspath; + } + server_name = rtems_bsdnet_bootp_server_name; + } + nfsMount(server_name, server_path, mount_point); +#endif +} + +/* + * Get to the startup script directory + * The TFTP filesystem requires a trailing '/' on chdir arguments. + */ +static void +set_directory (const char *commandline) +{ + const char *cp; + char *directoryPath; + int l; + + cp = strrchr(commandline, '/'); + if (cp == NULL) { + l = 0; + cp = "/"; + } + else { + l = cp - commandline; + cp = commandline; + } + directoryPath = mustMalloc(l + 2, "Command path directory "); + strncpy(directoryPath, cp, l); + directoryPath[l] = '/'; + directoryPath[l+1] = '\0'; + if (chdir (directoryPath) < 0) + LogFatal ("Can't set initial directory(%s): %s\n", directoryPath, strerror(errno)); + free(directoryPath); +} + +/* + *********************************************************************** + * RTEMS/EPICS COMMANDS * + *********************************************************************** + */ +/* + * RTEMS status + */ +static void +rtems_netstat (unsigned int level) +{ + rtems_bsdnet_show_if_stats (); + rtems_bsdnet_show_mbuf_stats (); + if (level >= 1) { + rtems_bsdnet_show_inet_routes (); + } + if (level >= 2) { + rtems_bsdnet_show_ip_stats (); + rtems_bsdnet_show_icmp_stats (); + rtems_bsdnet_show_udp_stats (); + rtems_bsdnet_show_tcp_stats (); + } +} + +static const iocshArg netStatArg0 = { "level",iocshArgInt}; +static const iocshArg * const netStatArgs[1] = {&netStatArg0}; +static const iocshFuncDef netStatFuncDef = {"netstat",1,netStatArgs}; +static void netStatCallFunc(const iocshArgBuf *args) +{ + rtems_netstat(args[0].ival); +} + +static const iocshFuncDef heapSpaceFuncDef = {"heapSpace",0,NULL}; +static void heapSpaceCallFunc(const iocshArgBuf *args) +{ + rtems_malloc_statistics_t s; + double x; + + malloc_get_statistics(&s); + x = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed); + if (x >= 1024*1024) + printf("Heap space: %.1f MB\n", x / (1024 * 1024)); + else + printf("Heap space: %.1f kB\n", x / 1024); +} + +#ifndef OMIT_NFS_SUPPORT +static const iocshArg nfsMountArg0 = { "[uid.gid@]host",iocshArgString}; +static const iocshArg nfsMountArg1 = { "server path",iocshArgString}; +static const iocshArg nfsMountArg2 = { "mount point",iocshArgString}; +static const iocshArg * const nfsMountArgs[3] = {&nfsMountArg0,&nfsMountArg1, + &nfsMountArg2}; +static const iocshFuncDef nfsMountFuncDef = {"nfsMount",3,nfsMountArgs}; +static void nfsMountCallFunc(const iocshArgBuf *args) +{ + char *cp = args[2].sval; + while ((cp = strchr(cp+1, '/')) != NULL) { + *cp = '\0'; + if ((mkdir (args[2].sval, 0755) != 0) && (errno != EEXIST)) { + printf("Can't create directory \"%s\": %s.\n", + args[2].sval, strerror(errno)); + return; + } + *cp = '/'; + } + nfsMount(args[0].sval, args[1].sval, args[2].sval); +} +#endif + +/* + * Register RTEMS-specific commands + */ +static void iocshRegisterRTEMS (void) +{ + iocshRegister(&netStatFuncDef, netStatCallFunc); + iocshRegister(&heapSpaceFuncDef, heapSpaceCallFunc); +#ifndef OMIT_NFS_SUPPORT + iocshRegister(&nfsMountFuncDef, nfsMountCallFunc); +#endif +} + +/* + * Set up the console serial line (no handshaking) + */ +static void +initConsole (void) +{ + struct termios t; + + if (tcgetattr (fileno (stdin), &t) < 0) { + printf ("tcgetattr failed: %s\n", strerror (errno)); + return; + } + t.c_iflag &= ~(IXOFF | IXON | IXANY); + if (tcsetattr (fileno (stdin), TCSANOW, &t) < 0) { + printf ("tcsetattr failed: %s\n", strerror (errno)); + return; + } +} + +/* + * Ensure that the configuration object files + * get pulled in from the library + */ +extern rtems_configuration_table Configuration; +extern struct rtems_bsdnet_config rtems_bsdnet_config; +const void *rtemsConfigArray[] = { + &Configuration, + &rtems_bsdnet_config +}; + +/* + * Hook to ensure that BSP cleanup code gets run on exit + */ +static void +exitHandler(void) +{ + rtems_shutdown_executive(0); +} + +/* + * RTEMS Startup task + */ +rtems_task +Init (rtems_task_argument ignored) +{ + int i; + char *argv[3] = { NULL, NULL, NULL }; + char *cp; + rtems_task_priority newpri; + rtems_status_code sc; + rtems_time_of_day now; + + /* + * Explain why we're here + */ + logReset(); + + /* + * Architecture-specific hooks + */ + if (epicsRtemsInitPreSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0) + delayedPanic("epicsRtemsInitPreSetBootConfigFromNVRAM"); + if (rtems_bsdnet_config.bootp == NULL) { + extern void setBootConfigFromNVRAM(void); + setBootConfigFromNVRAM(); + } + if (epicsRtemsInitPostSetBootConfigFromNVRAM(&rtems_bsdnet_config) != 0) + delayedPanic("epicsRtemsInitPostSetBootConfigFromNVRAM"); + + /* + * Override RTEMS configuration + */ + rtems_task_set_priority ( + RTEMS_SELF, + epicsThreadGetOssPriorityValue(epicsThreadPriorityIocsh), + &newpri); + + /* + * Create a reasonable environment + */ + initConsole (); + putenv ("TERM=xterm"); + putenv ("IOCSH_HISTSIZE=20"); + + /* + * Start network + */ + if ((cp = getenv("EPICS_TS_NTP_INET")) != NULL) + rtems_bsdnet_config.ntp_server[0] = cp; + if (rtems_bsdnet_config.network_task_priority == 0) + { + unsigned int p; + if (epicsThreadHighestPriorityLevelBelow(epicsThreadPriorityScanLow, &p) + == epicsThreadBooleanStatusSuccess) + { + rtems_bsdnet_config.network_task_priority = epicsThreadGetOssPriorityValue(p); + } + } + printf("\n***** Initializing network *****\n"); + rtems_bsdnet_initialize_network(); + initialize_remote_filesystem(argv, initialize_local_filesystem(argv)); + + /* + * More environment: iocsh prompt and hostname + */ + { + char hostname[1024]; + gethostname(hostname, 1023); + char *cp = mustMalloc(strlen(hostname)+3, "iocsh prompt"); + sprintf(cp, "%s> ", hostname); + epicsEnvSet ("IOCSH_PS1", cp); + epicsEnvSet("IOC_NAME", hostname); + } + + /* + * Use BSP-supplied time of day if available otherwise supply default time. + * It is very likely that other time synchronization facilities in EPICS + * will soon override this value. + */ + if (rtems_clock_get(RTEMS_CLOCK_GET_TOD,&now) != RTEMS_SUCCESSFUL) { + now.year = 2001; + now.month = 1; + now.day = 1; + now.hour = 0; + now.minute = 0; + now.second = 0; + now.ticks = 0; + if ((sc = rtems_clock_set (&now)) != RTEMS_SUCCESSFUL) + printf ("***** Can't set time: %s\n", rtems_status_text (sc)); + } + if (getenv("TZ") == NULL) { + const char *tzp = envGetConfigParamPtr(&EPICS_TIMEZONE); + if (tzp == NULL) { + printf("Warning -- no timezone information available -- times will be displayed as GMT.\n"); + } + else { + char tz[10]; + int minWest, toDst = 0, fromDst = 0; + if(sscanf(tzp, "%9[^:]::%d:%d:%d", tz, &minWest, &toDst, &fromDst) < 2) { + printf("Warning: EPICS_TIMEZONE (%s) unrecognizable -- times will be displayed as GMT.\n", tzp); + } + else { + char posixTzBuf[40]; + char *p = posixTzBuf; + p += sprintf(p, "%cST%d:%.2d", tz[0], minWest/60, minWest%60); + if (toDst != fromDst) + p += sprintf(p, "%cDT", tz[0]); + epicsEnvSet("TZ", posixTzBuf); + } + } + } + tzset(); + osdTimeRegister(); + + /* + * Run the EPICS startup script + */ + printf ("***** Starting EPICS application *****\n"); + iocshRegisterRTEMS (); + set_directory (argv[1]); + epicsEnvSet ("IOC_STARTUP_SCRIPT", argv[1]); + atexit(exitHandler); + i = main ((sizeof argv / sizeof argv[0]) - 1, argv); + printf ("***** IOC application terminating *****\n"); + epicsThreadSleep(1.0); + epicsExit(0); +} diff --git a/src/RTEMS/base/rtems_netconfig.c b/src/RTEMS/base/rtems_netconfig.c new file mode 100644 index 000000000..102c5c8cb --- /dev/null +++ b/src/RTEMS/base/rtems_netconfig.c @@ -0,0 +1,111 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS network configuration for EPICS + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric.norum@usask.ca + * (306) 966-5394 + * + * This file can be copied to an application source dirctory + * and modified to override the values shown below. + */ +#include +#include +#include + +extern void rtems_bsdnet_loopattach(); +static struct rtems_bsdnet_ifconfig loopback_config = { + "lo0", /* name */ + (int (*)(struct rtems_bsdnet_ifconfig *, int))rtems_bsdnet_loopattach, /* attach function */ + NULL, /* link to next interface */ + "127.0.0.1", /* IP address */ + "255.0.0.0", /* IP net mask */ +}; + +/* + * The following conditionals select the network interface card. + * + * On RTEMS-pc386 targets all network drivers which support run-time + * probing are linked. + * On other targets the network interface specified by the board-support + * package is used. + * To use a different NIC for a particular application, copy this file to the + * application directory and make the appropriate changes. + */ +#if defined(__i386__) +extern int rtems_fxp_attach (struct rtems_bsdnet_ifconfig *, int); +static struct rtems_bsdnet_ifconfig fxp_driver_config = { + "fxp1", /* name */ + rtems_fxp_attach, /* attach function */ + &loopback_config, /* link to next interface */ +}; +extern int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *, int); +static struct rtems_bsdnet_ifconfig e3c509_driver_config = { + "ep0", /* name */ + rtems_3c509_driver_attach, /* attach function */ + &fxp_driver_config, /* link to next interface */ +}; +#define FIRST_DRIVER_CONFIG &e3c509_driver_config +#else + +# if defined(__PPC) + /* + * FIXME: This really belongs in the BSP + */ +# ifndef RTEMS_BSP_NETWORK_DRIVER_NAME +# define RTEMS_BSP_NETWORK_DRIVER_NAME "dc1" +# endif +# ifndef RTEMS_BSP_NETWORK_DRIVER_ATTACH +# define RTEMS_BSP_NETWORK_DRIVER_ATTACH rtems_dec21140_driver_attach + extern int rtems_dec21140_driver_attach(); +# endif +# endif + +static struct rtems_bsdnet_ifconfig bsp_driver_config = { + RTEMS_BSP_NETWORK_DRIVER_NAME, /* name */ + RTEMS_BSP_NETWORK_DRIVER_ATTACH, /* attach function */ + &loopback_config, /* link to next interface */ +}; +#define FIRST_DRIVER_CONFIG &bsp_driver_config + +#endif + +/* + * Allow configure/os/CONFIG_SITE.Common.RTEMS to provide domain name + */ +#ifdef RTEMS_NETWORK_CONFIG_DNS_DOMAINNAME +# define XSTR(x) STR(x) +# define STR(x) #x +# define MY_DOMAINNAME XSTR(RTEMS_NETWORK_CONFIG_DNS_DOMAINNAME) +#else +# define MY_DOMAINNAME NULL +#endif + +/* + * Allow non-BOOTP network configuration + */ +#ifndef MY_DO_BOOTP +# define MY_DO_BOOTP rtems_bsdnet_do_bootp +#endif + +/* + * Network configuration + */ +struct rtems_bsdnet_config rtems_bsdnet_config = { + FIRST_DRIVER_CONFIG, /* Link to next interface */ + MY_DO_BOOTP, /* How to find network config */ + 10, /* If 0 then the network daemons will run at a */ + /* priority just less than the lowest-priority */ + /* EPICS scan thread. */ + /* If non-zero then the network daemons will run */ + /* at this *RTEMS* priority */ + 180*1024, /* MBUF space */ + 350*1024, /* MBUF cluster space */ + NULL, /* Host name */ + MY_DOMAINNAME, /* Domain name */ +}; diff --git a/src/RTEMS/base/rtems_util.c b/src/RTEMS/base/rtems_util.c new file mode 100644 index 000000000..0d2360343 --- /dev/null +++ b/src/RTEMS/base/rtems_util.c @@ -0,0 +1,45 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS utilitiy routines for EPICS + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + * + * Supplies routines that are present in vxWorks but missing in RTEMS. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Like connect(), but with an explicit timeout + */ +int connectWithTimeout (int sfd, + struct sockaddr *addr, + int addrlen, + struct timeval *timeout) +{ + struct timeval sv; + socklen_t svlen = sizeof sv; + int ret; + + if (!timeout) + return connect (sfd, addr, addrlen); + if (getsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&sv, &svlen) < 0) + return -1; + if (setsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)timeout, sizeof *timeout) < 0) + return -1; + ret = connect (sfd, addr, addrlen); + setsockopt (sfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&sv, sizeof sv); + return ret; +} diff --git a/src/RTEMS/base/setBootConfigFromNVRAM.c b/src/RTEMS/base/setBootConfigFromNVRAM.c new file mode 100644 index 000000000..6a162c602 --- /dev/null +++ b/src/RTEMS/base/setBootConfigFromNVRAM.c @@ -0,0 +1,370 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *env_nfsServer; +char *env_nfsPath; +char *env_nfsMountPoint; + +/* + * Split argument string of form nfs_server:nfs_export: + * The nfs_export component will be used as: + * - the path to the directory exported from the NFS server + * - the local mount point + * - a prefix of + * For example, the argument string: + * romeo:/export/users:smith/ioc/iocexample/st.cmd + * would: + * - mount /export/users from NFS server romeo on /export/users + * - chdir to /export/users/smith/ioc/iocexample + * - read commands from st.cmd + */ +static void +splitRtemsBsdnetBootpCmdline(void) +{ + char *cp1, *cp2, *cp3; + + if ((cp1 = rtems_bsdnet_bootp_cmdline) == NULL) + return; + if (((cp2 = strchr(cp1, ':')) != NULL) + && (((cp3 = strchr(cp2+1, ' ')) != NULL) + || ((cp3 = strchr(cp2+1, ':')) != NULL))) { + int l1 = cp2 - cp1; + int l2 = cp3 - cp2 - 1; + int l3 = strlen(cp3) - 1; + if (l1 && l2 && l3) { + *cp2++ = '\0'; + *cp3 = '\0'; + env_nfsServer = cp1; + env_nfsMountPoint = env_nfsPath = epicsStrDup(cp2); + *cp3 = '/'; + rtems_bsdnet_bootp_cmdline = cp2; + } + } +} + +/* + * Split NFS mount information of the form nfs_server:host_path:local_path + */ +static void +splitNfsMountPath(char *nfsString) +{ + char *cp2, *cp3; + + if (nfsString == NULL) + return; + if (((cp2 = strchr(nfsString, ':')) != NULL) + && (((cp3 = strchr(cp2+1, ' ')) != NULL) + || ((cp3 = strchr(cp2+1, ':')) != NULL))) { + int l1 = cp2 - nfsString; + int l2 = cp3 - cp2 - 1; + int l3 = strlen(cp3) - 1; + if (l1 && l2 && l3) { + *cp2++ = '\0'; + *cp3++ = '\0'; + env_nfsServer = nfsString; + env_nfsPath = cp2; + env_nfsMountPoint = cp3; + } + } +} + +#if defined(HAVE_MOTLOAD) + +/* + * Motorola MOTLOAD NVRAM Access + */ +static char * +gev(const char *parm, volatile char *nvp) +{ + const char *val; + const char *name; + char *ret; + char c; + + for (;;) { + if (*nvp == '\0') + return NULL; + name = parm; + while ((c = *nvp++) != '\0') { + if ((c == '=') && (*name == '\0')) { + val = (char *)nvp; + while (*nvp++ != '\0') + continue; + ret = malloc(nvp - val); + if (ret == NULL) + return NULL; + strcpy(ret, val); + return ret; + } + if (c != *name++) { + while (*nvp++ != '\0') + continue; + break; + } + } + } +} + +static char * +motScriptParm(const char *mot_script_boot, char parm) +{ + const char *cp; + char *ret; + int l; + + while (*mot_script_boot != '\0') { + if (isspace(*(unsigned char *)mot_script_boot) + && (*(mot_script_boot+1) == '-') + && (*(mot_script_boot+2) == parm)) { + mot_script_boot += 3; + cp = mot_script_boot; + while ((*mot_script_boot != '\0') && + !isspace(*(unsigned char *)mot_script_boot)) + mot_script_boot++; + l = mot_script_boot - cp; + ret = malloc(l+1); + if (ret == NULL) + return NULL; + strncpy(ret, cp, l); + *(ret+l) = '\0'; + return ret; + } + mot_script_boot++; + } + return NULL; +} + +void +setBootConfigFromNVRAM(void) +{ + char *cp; + const char *mot_script_boot; + char *nvp; + +# if defined(BSP_NVRAM_BASE_ADDR) + nvp = (volatile unsigned char *)(BSP_NVRAM_BASE_ADDR+0x70f8); +# elif defined(BSP_I2C_VPD_EEPROM_DEV_NAME) + char gev_buf[3592]; + int fd; + if ((fd = open(BSP_I2C_VPD_EEPROM_DEV_NAME, 0)) < 0) { + printf("Can't open %s: %s\n", BSP_I2C_VPD_EEPROM_DEV_NAME, strerror(errno)); + return; + } + lseek(fd, 0x10f8, SEEK_SET); + if (read(fd, gev_buf, sizeof gev_buf) != sizeof gev_buf) { + printf("Can't read %s: %s\n", BSP_I2C_VPD_EEPROM_DEV_NAME, strerror(errno)); + return; + } + close(fd); + nvp = gev_buf; +# else +# error "No way to read GEV!" +# endif + + if (rtems_bsdnet_config.bootp != NULL) + return; + mot_script_boot = gev("mot-script-boot", nvp); + if ((rtems_bsdnet_bootp_server_name = gev("mot-/dev/enet0-sipa", nvp)) == NULL) + rtems_bsdnet_bootp_server_name = motScriptParm(mot_script_boot, 's'); + if ((rtems_bsdnet_config.gateway = gev("mot-/dev/enet0-gipa", nvp)) == NULL) + rtems_bsdnet_config.gateway = motScriptParm(mot_script_boot, 'g'); + if ((rtems_bsdnet_config.ifconfig->ip_netmask = gev("mot-/dev/enet0-snma", nvp)) == NULL) + rtems_bsdnet_config.ifconfig->ip_netmask = motScriptParm(mot_script_boot, 'm'); + + rtems_bsdnet_config.name_server[0] = gev("rtems-dns-server", nvp); + if (rtems_bsdnet_config.name_server[0] == NULL) + rtems_bsdnet_config.name_server[0] = rtems_bsdnet_bootp_server_name; + cp = gev("rtems-dns-domainname", nvp); + if (cp) + rtems_bsdnet_config.domainname = cp; + + if ((rtems_bsdnet_config.ifconfig->ip_address = gev("mot-/dev/enet0-cipa", nvp)) == NULL) + rtems_bsdnet_config.ifconfig->ip_address = motScriptParm(mot_script_boot, 'c'); + rtems_bsdnet_config.hostname = gev("rtems-client-name", nvp); + if (rtems_bsdnet_config.hostname == NULL) + rtems_bsdnet_config.hostname = rtems_bsdnet_config.ifconfig->ip_address; + + if ((rtems_bsdnet_bootp_boot_file_name = gev("mot-/dev/enet0-file", nvp)) == NULL) + rtems_bsdnet_bootp_boot_file_name = motScriptParm(mot_script_boot, 'f'); + rtems_bsdnet_bootp_cmdline = gev("epics-script", nvp); + splitRtemsBsdnetBootpCmdline(); + splitNfsMountPath(gev("epics-nfsmount", nvp)); + rtems_bsdnet_config.ntp_server[0] = gev("epics-ntpserver", nvp); + if (rtems_bsdnet_config.ntp_server[0] == NULL) + rtems_bsdnet_config.ntp_server[0] = rtems_bsdnet_bootp_server_name; + if ((cp = gev("epics-tz", nvp)) != NULL) + epicsEnvSet("TZ", cp); +} + +#elif defined(HAVE_PPCBUG) +/* + * Motorola PPCBUG NVRAM Access + */ +struct ppcbug_nvram { + uint32_t PacketVersionIdentifier; + uint32_t NodeControlMemoryAddress; + uint32_t BootFileLoadAddress; + uint32_t BootFileExecutionAddress; + uint32_t BootFileExecutionDelay; + uint32_t BootFileLength; + uint32_t BootFileByteOffset; + uint32_t TraceBufferAddress; + uint32_t ClientIPAddress; + uint32_t ServerIPAddress; + uint32_t SubnetIPAddressMask; + uint32_t BroadcastIPAddressMask; + uint32_t GatewayIPAddress; + uint8_t BootpRarpRetry; + uint8_t TftpRarpRetry; + uint8_t BootpRarpControl; + uint8_t UpdateControl; + char BootFilenameString[64]; + char ArgumentFilenameString[64]; +}; + +static char *addr(char *cbuf, uint32_t addr) +{ + struct in_addr a; + if ((a.s_addr = addr) == 0) + return NULL; + return (char *)inet_ntop(AF_INET, &a, cbuf, INET_ADDRSTRLEN); +} + +void +setBootConfigFromNVRAM(void) +{ + static struct ppcbug_nvram nvram; + static char ip_address[INET_ADDRSTRLEN]; + static char ip_netmask[INET_ADDRSTRLEN]; + static char server[INET_ADDRSTRLEN]; + static char gateway[INET_ADDRSTRLEN]; + + if (rtems_bsdnet_config.bootp != NULL) + return; + + /* + * Get network configuation from PPCBUG. + * The 'correct' way to do this would be to issue a .NETCFIG PPCBUG + * system call. Unfortunately it is very difficult to issue such a + * call once RTEMS is up and running so we just copy from the 'known' + * location of the network configuration parameters. + * Care must be taken to access the NVRAM a byte at a time. + */ + +#if defined(NVRAM_INDIRECT) + { + volatile char *addrLo = (volatile char *)0x80000074; + volatile char *addrHi = (volatile char *)0x80000075; + volatile char *data = (volatile char *)0x80000077; + int addr = 0x1000; + char *d = (char *)&nvram; + + while (d < ((char *)&nvram + sizeof nvram)) { + *addrLo = addr & 0xFF; + *addrHi = (addr >> 8) & 0xFF; + *d++ = *data; + addr++; + } + } +#else + { + volatile char *s = (volatile char *)0xFFE81000; + char *d = (char *)&nvram; + + while (d < ((char *)&nvram + sizeof nvram)) + *d++ = *s++; + } +#endif + /* + * Assume that the boot server is also the name, log and ntp server! + */ + rtems_bsdnet_config.name_server[0] = + rtems_bsdnet_config.ntp_server[0] = + rtems_bsdnet_bootp_server_name = addr(server, nvram.ServerIPAddress); + rtems_bsdnet_bootp_server_address.s_addr = nvram.ServerIPAddress; + /* + * Nothing better to use as host name! + */ + rtems_bsdnet_config.ifconfig->ip_address = + rtems_bsdnet_config.hostname = addr(ip_address, nvram.ClientIPAddress); + + rtems_bsdnet_config.gateway = addr(gateway, nvram.GatewayIPAddress); + rtems_bsdnet_config.ifconfig->ip_netmask = addr(ip_netmask, nvram.SubnetIPAddressMask); + + rtems_bsdnet_bootp_boot_file_name = nvram.BootFilenameString; + rtems_bsdnet_bootp_cmdline = nvram.ArgumentFilenameString; + splitRtemsBsdnetBootpCmdline(); +} + +#elif defined(__mcf528x__) + +static char * +env(const char *parm, const char *defaultValue) +{ + const char *cp = bsp_getbenv(parm); + + if (!cp) { + if (!defaultValue) + return NULL; + cp = defaultValue; + printf ("%s environment variable missing -- using %s.\n", parm, cp); + } + return epicsStrDup(cp); +} + +void +setBootConfigFromNVRAM(void) +{ + const char *cp1; + + if (rtems_bsdnet_config.bootp != NULL) + return; + rtems_bsdnet_config.gateway = env("GATEWAY", NULL); + rtems_bsdnet_config.ifconfig->ip_netmask = env("NETMASK", "255.255.252.0"); + + rtems_bsdnet_bootp_server_name = env("SERVER", "192.168.0.1"); + rtems_bsdnet_config.name_server[0] = env("NAMESERVER", rtems_bsdnet_bootp_server_name); + rtems_bsdnet_config.ntp_server[0] = env("NTPSERVER", rtems_bsdnet_bootp_server_name); + cp1 = env("DOMAIN", NULL); + if (cp1 != NULL) + rtems_bsdnet_config.domainname = cp1; + rtems_bsdnet_config.hostname = env("HOSTNAME", "iocNobody"); + rtems_bsdnet_config.ifconfig->ip_address = env("IPADDR0", "192.168.0.2"); + rtems_bsdnet_bootp_boot_file_name = env("BOOTFILE", "uC5282App.boot"); + rtems_bsdnet_bootp_cmdline = env("CMDLINE", "epics/iocBoot/iocNobody/st.cmd"); + splitNfsMountPath(env("NFSMOUNT", NULL)); + if ((cp1 = env("TZ", NULL)) != NULL) + epicsEnvSet("TZ", cp1); +} + +#else +/* + * Placeholder for systems without NVRAM + */ +void +setBootConfigFromNVRAM(void) +{ + printf("SYSTEM HAS NO NON-VOLATILE RAM!\n"); + printf("YOU MUST USE SOME OTHER METHOD TO OBTAIN NETWORK CONFIGURATION\n"); +} +#endif diff --git a/src/as/Makefile b/src/as/Makefile new file mode 100644 index 000000000..ef3e0ba0f --- /dev/null +++ b/src/as/Makefile @@ -0,0 +1,51 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../.. +include $(TOP)/configure/CONFIG + +INC += asLib.h +INC += asDbLib.h +INC += asCa.h +INC += asTrapWrite.h +INC += asIocRegister.h + +LIB_SRCS += asLib.c +LIB_SRCS += asTrapWrite.c + +asIoc_SRCS += asDbLib.c +asIoc_SRCS += asCa.c +asIoc_SRCS += asIocRegister.c + +LIBRARY_HOST = asHost +LIBRARY_IOC = asIoc + +asHost_LIBS = dbStaticHost ca Com +asIoc_LIBS = dbIoc dbStaticIoc ca Com + +asHost_RCS = asHost.rc +asIoc_RCS = asIoc.rc + +PROD_HOST = ascheck +ascheck_SRCS = ascheck.c +PROD_LIBS = asHost dbStaticHost ca Com + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=asIoc +OBJLIB_SRCS += $(LIB_SRCS) $(asIoc_SRCS) +endif + +include $(TOP)/configure/RULES + +# Extra rule since asLib_lex.c is included by asLib.c +asLib$(OBJ): asLib_lex.c + +clean:: + @$(RM) asLib.c asLib_lex.c + diff --git a/src/as/asCa.c b/src/as/asCa.c new file mode 100644 index 000000000..abcbac819 --- /dev/null +++ b/src/as/asCa.c @@ -0,0 +1,333 @@ +/*asCa.c*/ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 10-15-93 */ + +/*This module is separate from asDbLib because CA uses old database access*/ +#include +#include +#include +#include + +#include "dbDefs.h" +#include "ellLib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "cantProceed.h" +#include "errlog.h" +#include "taskwd.h" +#include "callback.h" +#include "db_access.h" +#include "cadef.h" +#include "caerr.h" +#include "caeventmask.h" +#include "alarm.h" +#include "epicsStdioRedirect.h" + +#include "epicsExport.h" +#include "asLib.h" +#include "asDbLib.h" +#include "asCa.h" + +int asCaDebug = 0; +epicsExportAddress(int,asCaDebug); +static int firstTime = TRUE; +static epicsThreadId threadid=0; +static int caInitializing=FALSE; +static epicsMutexId asCaTaskLock; /*lock access to task */ +static epicsEventId asCaTaskWait; /*Wait for task to respond*/ +static epicsEventId asCaTaskAddChannels; /*Tell asCaTask to add channels*/ +static epicsEventId asCaTaskClearChannels;/*Tell asCaTask to clear channels*/ + +typedef struct { + struct dbr_sts_double rtndata; + chid chid; +} CAPVT; + +static void exceptionCallback(struct exception_handler_args args) +{ + chid chid = args.chid; + long stat = args.stat; /* Channel access status code*/ + const char *channel; + const char *context; + static char *unknown = "unknown"; + const char *nativeType; + const char *requestType; + long nativeCount; + long requestCount; + int readAccess; + int writeAccess; + + channel = (chid ? ca_name(chid) : unknown); + context = (args.ctx ? args.ctx : unknown); + nativeType = dbr_type_to_text((chid ? ca_field_type(chid) : -1)); + requestType = dbr_type_to_text(args.type); + nativeCount = (chid ? ca_element_count(chid) : 0); + requestCount = args.count; + readAccess = (chid ? ca_read_access(chid) : 0); + writeAccess = (chid ? ca_write_access(chid) : 0); + + errlogPrintf("dbCa:exceptionCallback stat \"%s\" channel \"%s\"" + " context \"%s\"\n" + " nativeType %s requestType %s" + " nativeCount %ld requestCount %ld %s %s\n", + ca_message(stat),channel,context, + nativeType,requestType, + nativeCount,requestCount, + (readAccess ? "readAccess" : "noReadAccess"), + (writeAccess ? "writeAccess" : "noWriteAccess")); +} + +/*connectCallback only handles disconnects*/ +static void connectCallback(struct connection_handler_args arg) +{ + chid chid = arg.chid; + ASGINP *pasginp = (ASGINP *)ca_puser(chid); + ASG *pasg = pasginp->pasg; + + if(ca_state(chid)!=cs_conn) { + if(!(pasg->inpBad & (1<inpIndex))) { + /*was good so lets make it bad*/ + pasg->inpBad |= (1<inpIndex); + if(!caInitializing) asComputeAsg(pasg); + if(asCaDebug) printf("as connectCallback disconnect %s\n", + ca_name(chid)); + } + } +} + +static void eventCallback(struct event_handler_args arg) +{ + int caStatus = arg.status; + chid chid = arg.chid; + ASGINP *pasginp = (ASGINP *)arg.usr; + ASG *pasg; + CAPVT *pcapvt; + const struct dbr_sts_double *pdata; + + if(caStatus!=ECA_NORMAL) { + if(chid) { + epicsPrintf("asCa: eventCallback error %s channel %s\n", + ca_message(caStatus),ca_name(chid)); + } else { + epicsPrintf("asCa: eventCallback error %s chid is null\n", + ca_message(caStatus)); + } + return; + } + pasg = pasginp->pasg; + pcapvt = (CAPVT *)pasginp->capvt; + if(chid!=pcapvt->chid) { + epicsPrintf("asCa: eventCallback error pcapvt->chid != arg.chid\n"); + return; + } + if(ca_state(chid)!=cs_conn || !ca_read_access(chid)) { + if(!(pasg->inpBad & (1<inpIndex))) { + /*was good so lets make it bad*/ + pasg->inpBad |= (1<inpIndex); + if(!caInitializing) asComputeAsg(pasg); + if(asCaDebug) { + printf("as eventCallback %s inpBad ca_state %d" + " ca_read_access %d\n", + ca_name(chid),ca_state(chid),ca_read_access(chid)); + } + } + return; + } + pdata = arg.dbr; + pcapvt->rtndata = *pdata; /*structure copy*/ + if(pdata->severity==INVALID_ALARM) { + pasg->inpBad |= (1<inpIndex); + if(asCaDebug) + printf("as eventCallback %s inpBad because INVALID_ALARM\n", + ca_name(chid)); + } else { + pasg->inpBad &= ~((1<inpIndex)); + pasg->pavalue[pasginp->inpIndex] = pdata->value; + if(asCaDebug) + printf("as eventCallback %s inpGood data %f\n", + ca_name(chid),pdata->value); + } + pasg->inpChanged |= (1<inpIndex); + if(!caInitializing) asComputeAsg(pasg); +} + +static void asCaTask(void) +{ + ASG *pasg; + ASGINP *pasginp; + CAPVT *pcapvt; + int status; + + taskwdInsert(epicsThreadGetIdSelf(),NULL,NULL); + SEVCHK(ca_context_create(ca_enable_preemptive_callback), + "asCaTask calling ca_context_create"); + SEVCHK(ca_add_exception_event(exceptionCallback,NULL), + "ca_add_exception_event"); + while(TRUE) { + epicsEventMustWait(asCaTaskAddChannels); + caInitializing = TRUE; + pasg = (ASG *)ellFirst(&pasbase->asgList); + while(pasg) { + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + while(pasginp) { + pasg->inpBad |= (1<inpIndex); + pcapvt = pasginp->capvt = asCalloc(1,sizeof(CAPVT)); + /*Note calls connectCallback immediately for local Pvs*/ + status = ca_search_and_connect(pasginp->inp,&pcapvt->chid, + connectCallback,pasginp); + if(status!=ECA_NORMAL) { + epicsPrintf("asCa ca_search_and_connect error %s\n", + ca_message(status)); + } + /*Note calls eventCallback immediately for local Pvs*/ + status = ca_add_event(DBR_STS_DOUBLE,pcapvt->chid, + eventCallback,pasginp,0); + if(status!=ECA_NORMAL) { + epicsPrintf("asCa ca_add_event error %s\n", + ca_message(status)); + } + pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); + } + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + SEVCHK(ca_flush_io(),"asCaTask"); + caInitializing = FALSE; + asComputeAllAsg(); + if(asCaDebug) printf("asCaTask initialized\n"); + epicsEventSignal(asCaTaskWait); + epicsEventMustWait(asCaTaskClearChannels); + pasg = (ASG *)ellFirst(&pasbase->asgList); + while(pasg) { + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + while(pasginp) { + pcapvt = (CAPVT *)pasginp->capvt; + status = ca_clear_channel(pcapvt->chid); + if(status!=ECA_NORMAL) { + epicsPrintf("asCa ca_clear_channel error %s\n", + ca_message(status)); + } + free(pasginp->capvt); + pasginp->capvt = 0; + pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); + } + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + if(asCaDebug) printf("asCaTask has cleared all channels\n"); + epicsEventSignal(asCaTaskWait); + } +} + +void epicsShareAPI asCaStart(void) +{ + if(asCaDebug) printf("asCaStart called\n"); + if(firstTime) { + firstTime = FALSE; + asCaTaskLock=epicsMutexMustCreate(); + asCaTaskWait=epicsEventMustCreate(epicsEventEmpty); + asCaTaskAddChannels=epicsEventMustCreate(epicsEventEmpty); + asCaTaskClearChannels=epicsEventMustCreate(epicsEventEmpty); + threadid = epicsThreadCreate("asCaTask", + (epicsThreadPriorityScanLow - 3), + epicsThreadGetStackSize(epicsThreadStackBig), + (EPICSTHREADFUNC)asCaTask,0); + if(threadid==0) { + errMessage(0,"asCaStart: taskSpawn Failure\n"); + } + } + epicsMutexMustLock(asCaTaskLock); + epicsEventSignal(asCaTaskAddChannels); + epicsEventMustWait(asCaTaskWait); + if(asCaDebug) printf("asCaStart done\n"); + epicsMutexUnlock(asCaTaskLock); +} + +void epicsShareAPI asCaStop(void) +{ + if(threadid==0) return; + if(asCaDebug) printf("asCaStop called\n"); + epicsMutexMustLock(asCaTaskLock); + epicsEventSignal(asCaTaskClearChannels); + epicsEventMustWait(asCaTaskWait); + if(asCaDebug) printf("asCaStop done\n"); + epicsMutexUnlock(asCaTaskLock); +} + +int epicsShareAPI ascar(int level) { return ascarFP(stdout,level);} + +int epicsShareAPI ascarFP(FILE *fp,int level) +{ + ASG *pasg; + int n=0,nbad=0; + enum channel_state state; + + if(!pasbase) { + fprintf(fp,"access security not started\n"); + return(0); + } + pasg = (ASG *)ellFirst(&pasbase->asgList); + while(pasg) { + ASGINP *pasginp; + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + while(pasginp) { + CAPVT *pcapvt = (CAPVT *)pasginp->capvt; + chid chid = pcapvt->chid; + pcapvt = pasginp->capvt; + ++n; + state = ca_state(chid); + if(state!=cs_conn) ++nbad; + if(level>1 || (level==1 && state!=cs_conn)) { + fprintf(fp,"connected:"); + if(state==cs_never_conn) fprintf(fp,"never "); + else if(state==cs_prev_conn) fprintf(fp,"prev "); + else if(state==cs_conn) fprintf(fp,"yes "); + else if(state==cs_closed) fprintf(fp,"closed"); + else fprintf(fp,"unknown"); + fprintf(fp," read:%s write:%s", + (ca_read_access(chid) ? "yes" : "no "), + (ca_write_access(chid) ? "yes" : "no ")); + fprintf(fp," %s %s\n", ca_name(chid),ca_host_name(chid)); + } + pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); + } + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + fprintf(fp,"%d channels %d not connected\n",n,nbad); + return(0); +} + +void epicsShareAPI ascaStats(int *pchans, int *pdiscon) +{ + ASG *pasg; + int n = 0; + int nbad = 0; + + if(!pasbase) { + if (pchans) *pchans = n; + if (pdiscon) *pdiscon = nbad; + return; + } + pasg = (ASG *)ellFirst(&pasbase->asgList); + while (pasg) { + ASGINP *pasginp; + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + while (pasginp) { + CAPVT *pcapvt = (CAPVT *)pasginp->capvt; + chid chid = pcapvt->chid; + ++n; + if (ca_state(chid) != cs_conn) ++nbad; + pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); + } + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + if (pchans) *pchans = n; + if (pdiscon) *pdiscon = nbad; +} + diff --git a/src/as/asCa.h b/src/as/asCa.h new file mode 100644 index 000000000..efc5ccbb5 --- /dev/null +++ b/src/as/asCa.h @@ -0,0 +1,30 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* asCa.h */ + +#ifndef INCasCah +#define INCasCah + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI asCaStart(void); +epicsShareFunc void epicsShareAPI asCaStop(void); +epicsShareFunc int epicsShareAPI ascar(int level); +epicsShareFunc int epicsShareAPI ascarFP(FILE *fp, int level); +epicsShareFunc void epicsShareAPI ascaStats(int *pchans, int *pdiscon); + +#ifdef __cplusplus +} +#endif + +#endif /*INCasCah*/ diff --git a/src/as/asDbLib.c b/src/as/asDbLib.c new file mode 100644 index 000000000..a838444c9 --- /dev/null +++ b/src/as/asDbLib.c @@ -0,0 +1,330 @@ +/* share/src/as/asDbLib.c */ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 02-11-94*/ + +#include +#include +#include +#include + +#include "epicsStdioRedirect.h" +#include "dbDefs.h" +#include "cantProceed.h" +#include "epicsThread.h" +#include "errlog.h" +#include "taskwd.h" +#include "alarm.h" +#include "caeventmask.h" +#include "callback.h" +#include "dbStaticLib.h" +#include "dbAddr.h" +#include "dbAccess.h" +#include "db_field_log.h" +#include "dbEvent.h" +#include "dbCommon.h" +#include "recSup.h" +#define epicsExportSharedSymbols +#include "asLib.h" +#include "asCa.h" +#include "asDbLib.h" + +static char *pacf=NULL; +static char *psubstitutions=NULL; +static epicsThreadId asInitTheadId=0; +static int firstTime = TRUE; + +static long asDbAddRecords(void) +{ + DBENTRY dbentry; + DBENTRY *pdbentry=&dbentry; + long status; + dbCommon *precord; + + dbInitEntry(pdbbase,pdbentry); + status = dbFirstRecordType(pdbentry); + while(!status) { + status = dbFirstRecord(pdbentry); + while(!status) { + precord = pdbentry->precnode->precord; + if(!precord->asp) { + status = asAddMember(&precord->asp, precord->asg); + if(status) errMessage(status,"asDbAddRecords:asAddMember"); + asPutMemberPvt(precord->asp,precord); + } + status = dbNextRecord(pdbentry); + } + status = dbNextRecordType(pdbentry); + } + dbFinishEntry(pdbentry); + return(0); +} + +int epicsShareAPI asSetFilename(const char *acf) +{ + if(pacf) free ((void *)pacf); + if(acf) { + pacf = calloc(1,strlen(acf)+1); + if(!pacf) { + errMessage(0,"asSetFilename calloc failure"); + } else { + strcpy(pacf,acf); + } + } else { + pacf = NULL; + } + return(0); +} + +int epicsShareAPI asSetSubstitutions(const char *substitutions) +{ + if(psubstitutions) free ((void *)psubstitutions); + if(substitutions) { + psubstitutions = calloc(1,strlen(substitutions)+1); + if(!psubstitutions) { + errMessage(0,"asSetSubstitutions calloc failure"); + } else { + strcpy(psubstitutions,substitutions); + } + } else { + psubstitutions = NULL; + } + return(0); +} + +static void asSpcAsCallback(struct dbCommon *precord) +{ + asChangeGroup(&precord->asp, precord->asg); +} + +static void asInitCommonOnce(void *arg) +{ + int *firstTime = (int *)arg; + *firstTime = FALSE; +} + +static long asInitCommon(void) +{ + long status; + int asWasActive = asActive; + int wasFirstTime = firstTime; + static epicsThreadOnceId asInitCommonOnceFlag = EPICS_THREAD_ONCE_INIT; + + + epicsThreadOnce(&asInitCommonOnceFlag,asInitCommonOnce,(void *)&firstTime); + if(wasFirstTime) { + if(!pacf) return(0); /*access security will NEVER be turned on*/ + } else { + if(!asActive) { + printf("Access security is NOT enabled." + " Was asSetFilename specified before iocInit?\n"); + return(S_asLib_asNotActive); + } + if(pacf) { + asCaStop(); + } else { /*Just leave everything as is */ + return(S_asLib_badConfig); + } + } + status = asInitFile(pacf,psubstitutions); + if(asActive) { + if(!asWasActive) { + dbSpcAsRegisterCallback(asSpcAsCallback); + asDbAddRecords(); + } + asCaStart(); + } + return(status); +} + +int epicsShareAPI asInit(void) +{ + return(asInitCommon()); +} + +static void wdCallback(void *arg) +{ + ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg; + pcallback->status = S_asLib_InitFailed; + callbackRequest(&pcallback->callback); +} + +static void asInitTask(ASDBCALLBACK *pcallback) +{ + long status; + + taskwdInsert(epicsThreadGetIdSelf(), wdCallback, (void *)pcallback); + status = asInitCommon(); + taskwdRemove(epicsThreadGetIdSelf()); + asInitTheadId = 0; + if(pcallback) { + pcallback->status = status; + callbackRequest(&pcallback->callback); + } +} + +int epicsShareAPI asInitAsyn(ASDBCALLBACK *pcallback) +{ + if(!pacf) return(0); + if(asInitTheadId) { + errMessage(-1,"asInit: asInitTask already active"); + if(pcallback) { + pcallback->status = S_asLib_InitFailed; + callbackRequest(&pcallback->callback); + } + return(-1); + } + asInitTheadId = epicsThreadCreate("asInitTask", + (epicsThreadPriorityCAServerHigh + 1), + epicsThreadGetStackSize(epicsThreadStackBig), + (EPICSTHREADFUNC)asInitTask,(void *)pcallback); + if(asInitTheadId==0) { + errMessage(0,"asInit: epicsThreadCreate Error"); + if(pcallback) { + pcallback->status = S_asLib_InitFailed; + callbackRequest(&pcallback->callback); + } + asInitTheadId = 0; + } + return(0); +} + +int epicsShareAPI asDbGetAsl(void *paddress) +{ + DBADDR *paddr = paddress; + dbFldDes *pflddes; + + pflddes = paddr->pfldDes; + return((int)pflddes->as_level); +} + +void * epicsShareAPI asDbGetMemberPvt(void *paddress) +{ + DBADDR *paddr = paddress; + dbCommon *precord; + + precord = paddr->precord; + return((void *)precord->asp); +} + +static void astacCallback(ASCLIENTPVT clientPvt,asClientStatus status) +{ + char *recordname; + + recordname = (char *)asGetClientPvt(clientPvt); + printf("astac callback %s: status=%d",recordname,status); + printf(" get %s put %s\n",(asCheckGet(clientPvt) ? "Yes" : "No"), + (asCheckPut(clientPvt) ? "Yes" : "No")); +} + +int epicsShareAPI astac(const char *pname,const char *user,const char *location) +{ + DBADDR *paddr; + long status; + ASCLIENTPVT *pasclientpvt=NULL; + dbCommon *precord; + dbFldDes *pflddes; + char *puser; + char *plocation; + + paddr = dbCalloc(1,sizeof(DBADDR) + sizeof(ASCLIENTPVT)); + pasclientpvt = (ASCLIENTPVT *)(paddr + 1); + status=dbNameToAddr(pname,paddr); + if(status) { + errMessage(status,"dbNameToAddr error"); + return(1); + } + precord = paddr->precord; + pflddes = paddr->pfldDes; + puser = asCalloc(1,strlen(user)+1); + strcpy(puser,user); + plocation = asCalloc(1,strlen(location)+1); + strcpy(plocation,location); + + status = asAddClient(pasclientpvt,precord->asp, + (int)pflddes->as_level,puser,plocation); + if(status) { + errMessage(status,"asAddClient error"); + return(1); + } else { + asPutClientPvt(*pasclientpvt,(void *)precord->name); + asRegisterClientCallback(*pasclientpvt,astacCallback); + } + return(0); +} + +static void myMemberCallback(ASMEMBERPVT memPvt,FILE *fp) +{ + dbCommon *precord; + + precord = asGetMemberPvt(memPvt); + if(precord) fprintf(fp," Record:%s",precord->name); +} + +int epicsShareAPI asdbdump(void) +{ + asDumpFP(stdout,myMemberCallback,NULL,1); + return(0); +} + +int epicsShareAPI asdbdumpFP(FILE *fp) +{ + asDumpFP(fp,myMemberCallback,NULL,1); + return(0); +} + +int epicsShareAPI aspuag(const char *uagname) +{ + asDumpUagFP(stdout,uagname); + return(0); +} + +int epicsShareAPI aspuagFP(FILE *fp,const char *uagname) +{ + + asDumpUagFP(fp,uagname); + return(0); +} + +int epicsShareAPI asphag(const char *hagname) +{ + asDumpHagFP(stdout,hagname); + return(0); +} + +int epicsShareAPI asphagFP(FILE *fp,const char *hagname) +{ + asDumpHagFP(fp,hagname); + return(0); +} + +int epicsShareAPI asprules(const char *asgname) +{ + asDumpRulesFP(stdout,asgname); + return(0); +} + +int epicsShareAPI asprulesFP(FILE *fp,const char *asgname) +{ + asDumpRulesFP(fp,asgname); + return(0); +} + +int epicsShareAPI aspmem(const char *asgname,int clients) +{ + asDumpMemFP(stdout,asgname,myMemberCallback,clients); + return(0); +} + +int epicsShareAPI aspmemFP(FILE *fp,const char *asgname,int clients) +{ + asDumpMemFP(fp,asgname,myMemberCallback,clients); + return(0); +} diff --git a/src/as/asDbLib.h b/src/as/asDbLib.h new file mode 100644 index 000000000..4bcf039e1 --- /dev/null +++ b/src/as/asDbLib.h @@ -0,0 +1,53 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* share/epicsH/dbAsLib.h */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* Author: Marty Kraimer Date: 02-23-94*/ + +#ifndef INCdbAsLibh +#define INCdbAsLibh + +#include "callback.h" +#include "shareLib.h" + +typedef struct { + CALLBACK callback; + long status; +} ASDBCALLBACK; + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsShareAPI asSetFilename(const char *acf); +epicsShareFunc int epicsShareAPI asSetSubstitutions(const char *substitutions); +epicsShareFunc int epicsShareAPI asInit(void); +epicsShareFunc int epicsShareAPI asInitAsyn(ASDBCALLBACK *pcallback); +epicsShareFunc int epicsShareAPI asDbGetAsl( void *paddr); +epicsShareFunc void * epicsShareAPI asDbGetMemberPvt( void *paddr); +epicsShareFunc int epicsShareAPI asdbdump(void); +epicsShareFunc int epicsShareAPI asdbdumpFP(FILE *fp); +epicsShareFunc int epicsShareAPI aspuag(const char *uagname); +epicsShareFunc int epicsShareAPI aspuagFP(FILE *fp,const char *uagname); +epicsShareFunc int epicsShareAPI asphag(const char *hagname); +epicsShareFunc int epicsShareAPI asphagFP(FILE *fp,const char *hagname); +epicsShareFunc int epicsShareAPI asprules(const char *asgname); +epicsShareFunc int epicsShareAPI asprulesFP(FILE *fp,const char *asgname); +epicsShareFunc int epicsShareAPI aspmem(const char *asgname,int clients); +epicsShareFunc int epicsShareAPI aspmemFP( + FILE *fp,const char *asgname,int clients); +epicsShareFunc int epicsShareAPI astac( + const char *recordname,const char *user,const char *location); + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbAsLibh*/ diff --git a/src/as/asHost.rc b/src/as/asHost.rc new file mode 100755 index 000000000..77981517e --- /dev/null +++ b/src/as/asHost.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Host Access Security Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Host Access Security Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "asHost\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "asHost.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/as/asIoc.rc b/src/as/asIoc.rc new file mode 100755 index 000000000..f83a3c17e --- /dev/null +++ b/src/as/asIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","IOC Access Security Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "IOC Access Security Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "asIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "asIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/as/asIocRegister.c b/src/as/asIocRegister.c new file mode 100644 index 000000000..8846765a8 --- /dev/null +++ b/src/as/asIocRegister.c @@ -0,0 +1,128 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "iocsh.h" +#define epicsExportSharedSymbols +#include "asLib.h" +#include "asDbLib.h" +#include "asCa.h" +#include "asIocRegister.h" + +/* asSetFilename */ +static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgString}; +static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0}; +static const iocshFuncDef asSetFilenameFuncDef = + {"asSetFilename",1,asSetFilenameArgs}; +static void asSetFilenameCallFunc(const iocshArgBuf *args) +{ + asSetFilename(args[0].sval); +} + +/* asSetSubstitutions */ +static const iocshArg asSetSubstitutionsArg0 = { "substitutions",iocshArgString}; +static const iocshArg * const asSetSubstitutionsArgs[] = {&asSetSubstitutionsArg0}; +static const iocshFuncDef asSetSubstitutionsFuncDef = + {"asSetSubstitutions",1,asSetSubstitutionsArgs}; +static void asSetSubstitutionsCallFunc(const iocshArgBuf *args) +{ + asSetSubstitutions(args[0].sval); +} + +/* asInit */ +static const iocshFuncDef asInitFuncDef = {"asInit",0}; +static void asInitCallFunc(const iocshArgBuf *args) +{ + asInit(); +} + +/* asdbdump */ +static const iocshFuncDef asdbdumpFuncDef = {"asdbdump",0}; +static void asdbdumpCallFunc(const iocshArgBuf *args) +{ + asdbdump(); +} + +/* aspuag */ +static const iocshArg aspuagArg0 = { "uagname",iocshArgString}; +static const iocshArg * const aspuagArgs[] = {&aspuagArg0}; +static const iocshFuncDef aspuagFuncDef = {"aspuag",1,aspuagArgs}; +static void aspuagCallFunc(const iocshArgBuf *args) +{ + aspuag(args[0].sval); +} + +/* asphag */ +static const iocshArg asphagArg0 = { "hagname",iocshArgString}; +static const iocshArg * const asphagArgs[] = {&asphagArg0}; +static const iocshFuncDef asphagFuncDef = {"asphag",1,asphagArgs}; +static void asphagCallFunc(const iocshArgBuf *args) +{ + asphag(args[0].sval); +} + +/* asprules */ +static const iocshArg asprulesArg0 = { "asgname",iocshArgString}; +static const iocshArg * const asprulesArgs[] = {&asprulesArg0}; +static const iocshFuncDef asprulesFuncDef = {"asprules",1,asprulesArgs}; +static void asprulesCallFunc(const iocshArgBuf *args) +{ + asprules(args[0].sval); +} + +/* aspmem */ +static const iocshArg aspmemArg0 = { "asgname",iocshArgString}; +static const iocshArg aspmemArg1 = { "clients",iocshArgInt}; +static const iocshArg * const aspmemArgs[] = {&aspmemArg0,&aspmemArg1}; +static const iocshFuncDef aspmemFuncDef = {"aspmem",2,aspmemArgs}; +static void aspmemCallFunc(const iocshArgBuf *args) +{ + aspmem(args[0].sval,args[1].ival); +} + +/* astac */ +static const iocshArg astacArg0 = { "recordname",iocshArgString}; +static const iocshArg astacArg1 = { "user",iocshArgString}; +static const iocshArg astacArg2 = { "location",iocshArgString}; +static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2}; +static const iocshFuncDef astacFuncDef = {"astac",3,astacArgs}; +static void astacCallFunc(const iocshArgBuf *args) +{ + astac(args[0].sval,args[1].sval,args[2].sval); +} + +/* ascar */ +static const iocshArg ascarArg0 = { "level",iocshArgInt}; +static const iocshArg * const ascarArgs[] = {&ascarArg0}; +static const iocshFuncDef ascarFuncDef = {"ascar",1,ascarArgs}; +static void ascarCallFunc(const iocshArgBuf *args) +{ + ascar(args[0].ival); +} + +/* asDumpHash */ +static const iocshFuncDef asDumpHashFuncDef = {"asDumpHash",0,0}; +static void asDumpHashCallFunc(const iocshArgBuf *args) +{ + asDumpHash(); +} + +void epicsShareAPI asIocRegister(void) +{ + iocshRegister(&asSetFilenameFuncDef,asSetFilenameCallFunc); + iocshRegister(&asSetSubstitutionsFuncDef,asSetSubstitutionsCallFunc); + iocshRegister(&asInitFuncDef,asInitCallFunc); + iocshRegister(&asdbdumpFuncDef,asdbdumpCallFunc); + iocshRegister(&aspuagFuncDef,aspuagCallFunc); + iocshRegister(&asphagFuncDef,asphagCallFunc); + iocshRegister(&asprulesFuncDef,asprulesCallFunc); + iocshRegister(&aspmemFuncDef,aspmemCallFunc); + iocshRegister(&astacFuncDef,astacCallFunc); + iocshRegister(&ascarFuncDef,ascarCallFunc); + iocshRegister(&asDumpHashFuncDef,asDumpHashCallFunc); +} diff --git a/src/as/asIocRegister.h b/src/as/asIocRegister.h new file mode 100644 index 000000000..50d44a5ac --- /dev/null +++ b/src/as/asIocRegister.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_asIocRegister_H +#define INC_asIocRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI asIocRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_asIocRegister_H */ diff --git a/src/as/asLib.h b/src/as/asLib.h new file mode 100644 index 000000000..b3da2f836 --- /dev/null +++ b/src/as/asLib.h @@ -0,0 +1,229 @@ +/* asLib.h */ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 09-27-93*/ + +#ifndef INCasLibh +#define INCasLibh + +#include "shareLib.h" +#include "ellLib.h" +#include "errMdef.h" +#include "errlog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct asgMember *ASMEMBERPVT; +typedef struct asgClient *ASCLIENTPVT; +typedef int (*ASINPUTFUNCPTR)(char *buf,int max_size); +typedef enum{ + asClientCOAR /*Change of access rights*/ + /*For now this is all*/ +} asClientStatus; + +typedef void (*ASCLIENTCALLBACK) (ASCLIENTPVT,asClientStatus); +/* The following routines are macros with the following syntax +long asCheckGet(ASCLIENTPVT asClientPvt); +long asCheckPut(ASCLIENTPVT asClientPvt); +*/ +#define asCheckGet(asClientPvt)\ + (asActive \ + ? ((asClientPvt)->access>=asREAD ? TRUE : FALSE)\ + : TRUE) +#define asCheckPut(asClientPvt)\ + (asActive \ + ? ((asClientPvt)->access>=asWRITE ? TRUE : FALSE)\ + : TRUE) +#define asTrapWriteBefore(asClientPvt,user,host,addr) \ + (((asActive) && (asClientPvt)->trapMask) \ + ? asTrapWriteBeforeWrite((user),(host),(addr)) \ + : 0) + +#define asTrapWriteAfter(pvt) if((pvt)) asTrapWriteAfterWrite((pvt)) + +epicsShareFunc long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction); +epicsShareFunc long epicsShareAPI asInitFile( + const char *filename,const char *substitutions); +epicsShareFunc long epicsShareAPI asInitFP(FILE *fp,const char *substitutions); +/*caller must provide permanent storage for asgName*/ +epicsShareFunc long epicsShareAPI asAddMember( + ASMEMBERPVT *asMemberPvt,const char *asgName); +epicsShareFunc long epicsShareAPI asRemoveMember(ASMEMBERPVT *asMemberPvt); +/*caller must provide permanent storage for newAsgName*/ +epicsShareFunc long epicsShareAPI asChangeGroup( + ASMEMBERPVT *asMemberPvt,const char *newAsgName); +epicsShareFunc void * epicsShareAPI asGetMemberPvt(ASMEMBERPVT asMemberPvt); +epicsShareFunc void epicsShareAPI asPutMemberPvt( + ASMEMBERPVT asMemberPvt,void *userPvt); +/*client must provide permanent storage for user and host*/ +epicsShareFunc long epicsShareAPI asAddClient( + ASCLIENTPVT *asClientPvt,ASMEMBERPVT asMemberPvt, + int asl,const char *user,char *host); +/*client must provide permanent storage for user and host*/ +epicsShareFunc long epicsShareAPI asChangeClient( + ASCLIENTPVT asClientPvt,int asl,const char *user,char *host); +epicsShareFunc long epicsShareAPI asRemoveClient(ASCLIENTPVT *asClientPvt); +epicsShareFunc void * epicsShareAPI asGetClientPvt(ASCLIENTPVT asClientPvt); +epicsShareFunc void epicsShareAPI asPutClientPvt( + ASCLIENTPVT asClientPvt,void *userPvt); +epicsShareFunc long epicsShareAPI asRegisterClientCallback( + ASCLIENTPVT asClientPvt, ASCLIENTCALLBACK pcallback); +epicsShareFunc long epicsShareAPI asComputeAllAsg(void); +/* following declared below after ASG is declared +epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); +*/ +epicsShareFunc long epicsShareAPI asCompute(ASCLIENTPVT asClientPvt); +epicsShareFunc int epicsShareAPI asDump( + void (*memcallback)(ASMEMBERPVT,FILE *), + void (*clientcallback)(ASCLIENTPVT,FILE *),int verbose); +epicsShareFunc int epicsShareAPI asDumpFP(FILE *fp, + void (*memcallback)(ASMEMBERPVT,FILE *), + void (*clientcallback)(ASCLIENTPVT,FILE *),int verbose); +epicsShareFunc int epicsShareAPI asDumpUag(const char *uagname); +epicsShareFunc int epicsShareAPI asDumpUagFP(FILE *fp,const char *uagname); +epicsShareFunc int epicsShareAPI asDumpHag(const char *hagname); +epicsShareFunc int epicsShareAPI asDumpHagFP(FILE *fp,const char *hagname); +epicsShareFunc int epicsShareAPI asDumpRules(const char *asgname); +epicsShareFunc int epicsShareAPI asDumpRulesFP(FILE *fp,const char *asgname); +epicsShareFunc int epicsShareAPI asDumpMem(const char *asgname, + void (*memcallback)(ASMEMBERPVT,FILE *),int clients); +epicsShareFunc int epicsShareAPI asDumpMemFP(FILE *fp,const char *asgname, + void (*memcallback)(ASMEMBERPVT,FILE *),int clients); +epicsShareFunc int epicsShareAPI asDumpHash(void); +epicsShareFunc int epicsShareAPI asDumpHashFP(FILE *fp); + +epicsShareFunc void * epicsShareAPI asTrapWriteBeforeWrite( + const char *userid,const char *hostid,void *addr); + +epicsShareFunc void epicsShareAPI asTrapWriteAfterWrite(void *pvt); + +#define S_asLib_clientsExist (M_asLib| 1) /*Client Exists*/ +#define S_asLib_noUag (M_asLib| 2) /*User Access Group does not exist*/ +#define S_asLib_noHag (M_asLib| 3) /*Host Access Group does not exist*/ +#define S_asLib_noAccess (M_asLib| 4) /*access security: no access allowed*/ +#define S_asLib_noModify (M_asLib| 5) /*access security: no modification allowed*/ +#define S_asLib_badConfig (M_asLib| 6) /*access security: bad configuration file*/ +#define S_asLib_badCalc (M_asLib| 7) /*access security: bad calculation espression*/ +#define S_asLib_dupAsg (M_asLib| 8) /*Duplicate Access Security Group */ +#define S_asLib_InitFailed (M_asLib| 9) /*access security: Init failed*/ +#define S_asLib_asNotActive (M_asLib|10) /*access security is not active*/ +#define S_asLib_badMember (M_asLib|11) /*access security: bad ASMEMBERPVT*/ +#define S_asLib_badClient (M_asLib|12) /*access security: bad ASCLIENTPVT*/ +#define S_asLib_badAsg (M_asLib|13) /*access security: bad ASG*/ +#define S_asLib_noMemory (M_asLib|14) /*access security: no Memory */ + +/*Private declarations */ +epicsShareExtern int asActive; + +/* definition of access rights*/ +typedef enum{asNOACCESS,asREAD,asWRITE} asAccessRights; + +struct gphPvt; + +/*Base pointers for access security*/ +typedef struct asBase{ + ELLLIST uagList; + ELLLIST hagList; + ELLLIST asgList; + struct gphPvt *phash; +} ASBASE; + +epicsShareExtern volatile ASBASE *pasbase; + +/*Defs for User Access Groups*/ +typedef struct{ + ELLNODE node; + char *user; +} UAGNAME; +typedef struct uag{ + ELLNODE node; + char *name; + ELLLIST list; /*list of UAGNAME*/ +} UAG; +/*Defs for Host Access Groups*/ +typedef struct{ + ELLNODE node; + char *host; +} HAGNAME; +typedef struct hag{ + ELLNODE node; + char *name; + ELLLIST list; /*list of HAGNAME*/ +} HAG; +/*Defs for Access SecurityGroups*/ +typedef struct { + ELLNODE node; + UAG *puag; +}ASGUAG; +typedef struct { + ELLNODE node; + HAG *phag; +}ASGHAG; +#define AS_TRAP_WRITE 1 +typedef struct{ + ELLNODE node; + asAccessRights access; + int level; + unsigned long inpUsed; /*bitmap of which inputs are used*/ + int result; /*Result of calc converted to TRUE/FALSE*/ + char *calc; + void *rpcl; + ELLLIST uagList; /*List of ASGUAG*/ + ELLLIST hagList; /*List of ASGHAG*/ + int trapMask; +} ASGRULE; +typedef struct{ + ELLNODE node; + char *inp; + void *capvt; + struct asg *pasg; + int inpIndex; +}ASGINP; + +typedef struct asg{ + ELLNODE node; + char *name; + ELLLIST inpList; + ELLLIST ruleList; + ELLLIST memberList; + double *pavalue; /*pointer to array of input values*/ + unsigned long inpBad; /*bitmap of which inputs are bad*/ + unsigned long inpChanged; /*bitmap of inputs that changed*/ +} ASG; +typedef struct asgMember { + ELLNODE node; + ASG *pasg; + ELLLIST clientList; + const char *asgName; + void *userPvt; +} ASGMEMBER; + +typedef struct asgClient { + ELLNODE node; + ASGMEMBER *pasgMember; + const char *user; + char *host; + void *userPvt; + ASCLIENTCALLBACK pcallback; + int level; + asAccessRights access; + int trapMask; +} ASGCLIENT; + +epicsShareFunc long epicsShareAPI asComputeAsg(ASG *pasg); +/*following is "friend" function*/ +epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size); +epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str); +#ifdef __cplusplus +} +#endif + +#endif /*INCasLibh*/ diff --git a/src/as/asLib.y b/src/as/asLib.y new file mode 100644 index 000000000..7e2803158 --- /dev/null +++ b/src/as/asLib.y @@ -0,0 +1,232 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +%{ +static int yyerror(char *); +static int yy_start; +#include "asLibRoutines.c" +static int yyFailed = FALSE; +static int line_num=1; +static UAG *yyUag=NULL; +static HAG *yyHag=NULL; +static ASG *yyAsg=NULL; +static ASGRULE *yyAsgRule=NULL; +%} + +%start asconfig + +%token tokenUAG tokenHAG tokenASG tokenRULE tokenCALC +%token tokenINP +%token tokenINTEGER +%token tokenSTRING + +%union +{ + int Int; + char Char; + char *Str; + double Real; +} + +%% + +asconfig: asconfig asconfig_item + | asconfig_item + +asconfig_item: tokenUAG uag_head uag_body + | tokenUAG uag_head + | tokenHAG hag_head hag_body + | tokenHAG hag_head + | tokenASG asg_head asg_body + | tokenASG asg_head + ; + +uag_head: '(' tokenSTRING ')' + { + yyUag = asUagAdd($2); + if(!yyUag) yyerror(""); + free((void *)$2); + } + ; + +uag_body: '{' uag_user_list '}' + { + ; + } + ; + +uag_user_list: uag_user_list ',' uag_user_list_name + | uag_user_list_name + ; + +uag_user_list_name: tokenSTRING + { + if (asUagAddUser(yyUag,$1)) + yyerror(""); + free((void *)$1); + } + ; + +hag_head: '(' tokenSTRING ')' + { + yyHag = asHagAdd($2); + if(!yyHag) yyerror(""); + free((void *)$2); + } + ; + +hag_body: '{' hag_host_list '}' + ; + +hag_host_list: hag_host_list ',' hag_host_list_name + | hag_host_list_name + ; + +hag_host_list_name: tokenSTRING + { + if (asHagAddHost(yyHag,$1)) + yyerror(""); + free((void *)$1); + } + ; + +asg_head: '(' tokenSTRING ')' + { + yyAsg = asAsgAdd($2); + if(!yyAsg) yyerror(""); + free((void *)$2); + } + ; + +asg_body: '{' asg_body_list '}' + { + } + +asg_body_list: asg_body_list asg_body_item + | asg_body_item + +asg_body_item: inp_config | rule_config + ; + +inp_config: tokenINP '(' tokenSTRING ')' + { + if (asAsgAddInp(yyAsg,$3,$1)) + yyerror(""); + free((void *)$3); + } + ; + +rule_config: tokenRULE rule_head rule_body + | tokenRULE rule_head + +rule_head: rule_head_manditory rule_head_options + +rule_head_manditory: '(' tokenINTEGER ',' tokenSTRING + { + asAccessRights rights; + + if((strcmp($4,"NONE")==0)) { + rights=asNOACCESS; + } else if((strcmp($4,"READ")==0)) { + rights=asREAD; + } else if((strcmp($4,"WRITE")==0)) { + rights=asWRITE; + } else { + yyerror("Access rights must be NONE, READ or WRITE"); + rights = asNOACCESS; + } + yyAsgRule = asAsgAddRule(yyAsg,rights,$2); + free((void *)$4); + } + ; + +rule_head_options: ')' + | rule_log_options + +rule_log_options: ',' tokenSTRING ')' + { + if((strcmp($2,"TRAPWRITE")==0)) { + long status; + status = asAsgAddRuleOptions(yyAsgRule,AS_TRAP_WRITE); + if(status) yyerror(""); + } else if((strcmp($2,"NOTRAPWRITE")!=0)) { + yyerror("Log options must be TRAPWRITE or NOTRAPWRITE"); + } + free((void *)$2); + } + ; + +rule_body: '{' rule_list '}' + ; + +rule_list: rule_list rule_list_item + | rule_list_item + ; + +rule_list_item: tokenUAG '(' rule_uag_list ')' + | tokenHAG '(' rule_hag_list ')' + | tokenCALC '(' tokenSTRING ')' + { + if (asAsgRuleCalc(yyAsgRule,$3)) + yyerror(""); + free((void *)$3); + } + ; + +rule_uag_list: rule_uag_list ',' rule_uag_list_name + | rule_uag_list_name + ; + +rule_uag_list_name: tokenSTRING + { + if (asAsgRuleUagAdd(yyAsgRule,$1)) + yyerror(""); + free((void *)$1); + } + ; + +rule_hag_list: rule_hag_list ',' rule_hag_list_name + | rule_hag_list_name + ; + +rule_hag_list_name: tokenSTRING + { + if (asAsgRuleHagAdd(yyAsgRule,$1)) + yyerror(""); + free((void *)$1); + } + ; +%% + +#include "asLib_lex.c" + +static int yyerror(char *str) +{ + if (strlen(str)) epicsPrintf("%s\n", str); + epicsPrintf("Access Security file error at line %d\n", + line_num); + yyFailed = TRUE; + return(0); +} +static int myParse(ASINPUTFUNCPTR inputfunction) +{ + static int FirstFlag = 1; + int rtnval; + + my_yyinput = &inputfunction; + if (!FirstFlag) { + line_num=1; + yyFailed = FALSE; + yyreset(); + yyrestart(NULL); + } + FirstFlag = 0; + rtnval = yyparse(); + if(rtnval!=0 || yyFailed) return(-1); else return(0); +} diff --git a/src/as/asLibRoutines.c b/src/as/asLibRoutines.c new file mode 100644 index 000000000..2a5c0d798 --- /dev/null +++ b/src/as/asLibRoutines.c @@ -0,0 +1,1352 @@ +/* share/src/as/asLibRoutines.c */ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* Author: Marty Kraimer Date: 10-15-93 */ + +#include +#include +#include +#include +#include + +#include "epicsStdioRedirect.h" +#include "dbDefs.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsMutex.h" +#include "epicsPrint.h" +#include "gpHash.h" +#include "freeList.h" +#include "macLib.h" +#include "postfix.h" + +static epicsMutexId asLock; +#define LOCK epicsMutexMustLock(asLock) +#define UNLOCK epicsMutexUnlock(asLock) + +#define epicsExportSharedSymbols +#include "asLib.h" + +/*following must be global because asCa nneeds it*/ +epicsShareDef ASBASE volatile *pasbase=NULL; +static ASBASE *pasbasenew=NULL; +epicsShareDef int asActive = FALSE; + +static void *freeListPvt = NULL; + + +#define DEFAULT "DEFAULT" + +/* Defined in asLib.y */ +static int myParse(ASINPUTFUNCPTR inputfunction); + +/*private routines */ +static long asAddMemberPvt(ASMEMBERPVT *pasMemberPvt,const char *asgName); +static long asComputeAllAsgPvt(void); +static long asComputeAsgPvt(ASG *pasg); +static long asComputePvt(ASCLIENTPVT asClientPvt); +static void asFreeAll(ASBASE *pasbase); +static UAG *asUagAdd(const char *uagName); +static long asUagAddUser(UAG *puag,const char *user); +static HAG *asHagAdd(const char *hagName); +static long asHagAddHost(HAG *phag,const char *host); +static ASG *asAsgAdd(const char *asgName); +static long asAsgAddInp(ASG *pasg,const char *inp,int inpIndex); +static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level); +static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask); +static long asAsgRuleUagAdd(ASGRULE *pasgrule,const char *name); +static long asAsgRuleHagAdd(ASGRULE *pasgrule,const char *name); +static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc); + +/* + asInitialize can be called while access security is already active. + This is accomplished by doing the following: + + The version pointed to by pasbase is kept as is but locked against changes + A new version is created and pointed to by pasbasenew + If anything goes wrong. The original version is kept. This results is some + wasted space but at least things still work. + If the new access security configuration is successfully read then: + the old memberList is moved from old to new. + the old structures are freed. +*/ +static void asInitializeOnce(void *arg) +{ + asLock = epicsMutexMustCreate(); +} +long epicsShareAPI asInitialize(ASINPUTFUNCPTR inputfunction) +{ + ASG *pasg; + long status; + ASBASE *pasbaseold; + GPHENTRY *pgphentry; + UAG *puag; + UAGNAME *puagname; + HAG *phag; + HAGNAME *phagname; + static epicsThreadOnceId asInitializeOnceFlag = EPICS_THREAD_ONCE_INIT; + + epicsThreadOnce(&asInitializeOnceFlag,asInitializeOnce,(void *)0); + LOCK; + pasbasenew = asCalloc(1,sizeof(ASBASE)); + if(!freeListPvt) freeListInitPvt(&freeListPvt,sizeof(ASGCLIENT),20); + ellInit(&pasbasenew->uagList); + ellInit(&pasbasenew->hagList); + ellInit(&pasbasenew->asgList); + asAsgAdd(DEFAULT); + status = myParse(inputfunction); + if(status) { + status = S_asLib_badConfig; + /*Not safe to call asFreeAll */ + UNLOCK; + return(status); + } + pasg = (ASG *)ellFirst(&pasbasenew->asgList); + while(pasg) { + pasg->pavalue = asCalloc(CALCPERFORM_NARGS, sizeof(double)); + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + gphInitPvt(&pasbasenew->phash, 256); + /*Hash each uagname and each hagname*/ + puag = (UAG *)ellFirst(&pasbasenew->uagList); + while(puag) { + puagname = (UAGNAME *)ellFirst(&puag->list); + while(puagname) { + pgphentry = gphAdd(pasbasenew->phash,puagname->user,puag); + if(!pgphentry) { + epicsPrintf("Duplicated user '%s' in UAG '%s'\n", + puagname->user, puag->name); + } + puagname = (UAGNAME *)ellNext((ELLNODE *)puagname); + } + puag = (UAG *)ellNext((ELLNODE *)puag); + } + phag = (HAG *)ellFirst(&pasbasenew->hagList); + while(phag) { + phagname = (HAGNAME *)ellFirst(&phag->list); + while(phagname) { + pgphentry = gphAdd(pasbasenew->phash,phagname->host,phag); + if(!pgphentry) { + epicsPrintf("Duplicated host '%s' in HAG '%s'\n", + phagname->host, phag->name); + } + phagname = (HAGNAME *)ellNext((ELLNODE *)phagname); + } + phag = (HAG *)ellNext((ELLNODE *)phag); + } + pasbaseold = (ASBASE *)pasbase; + pasbase = (ASBASE volatile *)pasbasenew; + if(pasbaseold) { + ASG *poldasg; + ASGMEMBER *poldmem; + ASGMEMBER *pnextoldmem; + + poldasg = (ASG *)ellFirst(&pasbaseold->asgList); + while(poldasg) { + poldmem = (ASGMEMBER *)ellFirst(&poldasg->memberList); + while(poldmem) { + pnextoldmem = (ASGMEMBER *)ellNext((ELLNODE *)poldmem); + ellDelete(&poldasg->memberList,(ELLNODE *)poldmem); + status = asAddMemberPvt(&poldmem,poldmem->asgName); + poldmem = pnextoldmem; + } + poldasg = (ASG *)ellNext((ELLNODE *)poldasg); + } + asFreeAll(pasbaseold); + } + asActive = TRUE; + UNLOCK; + return(0); +} + +long epicsShareAPI asInitFile(const char *filename,const char *substitutions) +{ + FILE *fp; + long status; + + fp = fopen(filename,"r"); + if(!fp) { + errlogPrintf("asInitFile: Can't open file '%s'\n", filename); + return(S_asLib_badConfig); + } + status = asInitFP(fp,substitutions); + if(fclose(fp)==EOF) { + errMessage(0,"asInitFile: fclose failed!"); + if(!status) status = S_asLib_badConfig; + } + return(status); +} + +#define BUF_SIZE 200 +static char *my_buffer; +static char *my_buffer_ptr; +static FILE *stream; +static char *mac_input_buffer=NULL; +static MAC_HANDLE *macHandle = NULL; + +static int myInputFunction(char *buf, int max_size) +{ + int l,n; + char *fgetsRtn; + + if(*my_buffer_ptr==0) { + if(macHandle) { + fgetsRtn = fgets(mac_input_buffer,BUF_SIZE,stream); + if(fgetsRtn) { + n = macExpandString(macHandle,mac_input_buffer, + my_buffer,BUF_SIZE); + if(n<0) { + epicsPrintf("access security: macExpandString failed\n" + "input line: %s\n",mac_input_buffer); + return(0); + } + } + } else { + fgetsRtn = fgets(my_buffer,BUF_SIZE,stream); + } + if(fgetsRtn==NULL) return(0); + my_buffer_ptr = my_buffer; + } + l = strlen(my_buffer_ptr); + n = (l<=max_size ? l : max_size); + memcpy(buf,my_buffer_ptr,n); + my_buffer_ptr += n; + return(n); +} + +long epicsShareAPI asInitFP(FILE *fp,const char *substitutions) +{ + char buffer[BUF_SIZE]; + char mac_buffer[BUF_SIZE]; + long status; + char **macPairs; + + buffer[0] = 0; + my_buffer = buffer; + my_buffer_ptr = my_buffer; + stream = fp; + if(substitutions) { + if((status = macCreateHandle(&macHandle,NULL))) { + errMessage(status,"asInitFP: macCreateHandle error"); + return(status); + } + macParseDefns(macHandle,substitutions,&macPairs); + if(macPairs ==NULL) { + macDeleteHandle(macHandle); + macHandle = NULL; + } else { + macInstallMacros(macHandle,macPairs); + free((void *)macPairs); + mac_input_buffer = mac_buffer; + } + } + status = asInitialize(myInputFunction); + if(macHandle) { + macDeleteHandle(macHandle); + macHandle = NULL; + } + return(status); +} + +long epicsShareAPI asAddMember(ASMEMBERPVT *pasMemberPvt,const char *asgName) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asAddMemberPvt(pasMemberPvt,asgName); + UNLOCK; + return(status); +} + +long epicsShareAPI asRemoveMember(ASMEMBERPVT *asMemberPvt) +{ + ASGMEMBER *pasgmember; + + if(!asActive) return(S_asLib_asNotActive); + pasgmember = *asMemberPvt; + if(!pasgmember) return(S_asLib_badMember); + LOCK; + if (ellCount(&pasgmember->clientList) > 0) { + UNLOCK; + return(S_asLib_clientsExist); + } + if(pasgmember->pasg) { + ellDelete(&pasgmember->pasg->memberList,(ELLNODE *)pasgmember); + } else { + errMessage(-1,"Logic error in asRemoveMember"); + UNLOCK; + return(-1); + } + free((void *)pasgmember); + *asMemberPvt = NULL; + UNLOCK; + return(0); +} + +long epicsShareAPI asChangeGroup(ASMEMBERPVT *asMemberPvt,const char *newAsgName) +{ + ASGMEMBER *pasgmember; + long status; + + if(!asActive) return(S_asLib_asNotActive); + pasgmember = *asMemberPvt; + if(!pasgmember) return(S_asLib_badMember); + LOCK; + if(pasgmember->pasg) { + ellDelete(&pasgmember->pasg->memberList,(ELLNODE *)pasgmember); + } else { + errMessage(-1,"Logic error in asChangeGroup"); + UNLOCK; + return(-1); + } + status = asAddMemberPvt(asMemberPvt,newAsgName); + UNLOCK; + return(status); +} + +void * epicsShareAPI asGetMemberPvt(ASMEMBERPVT asMemberPvt) +{ + ASGMEMBER *pasgmember = asMemberPvt; + + if(!asActive) return(NULL); + if(!pasgmember) return(NULL); + return(pasgmember->userPvt); +} + +void epicsShareAPI asPutMemberPvt(ASMEMBERPVT asMemberPvt,void *userPvt) +{ + ASGMEMBER *pasgmember = asMemberPvt; + + if(!asActive) return; + if(!pasgmember) return; + pasgmember->userPvt = userPvt; + return; +} + +long epicsShareAPI asAddClient(ASCLIENTPVT *pasClientPvt,ASMEMBERPVT asMemberPvt, + int asl,const char *user,char *host) +{ + ASGMEMBER *pasgmember = asMemberPvt; + ASGCLIENT *pasgclient; + int len, i; + + long status; + if(!asActive) return(S_asLib_asNotActive); + if(!pasgmember) return(S_asLib_badMember); + pasgclient = freeListCalloc(freeListPvt); + if(!pasgclient) return(S_asLib_noMemory); + len = strlen(host); + for (i = 0; i < len; i++) { + host[i] = (char)tolower((int)host[i]); + } + *pasClientPvt = pasgclient; + pasgclient->pasgMember = asMemberPvt; + pasgclient->level = asl; + pasgclient->user = user; + pasgclient->host = host; + LOCK; + ellAdd(&pasgmember->clientList,(ELLNODE *)pasgclient); + status = asComputePvt(pasgclient); + UNLOCK; + return(status); +} + +long epicsShareAPI asChangeClient( + ASCLIENTPVT asClientPvt,int asl,const char *user,char *host) +{ + ASGCLIENT *pasgclient = asClientPvt; + long status; + int len, i; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + len = strlen(host); + for (i = 0; i < len; i++) { + host[i] = (char)tolower((int)host[i]); + } + LOCK; + pasgclient->level = asl; + pasgclient->user = user; + pasgclient->host = host; + status = asComputePvt(pasgclient); + UNLOCK; + return(status); +} + +long epicsShareAPI asRemoveClient(ASCLIENTPVT *asClientPvt) +{ + ASGCLIENT *pasgclient = *asClientPvt; + ASGMEMBER *pasgMember; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + LOCK; + pasgMember = pasgclient->pasgMember; + if(!pasgMember) { + errMessage(-1,"asRemoveClient: No ASGMEMBER"); + UNLOCK; + return(-1); + } + ellDelete(&pasgMember->clientList,(ELLNODE *)pasgclient); + UNLOCK; + freeListFree(freeListPvt,pasgclient); + *asClientPvt = NULL; + return(0); +} + +long epicsShareAPI asRegisterClientCallback(ASCLIENTPVT asClientPvt, + ASCLIENTCALLBACK pcallback) +{ + ASGCLIENT *pasgclient = asClientPvt; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + LOCK; + pasgclient->pcallback = pcallback; + (*pasgclient->pcallback)(pasgclient,asClientCOAR); + UNLOCK; + return(0); +} + +void * epicsShareAPI asGetClientPvt(ASCLIENTPVT asClientPvt) +{ + ASGCLIENT *pasgclient = asClientPvt; + + if(!asActive) return(NULL); + if(!pasgclient) return(NULL); + return(pasgclient->userPvt); +} + +void epicsShareAPI asPutClientPvt(ASCLIENTPVT asClientPvt,void *userPvt) +{ + ASGCLIENT *pasgclient = asClientPvt; + if(!asActive) return; + if(!pasgclient) return; + LOCK; + pasgclient->userPvt = userPvt; + UNLOCK; +} + +long epicsShareAPI asComputeAllAsg(void) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asComputeAllAsgPvt(); + UNLOCK; + return(status); +} + +long epicsShareAPI asComputeAsg(ASG *pasg) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asComputeAsgPvt(pasg); + UNLOCK; + return(status); +} + +long epicsShareAPI asCompute(ASCLIENTPVT asClientPvt) +{ + long status; + + if(!asActive) return(S_asLib_asNotActive); + LOCK; + status = asComputePvt(asClientPvt); + UNLOCK; + return(status); +} + +/*The dump routines do not lock. Thus they may get inconsistant data.*/ +/*HOWEVER if they did lock and a user interrupts one of then then BAD BAD*/ +static const char *asAccessName[] = {"NONE","READ","WRITE"}; +static const char *asTrapOption[] = {"NOTRAPWRITE","TRAPWRITE"}; +static const char *asLevelName[] = {"ASL0","ASL1"}; +int epicsShareAPI asDump( + void (*memcallback)(struct asgMember *,FILE *), + void (*clientcallback)(struct asgClient *,FILE *), + int verbose) +{ + return asDumpFP(stdout,memcallback,clientcallback,verbose); +} + +int epicsShareAPI asDumpFP( + FILE *fp, + void (*memcallback)(struct asgMember *,FILE *), + void (*clientcallback)(struct asgClient *,FILE *), + int verbose) +{ + UAG *puag; + UAGNAME *puagname; + HAG *phag; + HAGNAME *phagname; + ASG *pasg; + ASGINP *pasginp; + ASGRULE *pasgrule; + ASGHAG *pasghag; + ASGUAG *pasguag; + ASGMEMBER *pasgmember; + ASGCLIENT *pasgclient; + + if(!asActive) return(0); + puag = (UAG *)ellFirst(&pasbase->uagList); + if(!puag) fprintf(fp,"No UAGs\n"); + while(puag) { + fprintf(fp,"UAG(%s)",puag->name); + puagname = (UAGNAME *)ellFirst(&puag->list); + if(puagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(puagname) { + fprintf(fp,"%s",puagname->user); + puagname = (UAGNAME *)ellNext((ELLNODE *)puagname); + if(puagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + puag = (UAG *)ellNext((ELLNODE *)puag); + } + phag = (HAG *)ellFirst(&pasbase->hagList); + if(!phag) fprintf(fp,"No HAGs\n"); + while(phag) { + fprintf(fp,"HAG(%s)",phag->name); + phagname = (HAGNAME *)ellFirst(&phag->list); + if(phagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(phagname) { + fprintf(fp,"%s",phagname->host); + phagname = (HAGNAME *)ellNext((ELLNODE *)phagname); + if(phagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + phag = (HAG *)ellNext((ELLNODE *)phag); + } + pasg = (ASG *)ellFirst(&pasbase->asgList); + if(!pasg) fprintf(fp,"No ASGs\n"); + while(pasg) { + int print_end_brace; + + fprintf(fp,"ASG(%s)",pasg->name); + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + if(pasginp || pasgrule) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + while(pasginp) { + + fprintf(fp,"\tINP%c(%s)",(pasginp->inpIndex + 'A'),pasginp->inp); + if(verbose) { + if((pasg->inpBad & (1ul << pasginp->inpIndex))) + fprintf(fp," INVALID"); + else + fprintf(fp," VALID"); + fprintf(fp," value=%f",pasg->pavalue[pasginp->inpIndex]); + } + fprintf(fp,"\n"); + pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); + } + while(pasgrule) { + int print_end_brace; + + fprintf(fp,"\tRULE(%d,%s,%s)", + pasgrule->level,asAccessName[pasgrule->access], + asTrapOption[pasgrule->trapMask]); + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasguag || pasghag || pasgrule->calc) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + if(pasguag) fprintf(fp,"\t\tUAG("); + while(pasguag) { + fprintf(fp,"%s",pasguag->puag->name); + pasguag = (ASGUAG *)ellNext((ELLNODE *)pasguag); + if(pasguag) fprintf(fp,","); else fprintf(fp,")\n"); + } + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasghag) fprintf(fp,"\t\tHAG("); + while(pasghag) { + fprintf(fp,"%s",pasghag->phag->name); + pasghag = (ASGHAG *)ellNext((ELLNODE *)pasghag); + if(pasghag) fprintf(fp,","); else fprintf(fp,")\n"); + } + if(pasgrule->calc) { + fprintf(fp,"\t\tCALC(\"%s\")",pasgrule->calc); + if(verbose) + fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); + fprintf(fp,"\n"); + } + if(print_end_brace) fprintf(fp,"\t}\n"); + pasgrule = (ASGRULE *)ellNext((ELLNODE *)pasgrule); + } + pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); + if(!verbose) pasgmember = NULL; + if(pasgmember) fprintf(fp,"\tMEMBERLIST\n"); + while(pasgmember) { + if(strlen(pasgmember->asgName)==0) + fprintf(fp,"\t\t"); + else + fprintf(fp,"\t\t%s",pasgmember->asgName); + if(memcallback) memcallback(pasgmember,fp); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + while(pasgclient) { + fprintf(fp,"\t\t\t %s %s",pasgclient->user,pasgclient->host); + if(pasgclient->level>=0 && pasgclient->level<=1) + fprintf(fp," %s",asLevelName[pasgclient->level]); + else + fprintf(fp," Illegal Level %d",pasgclient->level); + if(pasgclient->access>=0 && pasgclient->access<=2) + fprintf(fp," %s %s", + asAccessName[pasgclient->access], + asTrapOption[pasgclient->trapMask]); + else + fprintf(fp," Illegal Access %d",pasgclient->access); + if(clientcallback) clientcallback(pasgclient,fp); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellNext((ELLNODE *)pasgclient); + } + pasgmember = (ASGMEMBER *)ellNext((ELLNODE *)pasgmember); + } + if(print_end_brace) fprintf(fp,"}\n"); + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + return(0); +} + +int epicsShareAPI asDumpUag(const char *uagname) +{ + return asDumpUagFP(stdout,uagname); +} + +int epicsShareAPI asDumpUagFP(FILE *fp,const char *uagname) +{ + UAG *puag; + UAGNAME *puagname; + + if(!asActive) return(0); + puag = (UAG *)ellFirst(&pasbase->uagList); + if(!puag) fprintf(fp,"No UAGs\n"); + while(puag) { + if(uagname && strcmp(uagname,puag->name)!=0) { + puag = (UAG *)ellNext((ELLNODE *)puag); + continue; + } + fprintf(fp,"UAG(%s)",puag->name); + puagname = (UAGNAME *)ellFirst(&puag->list); + if(puagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(puagname) { + fprintf(fp,"%s",puagname->user); + puagname = (UAGNAME *)ellNext((ELLNODE *)puagname); + if(puagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + puag = (UAG *)ellNext((ELLNODE *)puag); + } + return(0); +} + +int epicsShareAPI asDumpHag(const char *hagname) +{ + return asDumpHagFP(stdout,hagname); +} + +int epicsShareAPI asDumpHagFP(FILE *fp,const char *hagname) +{ + HAG *phag; + HAGNAME *phagname; + + if(!asActive) return(0); + phag = (HAG *)ellFirst(&pasbase->hagList); + if(!phag) fprintf(fp,"No HAGs\n"); + while(phag) { + if(hagname && strcmp(hagname,phag->name)!=0) { + phag = (HAG *)ellNext((ELLNODE *)phag); + continue; + } + fprintf(fp,"HAG(%s)",phag->name); + phagname = (HAGNAME *)ellFirst(&phag->list); + if(phagname) fprintf(fp," {"); else fprintf(fp,"\n"); + while(phagname) { + fprintf(fp,"%s",phagname->host); + phagname = (HAGNAME *)ellNext((ELLNODE *)phagname); + if(phagname) fprintf(fp,","); else fprintf(fp,"}\n"); + } + phag = (HAG *)ellNext((ELLNODE *)phag); + } + return(0); +} + +int epicsShareAPI asDumpRules(const char *asgname) +{ + return asDumpRulesFP(stdout,asgname); +} + +int epicsShareAPI asDumpRulesFP(FILE *fp,const char *asgname) +{ + ASG *pasg; + ASGINP *pasginp; + ASGRULE *pasgrule; + ASGHAG *pasghag; + ASGUAG *pasguag; + + if(!asActive) return(0); + pasg = (ASG *)ellFirst(&pasbase->asgList); + if(!pasg) fprintf(fp,"No ASGs\n"); + while(pasg) { + int print_end_brace; + + if(asgname && strcmp(asgname,pasg->name)!=0) { + pasg = (ASG *)ellNext((ELLNODE *)pasg); + continue; + } + fprintf(fp,"ASG(%s)",pasg->name); + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + if(pasginp || pasgrule) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + while(pasginp) { + + fprintf(fp,"\tINP%c(%s)",(pasginp->inpIndex + 'A'),pasginp->inp); + if ((pasg->inpBad & (1ul << pasginp->inpIndex))) + fprintf(fp," INVALID"); + fprintf(fp," value=%f",pasg->pavalue[pasginp->inpIndex]); + fprintf(fp,"\n"); + pasginp = (ASGINP *)ellNext((ELLNODE *)pasginp); + } + while(pasgrule) { + int print_end_brace; + + fprintf(fp,"\tRULE(%d,%s,%s)", + pasgrule->level,asAccessName[pasgrule->access], + asTrapOption[pasgrule->trapMask]); + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasguag || pasghag || pasgrule->calc) { + fprintf(fp," {\n"); + print_end_brace = TRUE; + } else { + fprintf(fp,"\n"); + print_end_brace = FALSE; + } + if(pasguag) fprintf(fp,"\t\tUAG("); + while(pasguag) { + fprintf(fp,"%s",pasguag->puag->name); + pasguag = (ASGUAG *)ellNext((ELLNODE *)pasguag); + if(pasguag) fprintf(fp,","); else fprintf(fp,")\n"); + } + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + if(pasghag) fprintf(fp,"\t\tHAG("); + while(pasghag) { + fprintf(fp,"%s",pasghag->phag->name); + pasghag = (ASGHAG *)ellNext((ELLNODE *)pasghag); + if(pasghag) fprintf(fp,","); else fprintf(fp,")\n"); + } + if(pasgrule->calc) { + fprintf(fp,"\t\tCALC(\"%s\")",pasgrule->calc); + fprintf(fp," result=%s",(pasgrule->result==1 ? "TRUE" : "FALSE")); + fprintf(fp,"\n"); + } + if(print_end_brace) fprintf(fp,"\t}\n"); + pasgrule = (ASGRULE *)ellNext((ELLNODE *)pasgrule); + } + if(print_end_brace) fprintf(fp,"}\n"); + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + return(0); +} + +int epicsShareAPI asDumpMem(const char *asgname,void (*memcallback)(ASMEMBERPVT,FILE *), + int clients) +{ + return asDumpMemFP(stdout,asgname,memcallback,clients); +} + +int epicsShareAPI asDumpMemFP(FILE *fp,const char *asgname, + void (*memcallback)(ASMEMBERPVT,FILE *),int clients) +{ + ASG *pasg; + ASGMEMBER *pasgmember; + ASGCLIENT *pasgclient; + + if(!asActive) return(0); + pasg = (ASG *)ellFirst(&pasbase->asgList); + if(!pasg) fprintf(fp,"No ASGs\n"); + while(pasg) { + + if(asgname && strcmp(asgname,pasg->name)!=0) { + pasg = (ASG *)ellNext((ELLNODE *)pasg); + continue; + } + fprintf(fp,"ASG(%s)\n",pasg->name); + pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); + if(pasgmember) fprintf(fp,"\tMEMBERLIST\n"); + while(pasgmember) { + if(strlen(pasgmember->asgName)==0) + fprintf(fp,"\t\t"); + else + fprintf(fp,"\t\t%s",pasgmember->asgName); + if(memcallback) memcallback(pasgmember,fp); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + if(!clients) pasgclient = NULL; + while(pasgclient) { + fprintf(fp,"\t\t\t %s %s", + pasgclient->user,pasgclient->host); + if(pasgclient->level>=0 && pasgclient->level<=1) + fprintf(fp," %s",asLevelName[pasgclient->level]); + else + fprintf(fp," Illegal Level %d",pasgclient->level); + if(pasgclient->access>=0 && pasgclient->access<=2) + fprintf(fp," %s %s", + asAccessName[pasgclient->access], + asTrapOption[pasgclient->trapMask]); + else + fprintf(fp," Illegal Access %d",pasgclient->access); + fprintf(fp,"\n"); + pasgclient = (ASGCLIENT *)ellNext((ELLNODE *)pasgclient); + } + pasgmember = (ASGMEMBER *)ellNext((ELLNODE *)pasgmember); + } + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + return(0); +} + +epicsShareFunc int epicsShareAPI asDumpHash(void) +{ + return asDumpHashFP(stdout); +} + +epicsShareFunc int epicsShareAPI asDumpHashFP(FILE *fp) +{ + if(!asActive) return(0); + gphDumpFP(fp,pasbase->phash); + return(0); +} + +/*Start of private routines*/ +/* asCalloc is "friend" function */ +epicsShareFunc void * epicsShareAPI asCalloc(size_t nobj,size_t size) +{ + void *p; + + p=callocMustSucceed(nobj,size,"asCalloc"); + return(p); +} +epicsShareFunc char * epicsShareAPI asStrdup(unsigned char *str) +{ + size_t len = strlen((char *) str); + char *buf = asCalloc(1, len + 1); + strcpy(buf, (char *) str); + return buf; +} + +static long asAddMemberPvt(ASMEMBERPVT *pasMemberPvt,const char *asgName) +{ + ASGMEMBER *pasgmember; + ASG *pgroup; + ASGCLIENT *pasgclient; + + if(*pasMemberPvt) { + pasgmember = *pasMemberPvt; + } else { + pasgmember = asCalloc(1,sizeof(ASGMEMBER)); + ellInit(&pasgmember->clientList); + *pasMemberPvt = pasgmember; + } + pasgmember->asgName = asgName; + pgroup = (ASG *)ellFirst(&pasbase->asgList); + while(pgroup) { + if(strcmp(pgroup->name,pasgmember->asgName)==0) goto got_it; + pgroup = (ASG *)ellNext((ELLNODE *)pgroup); + } + /* Put it in DEFAULT*/ + pgroup = (ASG *)ellFirst(&pasbase->asgList); + while(pgroup) { + if(strcmp(pgroup->name,DEFAULT)==0) goto got_it; + pgroup = (ASG *)ellNext((ELLNODE *)pgroup); + } + errMessage(-1,"Logic Error in asAddMember"); + return(-1); +got_it: + pasgmember->pasg = pgroup; + ellAdd(&pgroup->memberList,(ELLNODE *)pasgmember); + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + while(pasgclient) { + asComputePvt((ASCLIENTPVT)pasgclient); + pasgclient = (ASGCLIENT *)ellNext((ELLNODE *)pasgclient); + } + return(0); +} + +static long asComputeAllAsgPvt(void) +{ + ASG *pasg; + + if(!asActive) return(S_asLib_asNotActive); + pasg = (ASG *)ellFirst(&pasbase->asgList); + while(pasg) { + asComputeAsgPvt(pasg); + pasg = (ASG *)ellNext((ELLNODE *)pasg); + } + return(0); +} + +static long asComputeAsgPvt(ASG *pasg) +{ + ASGRULE *pasgrule; + ASGMEMBER *pasgmember; + ASGCLIENT *pasgclient; + + if(!asActive) return(S_asLib_asNotActive); + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + while(pasgrule) { + double result = pasgrule->result; /* set for VAL */ + long status; + + if(pasgrule->calc && (pasg->inpChanged & pasgrule->inpUsed)) { + status = calcPerform(pasg->pavalue,&result,pasgrule->rpcl); + if(status) { + pasgrule->result = 0; + errMessage(status,"asComputeAsg"); + } else { + pasgrule->result = ((result>.99) && (result<1.01)) ? 1 : 0; + } + } + pasgrule = (ASGRULE *)ellNext((ELLNODE *)pasgrule); + } + pasg->inpChanged = FALSE; + pasgmember = (ASGMEMBER *)ellFirst(&pasg->memberList); + while(pasgmember) { + pasgclient = (ASGCLIENT *)ellFirst(&pasgmember->clientList); + while(pasgclient) { + asComputePvt((ASCLIENTPVT)pasgclient); + pasgclient = (ASGCLIENT *)ellNext((ELLNODE *)pasgclient); + } + pasgmember = (ASGMEMBER *)ellNext((ELLNODE *)pasgmember); + } + return(0); +} + +static long asComputePvt(ASCLIENTPVT asClientPvt) +{ + asAccessRights access=asNOACCESS; + int trapMask=0; + ASGCLIENT *pasgclient = asClientPvt; + ASGMEMBER *pasgMember; + ASG *pasg; + ASGRULE *pasgrule; + asAccessRights oldaccess; + GPHENTRY *pgphentry; + + if(!asActive) return(S_asLib_asNotActive); + if(!pasgclient) return(S_asLib_badClient); + pasgMember = pasgclient->pasgMember; + if(!pasgMember) return(S_asLib_badMember); + pasg = pasgMember->pasg; + if(!pasg) return(S_asLib_badAsg); + oldaccess=pasgclient->access; + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + while(pasgrule) { + if(access == asWRITE) break; + if(access>=pasgrule->access) goto next_rule; + if(pasgclient->level > pasgrule->level) goto next_rule; + /*if uagList is empty then no need to check uag*/ + if(ellCount(&pasgrule->uagList)>0){ + ASGUAG *pasguag; + UAG *puag; + + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + while(pasguag) { + if((puag = pasguag->puag)) { + pgphentry = gphFind(pasbase->phash,pasgclient->user,puag); + if(pgphentry) goto check_hag; + } + pasguag = (ASGUAG *)ellNext((ELLNODE *)pasguag); + } + goto next_rule; + } +check_hag: + /*if hagList is empty then no need to check hag*/ + if(ellCount(&pasgrule->hagList)>0) { + ASGHAG *pasghag; + HAG *phag; + + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + while(pasghag) { + if((phag = pasghag->phag)) { + pgphentry=gphFind(pasbase->phash,pasgclient->host,phag); + if(pgphentry) goto check_calc; + } + pasghag = (ASGHAG *)ellNext((ELLNODE *)pasghag); + } + goto next_rule; + } +check_calc: + if(!pasgrule->calc + || (!(pasg->inpBad & pasgrule->inpUsed) && (pasgrule->result==1))) { + access = pasgrule->access; + trapMask = pasgrule->trapMask; + } +next_rule: + pasgrule = (ASGRULE *)ellNext((ELLNODE *)pasgrule); + } + pasgclient->access = access; + pasgclient->trapMask = trapMask; + if(pasgclient->pcallback && oldaccess!=access) { + (*pasgclient->pcallback)(pasgclient,asClientCOAR); + } + return(0); +} + +static void asFreeAll(ASBASE *pasbase) +{ + UAG *puag; + UAGNAME *puagname; + HAG *phag; + HAGNAME *phagname; + ASG *pasg; + ASGINP *pasginp; + ASGRULE *pasgrule; + ASGHAG *pasghag; + ASGUAG *pasguag; + void *pnext; + + puag = (UAG *)ellFirst(&pasbase->uagList); + while(puag) { + puagname = (UAGNAME *)ellFirst(&puag->list); + while(puagname) { + pnext = ellNext((ELLNODE *)puagname); + ellDelete(&puag->list,(ELLNODE *)puagname); + free((void *)puagname); + puagname = pnext; + } + pnext = ellNext((ELLNODE *)puag); + ellDelete(&pasbase->uagList,(ELLNODE *)puag); + free((void *)puag); + puag = pnext; + } + phag = (HAG *)ellFirst(&pasbase->hagList); + while(phag) { + phagname = (HAGNAME *)ellFirst(&phag->list); + while(phagname) { + pnext = ellNext((ELLNODE *)phagname); + ellDelete(&phag->list,(ELLNODE *)phagname); + free((void *)phagname); + phagname = pnext; + } + pnext = ellNext((ELLNODE *)phag); + ellDelete(&pasbase->hagList,(ELLNODE *)phag); + free((void *)phag); + phag = pnext; + } + pasg = (ASG *)ellFirst(&pasbase->asgList); + while(pasg) { + free((void *)pasg->pavalue); + pasginp = (ASGINP *)ellFirst(&pasg->inpList); + while(pasginp) { + pnext = ellNext((ELLNODE *)pasginp); + ellDelete(&pasg->inpList,(ELLNODE *)pasginp); + free((void *)pasginp); + pasginp = pnext; + } + pasgrule = (ASGRULE *)ellFirst(&pasg->ruleList); + while(pasgrule) { + free((void *)pasgrule->calc); + free((void *)pasgrule->rpcl); + pasguag = (ASGUAG *)ellFirst(&pasgrule->uagList); + while(pasguag) { + pnext = ellNext((ELLNODE *)pasguag); + ellDelete(&pasgrule->uagList,(ELLNODE *)pasguag); + free((void *)pasguag); + pasguag = pnext; + } + pasghag = (ASGHAG *)ellFirst(&pasgrule->hagList); + while(pasghag) { + pnext = ellNext((ELLNODE *)pasghag); + ellDelete(&pasgrule->hagList,(ELLNODE *)pasghag); + free((void *)pasghag); + pasghag = pnext; + } + pnext = ellNext((ELLNODE *)pasgrule); + ellDelete(&pasg->ruleList,(ELLNODE *)pasgrule); + free((void *)pasgrule); + pasgrule = pnext; + } + pnext = ellNext((ELLNODE *)pasg); + ellDelete(&pasbase->asgList,(ELLNODE *)pasg); + free((void *)pasg); + pasg = pnext; + } + gphFreeMem(pasbase->phash); + free((void *)pasbase); +} + +/*Beginning of routines called by lex code*/ +static UAG *asUagAdd(const char *uagName) +{ + UAG *pprev; + UAG *pnext; + UAG *puag; + int cmpvalue; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + /*Insert in alphabetic order*/ + pnext = (UAG *)ellFirst(&pasbase->uagList); + while(pnext) { + cmpvalue = strcmp(uagName,pnext->name); + if(cmpvalue < 0) break; + if(cmpvalue==0) { + errlogPrintf("Duplicate User Access Group named '%s'\n", uagName); + return(NULL); + } + pnext = (UAG *)ellNext((ELLNODE *)pnext); + } + puag = asCalloc(1,sizeof(UAG)+strlen(uagName)+1); + ellInit(&puag->list); + puag->name = (char *)(puag+1); + strcpy(puag->name,uagName); + if(pnext==NULL) { /*Add to end of list*/ + ellAdd(&pasbase->uagList,(ELLNODE *)puag); + } else { + pprev = (UAG *)ellPrevious((ELLNODE *)pnext); + ellInsert(&pasbase->uagList,(ELLNODE *)pprev,(ELLNODE *)puag); + } + return(puag); +} + +static long asUagAddUser(UAG *puag,const char *user) +{ + UAGNAME *puagname; + + if(!puag) return(0); + puagname = asCalloc(1,sizeof(UAGNAME)+strlen(user)+1); + puagname->user = (char *)(puagname+1); + strcpy(puagname->user,user); + ellAdd(&puag->list,(ELLNODE *)puagname); + return(0); +} + +static HAG *asHagAdd(const char *hagName) +{ + HAG *pprev; + HAG *pnext; + HAG *phag; + int cmpvalue; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + /*Insert in alphabetic order*/ + pnext = (HAG *)ellFirst(&pasbase->hagList); + while(pnext) { + cmpvalue = strcmp(hagName,pnext->name); + if(cmpvalue < 0) break; + if(cmpvalue==0) { + errlogPrintf("Duplicate Host Access Group named '%s'\n", hagName); + return(NULL); + } + pnext = (HAG *)ellNext((ELLNODE *)pnext); + } + phag = asCalloc(1,sizeof(HAG)+strlen(hagName)+1); + ellInit(&phag->list); + phag->name = (char *)(phag+1); + strcpy(phag->name,hagName); + if(pnext==NULL) { /*Add to end of list*/ + ellAdd(&pasbase->hagList,(ELLNODE *)phag); + } else { + pprev = (HAG *)ellPrevious((ELLNODE *)pnext); + ellInsert(&pasbase->hagList,(ELLNODE *)pprev,(ELLNODE *)phag); + } + return(phag); +} + +static long asHagAddHost(HAG *phag,const char *host) +{ + HAGNAME *phagname; + int len, i; + + if (!phag) return 0; + len = strlen(host); + phagname = asCalloc(1, sizeof(HAGNAME) + len + 1); + phagname->host = (char *)(phagname + 1); + for (i = 0; i < len; i++) { + phagname->host[i] = (char)tolower((int)host[i]); + } + ellAdd(&phag->list, (ELLNODE *)phagname); + return 0; +} + +static ASG *asAsgAdd(const char *asgName) +{ + ASG *pprev; + ASG *pnext; + ASG *pasg; + int cmpvalue; + ASBASE *pasbase = (ASBASE *)pasbasenew; + + /*Insert in alphabetic order*/ + pnext = (ASG *)ellFirst(&pasbase->asgList); + while(pnext) { + cmpvalue = strcmp(asgName,pnext->name); + if(cmpvalue < 0) break; + if(cmpvalue==0) { + if(strcmp(DEFAULT,pnext->name)==0) { + if(ellCount(&pnext->inpList)==0 + && ellCount(&pnext->ruleList)==0) + return(pnext); + } + errlogPrintf("Duplicate Access Security Group named '%s'\n", asgName); + return(NULL); + } + pnext = (ASG *)ellNext((ELLNODE *)pnext); + } + pasg = asCalloc(1,sizeof(ASG)+strlen(asgName)+1); + ellInit(&pasg->inpList); + ellInit(&pasg->ruleList); + ellInit(&pasg->memberList); + pasg->name = (char *)(pasg+1); + strcpy(pasg->name,asgName); + if(pnext==NULL) { /*Add to end of list*/ + ellAdd(&pasbase->asgList,(ELLNODE *)pasg); + } else { + pprev = (ASG *)ellPrevious((ELLNODE *)pnext); + ellInsert(&pasbase->asgList,(ELLNODE *)pprev,(ELLNODE *)pasg); + } + return(pasg); +} + +static long asAsgAddInp(ASG *pasg,const char *inp,int inpIndex) +{ + ASGINP *pasginp; + + if(!pasg) return(0); + pasginp = asCalloc(1,sizeof(ASGINP)+strlen(inp)+1); + pasginp->inp = (char *)(pasginp+1); + strcpy(pasginp->inp,inp); + pasginp->pasg = pasg; + pasginp->inpIndex = inpIndex; + ellAdd(&pasg->inpList,(ELLNODE *)pasginp); + return(0); +} + +static ASGRULE *asAsgAddRule(ASG *pasg,asAccessRights access,int level) +{ + ASGRULE *pasgrule; + + if(!pasg) return(0); + pasgrule = asCalloc(1,sizeof(ASGRULE)); + pasgrule->access = access; + pasgrule->trapMask = 0; + pasgrule->level = level; + ellInit(&pasgrule->uagList); + ellInit(&pasgrule->hagList); + ellAdd(&pasg->ruleList,(ELLNODE *)pasgrule); + return(pasgrule); +} + +static long asAsgAddRuleOptions(ASGRULE *pasgrule,int trapMask) +{ + if(!pasgrule) return(0); + pasgrule->trapMask = trapMask; + return(0); +} + +static long asAsgRuleUagAdd(ASGRULE *pasgrule,const char *name) +{ + ASGUAG *pasguag; + UAG *puag; + ASBASE *pasbase = (ASBASE *)pasbasenew; + long status; + + if(!pasgrule) return(0); + puag = (UAG *)ellFirst(&pasbase->uagList); + while(puag) { + if(strcmp(puag->name,name)==0) break; + puag = (UAG *)ellNext((ELLNODE *)puag); + } + if(!puag){ + status = S_asLib_noUag; + errlogPrintf("No User Access Group named '%s' defined\n", name); + return(S_asLib_noUag); + } + pasguag = asCalloc(1,sizeof(ASGUAG)); + pasguag->puag = puag; + ellAdd(&pasgrule->uagList,(ELLNODE *)pasguag); + return(0); +} + +static long asAsgRuleHagAdd(ASGRULE *pasgrule,const char *name) +{ + ASGHAG *pasghag; + HAG *phag; + ASBASE *pasbase = (ASBASE *)pasbasenew; + long status; + + if(!pasgrule) return(0); + phag = (HAG *)ellFirst(&pasbase->hagList); + while(phag) { + if(strcmp(phag->name,name)==0) break; + phag = (HAG *)ellNext((ELLNODE *)phag); + } + if(!phag){ + status = S_asLib_noHag; + errlogPrintf("No Host Access Group named '%s' defined\n", name); + return(S_asLib_noHag); + } + pasghag = asCalloc(1,sizeof(ASGHAG)); + pasghag->phag = phag; + ellAdd(&pasgrule->hagList,(ELLNODE *)pasghag); + return(0); +} + +static long asAsgRuleCalc(ASGRULE *pasgrule,const char *calc) +{ + short err; + long status; + size_t insize; + unsigned long stores; + + if (!pasgrule) return 0; + insize = strlen(calc) + 1; + pasgrule->calc = asCalloc(1, insize); + strcpy(pasgrule->calc, calc); + pasgrule->rpcl = asCalloc(1, INFIX_TO_POSTFIX_SIZE(insize)); + status = postfix(pasgrule->calc, pasgrule->rpcl, &err); + if(status) { + free((void *)pasgrule->calc); + free((void *)pasgrule->rpcl); + pasgrule->calc = NULL; + pasgrule->rpcl = NULL; + status = S_asLib_badCalc; + errlogPrintf("%s in CALC expression '%s'\n", calcErrorStr(err), calc); + return status; + } + calcArgUsage(pasgrule->rpcl, &pasgrule->inpUsed, &stores); + /* Until someone proves stores are not dangerous, don't allow them */ + if (stores) { + free((void *)pasgrule->calc); + free((void *)pasgrule->rpcl); + pasgrule->calc = NULL; + pasgrule->rpcl = NULL; + status = S_asLib_badCalc; + errlogPrintf("Assignment operator used in CALC expression '%s'\n", + calc); + } + return(status); +} diff --git a/src/as/asLib_lex.l b/src/as/asLib_lex.l new file mode 100644 index 000000000..90e1b55bb --- /dev/null +++ b/src/as/asLib_lex.l @@ -0,0 +1,82 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +digit [0-9] +name [a-zA-Z0-9_\-:\.\[\]<>;] +notquote [^\"] +escapequote \\\" +string {notquote}|{escapequote} +whitespace [ \t\r] +punctuation [(){},] +link [A-L] + +%{ +static ASINPUTFUNCPTR *my_yyinput; +#undef YY_INPUT +#define YY_INPUT(b,r,ms) (r=(*my_yyinput)((char *)b,ms)) + +static int yyreset(void) +{ + line_num=1; + BEGIN INITIAL; + return(0); +} + +%} + +%% + +UAG { return(tokenUAG); } +HAG { return(tokenHAG); } +ASG { return(tokenASG); } +RULE { return(tokenRULE); } +CALC { return(tokenCALC); } +INP{link} { + yylval.Int = (unsigned char)yytext[3]; + yylval.Int -= 'A'; + return(tokenINP); + } + +{digit}+ { /*integer*/ + yylval.Int = atoi((char *)yytext); + return(tokenINTEGER); + } + +{name}+ { /*unquoted string*/ + yylval.Str=asStrdup(yytext); + return(tokenSTRING); + } + +\"{string}*\" { /*quoted string*/ + /* making sure that neither double quote gets passed back */ + yylval.Str=asStrdup(yytext+1); + yylval.Str[strlen(yylval.Str)-1] = '\0'; + return(tokenSTRING); + } + +{punctuation} { return(yytext[0]); } + +^#.* +{whitespace} ; + +\n { line_num ++;} + +. { + char message[20]; + YY_BUFFER_STATE *dummy=0; + + sprintf(message,"invalid character '%c'",yytext[0]); + yyerror(message); + + /*The following suppress compiler warning messages*/ + if (0) yyunput('c',(unsigned char *) message); + if (0) yy_switch_to_buffer(*dummy); + } + +%% diff --git a/src/as/asTrapWrite.c b/src/as/asTrapWrite.c new file mode 100644 index 000000000..7c2536f93 --- /dev/null +++ b/src/as/asTrapWrite.c @@ -0,0 +1,167 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*asTrapWrite.c */ +/* Author: Marty Kraimer Date: 07NOV2000 */ + +/* Matthias Clausen and Vladis Korobov at DESY + * implemented the first logging of Channel Access Puts + * This implementation uses many ideas from their implementation +*/ + +#include +#include +#include +#include + +#include "ellLib.h" +#include "freeList.h" +#include "epicsStdioRedirect.h" +#include "cantProceed.h" +#include "epicsMutex.h" +#include "ellLib.h" +#define epicsExportSharedSymbols +#include "asLib.h" +#include "asTrapWrite.h" + +typedef struct listenerPvt { + ELLNODE node; + struct listener *plistener; + void *userPvt; +}listenerPvt; + +typedef struct listener{ + ELLNODE node; + asTrapWriteListener func; +}listener; + +typedef struct writeMessage { + ELLNODE node; + asTrapWriteMessage message; + ELLLIST listenerPvtList; +}writeMessage; + + +typedef struct asTrapWritePvt +{ + ELLLIST listenerList; + ELLLIST writeMessageList; + void *freeListWriteMessage; + void *freeListListenerPvt; + epicsMutexId lock; +}asTrapWritePvt; + +static asTrapWritePvt *pasTrapWritePvt = 0; + +static void asTrapWriteInit(void) +{ + pasTrapWritePvt = callocMustSucceed(1,sizeof(asTrapWritePvt),"asTrapWriteInit"); + ellInit(&pasTrapWritePvt->listenerList); + ellInit(&pasTrapWritePvt->writeMessageList); + freeListInitPvt( + &pasTrapWritePvt->freeListWriteMessage,sizeof(writeMessage),20); + freeListInitPvt( + &pasTrapWritePvt->freeListListenerPvt,sizeof(listenerPvt),20); + pasTrapWritePvt->lock = epicsMutexMustCreate(); +} + +asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( + asTrapWriteListener func) +{ + listener *plistener; + if(pasTrapWritePvt==0) asTrapWriteInit(); + plistener = callocMustSucceed(1,sizeof(listener),"asTrapWriteRegisterListener"); + plistener->func = func; + epicsMutexMustLock(pasTrapWritePvt->lock); + ellAdd(&pasTrapWritePvt->listenerList,&plistener->node); + epicsMutexUnlock(pasTrapWritePvt->lock); + return((asTrapWriteId)plistener); +} + +void epicsShareAPI asTrapWriteUnregisterListener(asTrapWriteId id) +{ + listener *plistener = (listener *)id; + writeMessage *pwriteMessage; + + if(pasTrapWritePvt==0) return; + epicsMutexMustLock(pasTrapWritePvt->lock); + pwriteMessage = (writeMessage *)ellFirst(&pasTrapWritePvt->writeMessageList); + while(pwriteMessage) { + listenerPvt *plistenerPvt + = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); + while(plistenerPvt) { + listenerPvt *pnext + = (listenerPvt *)ellNext(&plistenerPvt->node); + if(plistenerPvt->plistener == plistener) { + ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + freeListFree(pasTrapWritePvt->freeListListenerPvt,(void *)plistenerPvt); + } + plistenerPvt = pnext; + } + pwriteMessage = (writeMessage *)ellNext(&pwriteMessage->node); + } + ellDelete(&pasTrapWritePvt->listenerList,&plistener->node); + free((void *)plistener); + epicsMutexUnlock(pasTrapWritePvt->lock); +} + +void * epicsShareAPI asTrapWriteBeforeWrite( + const char *userid,const char *hostid,void *addr) +{ + writeMessage *pwriteMessage; + listener *plistener; + listenerPvt *plistenerPvt; + + if(pasTrapWritePvt==0) return(0); + if(ellCount(&pasTrapWritePvt->listenerList)<=0) return 0; + pwriteMessage = (writeMessage *)freeListCalloc( + pasTrapWritePvt->freeListWriteMessage); + pwriteMessage->message.userid = userid; + pwriteMessage->message.hostid = hostid; + pwriteMessage->message.serverSpecific = addr; + ellInit(&pwriteMessage->listenerPvtList); + epicsMutexMustLock(pasTrapWritePvt->lock); + ellAdd(&pasTrapWritePvt->writeMessageList,&pwriteMessage->node); + plistener = (listener *)ellFirst(&pasTrapWritePvt->listenerList); + while(plistener) { + plistenerPvt = (listenerPvt *)freeListCalloc( + pasTrapWritePvt->freeListListenerPvt); + plistenerPvt->plistener = plistener; + pwriteMessage->message.userPvt = 0; + (*plistener->func)(&pwriteMessage->message,0); + plistenerPvt->userPvt = pwriteMessage->message.userPvt; + ellAdd(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + plistener = (listener *)ellNext(&plistener->node); + } + epicsMutexUnlock(pasTrapWritePvt->lock); + return((void *)pwriteMessage); +} + +void epicsShareAPI asTrapWriteAfterWrite(void *pvt) +{ + writeMessage *pwriteMessage = (writeMessage *)pvt; + listenerPvt *plistenerPvt; + + if(pwriteMessage==0 || pasTrapWritePvt==0) return; + epicsMutexMustLock(pasTrapWritePvt->lock); + plistenerPvt = (listenerPvt *)ellFirst(&pwriteMessage->listenerPvtList); + while(plistenerPvt) { + listenerPvt *pnext = (listenerPvt *)ellNext(&plistenerPvt->node); + listener *plistener; + plistener = plistenerPvt->plistener; + pwriteMessage->message.userPvt = plistenerPvt->userPvt; + (*plistener->func)(&pwriteMessage->message,1); + ellDelete(&pwriteMessage->listenerPvtList,&plistenerPvt->node); + freeListFree(pasTrapWritePvt->freeListListenerPvt,(void *)plistenerPvt); + plistenerPvt = pnext; + } + ellDelete(&pasTrapWritePvt->writeMessageList,&pwriteMessage->node); + freeListFree(pasTrapWritePvt->freeListWriteMessage,(void *)pwriteMessage); + epicsMutexUnlock(pasTrapWritePvt->lock); +} diff --git a/src/as/asTrapWrite.h b/src/as/asTrapWrite.h new file mode 100644 index 000000000..ec34c5312 --- /dev/null +++ b/src/as/asTrapWrite.h @@ -0,0 +1,54 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*asTrapWrite.h*/ +/* Author: Marty Kraimer Date: 07NOV2000 */ + +#ifndef INCasTrapWriteh +#define INCasTrapWriteh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct asTrapWriteMessage { + const char *userid; + const char *hostid; + void *serverSpecific; + void *userPvt; +} asTrapWriteMessage; + + +typedef void *asTrapWriteId; +typedef void(*asTrapWriteListener)(asTrapWriteMessage *pmessage,int after); + +epicsShareFunc asTrapWriteId epicsShareAPI asTrapWriteRegisterListener( + asTrapWriteListener func); +epicsShareFunc void epicsShareAPI asTrapWriteUnregisterListener( + asTrapWriteId id); + +/* + * asTrapWriteListener is called before and after the write is performed. + * The listener can set userPvt on the before call and retrieve it after + * after = (0,1) (before,after) the put. + * + * Each asTrapWriteMessage can change or may be deleted after + * the user's asTrapWriteListener returns + * + * asTrapWriteListener delays the associated server thread so it must not + * do anything that causes to to block. +*/ + +#ifdef __cplusplus +} +#endif + +#endif /*INCasTrapWriteh*/ diff --git a/src/as/ascheck.c b/src/as/ascheck.c new file mode 100644 index 000000000..5c1813b79 --- /dev/null +++ b/src/as/ascheck.c @@ -0,0 +1,57 @@ +/*************************************************************************\ +* 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: Marty Kraimer Date: 03-24-94 */ + +#include +#include +#include +#include + +#include "errlog.h" +#include "asLib.h" +#include "dbStaticLib.h" +int main(int argc,char **argv) +{ + int argn = 1; + char *sub = NULL; + int subLength = 0; + char **pstr; + char *psep; + int *len; + long status = 0; + static char *subSep = ","; + + /* Look for -Smacro=value options */ + while (argc>argn && (strncmp(argv[argn], "-S", 2)==0)) { + pstr = ⊂ + psep = subSep; + len = &subLength; + if (strlen(argv[argn])==2) { + dbCatString(pstr, len, argv[++argn], psep); + } else { + dbCatString(pstr, len, argv[argn]+2, psep); + } + argn++; + } + if (argc == argn) { + status = asInitFP(stdin, sub); + if(status) errlogPrintf("ascheck: Access Security File failed.\n"); + } else if (argc == argn+1) { + status = asInitFile(argv[argn], sub); + if(status) errlogPrintf("ascheck: Access Security File failed.\n"); + } else { + printf("usage: ascheck [-Smac=sub ...] [<] file\n"); + status = -1; + } + errlogFlush(); + return status; +} diff --git a/src/bpt/Makefile b/src/bpt/Makefile new file mode 100644 index 000000000..f78b13d98 --- /dev/null +++ b/src/bpt/Makefile @@ -0,0 +1,30 @@ +#************************************************************************* +# 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 + +INC += cvtTable.h + +DBD += menuConvert.dbd +INC += menuConvert.h + +DBD += bptTypeJdegC.dbd +DBD += bptTypeJdegF.dbd +DBD += bptTypeKdegC.dbd +DBD += bptTypeKdegF.dbd + +PROD_LIBS = Com +PROD_HOST += makeBpt +makeBpt_SRCS=makeBpt + +include $(TOP)/configure/RULES + +$(COMMON_DBDS): $(TOOLS)/makeBpt$(HOSTEXE) + diff --git a/src/bpt/bptTypeJdegC.data b/src/bpt/bptTypeJdegC.data new file mode 100644 index 000000000..a2ac1928f --- /dev/null +++ b/src/bpt/bptTypeJdegC.data @@ -0,0 +1,142 @@ +!header +"typeJdegC" 0 0 700 4095 .5 -210 760 1 +!data + +-8.096 -8.076 -8.057 -8.037 -8.017 -7.996 -7.976 -7.955 -7.934 -7.912 + + +-7.890 -7.868 -7.846 -7.824 -7.801 -7.778 -7.755 -7.731 -7.707 -7.683 +-7.659 -7.634 -7.609 -7.584 -7.559 -7.533 -7.508 -7.482 -7.455 -7.429 +-7.402 -7.375 -7.348 -7.321 -7.293 -7.265 -7.237 -7.209 -7.180 -7.151 +-7.122 -7.093 -7.064 -7.034 -7.004 -6.974 -6.944 -6.914 -6.883 -6.852 +-6.821 -6.790 -6.758 -6.727 -6.695 -6.663 -6.630 -6.598 -6.565 -6.532 + + +-6.499 -6.466 -6.433 -6.399 -6.365 -6.331 -6.297 -6.263 -6.228 -6.194 +-6.159 -6.124 -6.089 -6.053 -6.018 -5.982 -5.946 -5.910 -5.874 -5.837 +-5.801 -5.764 -5.727 -5.690 -5.653 -5.615 -5.578 -5.540 -5.502 -5.464 +-5.426 -5.388 -5.349 -5.311 -5.272 -5.233 -5.194 -5.155 -5.115 -5.076 +-5.036 -4.996 -4.956 -4.916 -4.876 -4.836 -4.795 -4.755 -4.714 -4.673 + + +-4.632 -4.591 -4.550 -4.508 -4.467 -4.425 -4.383 -4.341 -4.299 -4.257 +-4.215 -4.172 -4.130 -4.087 -4.044 -4.001 -3.958 -3.915 -3.872 -3.829 +-3.785 -3.742 -3.698 -3.654 -3.610 -3.566 -3.522 -3.478 -3.433 -3.389 +-3.344 -3.299 -3.255 -3.210 -3.165 -3.120 -3.074 -3.029 -2.984 -2.938 +-2.892 -2.847 -2.801 -2.755 -2.709 -2.663 -2.617 -2.570 -2.524 -2.478 + + +-2.431 -2.384 -2.338 -2.291 -2.244 -2.197 -2.150 -2.102 -2.055 -2.008 +-1.960 -1.913 -1.865 -1.818 -1.770 -1.722 -1.674 -1.626 -1.578 -1.530 +-1.481 -1.433 -1.385 -1.336 -1.288 -1.239 -1.190 -1.141 -1.093 -1.044 +-0.995 -0.945 -0.896 -0.847 -0.798 -0.748 -0.699 -0.650 -0.600 -0.550 +-0.501 -0.451 -0.401 -0.351 -0.301 -0.251 -0.201 -0.151 -0.101 -0.050 + + +0.0 0.050 0.101 0.151 0.202 0.253 0.303 0.354 0.405 0.456 +0.507 0.558 0.609 0.660 0.711 0.762 0.813 0.865 0.916 0.967 +1.019 1.070 1.122 1.174 1.225 1.277 1.329 1.381 1.432 1.484 +1.536 1.588 1.640 1.693 1.745 1.797 1.849 1.901 1.954 2.006 +2.058 2.111 2.163 2.216 2.268 2.321 2.374 2.426 2.479 2.532 + + +2.585 2.638 2.691 2.743 2.796 2.849 2.902 2.956 3.009 3.062 +3.115 3.168 3.221 3.275 3.328 3.381 3.435 3.488 3.542 3.595 +3.649 3.702 3.756 3.809 3.863 3.917 3.971 4.024 4.078 4.132 +4.186 4.239 4.293 4.347 4.401 4.455 4.509 4.563 4.617 4.671 +4.725 4.780 4.834 4.888 4.942 4.996 5.050 5.105 5.159 5.213 + + +5.268 5.322 5.376 5.431 5.485 5.540 5.594 5.649 5.703 5.758 +5.812 5.867 5.921 5.976 6.031 6.085 6.140 6.195 6.249 6.304 +6.359 6.414 6.468 6.523 6.578 6.633 6.688 6.742 6.797 6.852 +6.907 6.962 7.017 7.072 7.127 7.182 7.237 7.292 7.347 7.402 +7.457 7.512 7.567 7.622 7.677 7.732 7.787 7.843 7.898 7.953 + + +8.008 8.063 8.118 8.174 8.229 8.284 8.339 8.394 8.450 8.505 +8.560 8.616 8.671 8.726 8.781 8.837 8.892 8.947 9.003 9.058 +9.113 9.169 9.224 9.279 9.335 9.390 9.446 9.501 9.556 9.612 +9.667 9.723 9.778 9.834 9.889 9.944 10.000 10.055 10.111 10.166 +10.222 10.277 10.333 10.388 10.444 10.499 10.555 10.610 10.666 10.721 + + +10.777 10.832 10.888 10.943 10.999 11.054 11.110 11.165 11.221 11.276 +11.332 11.387 11.443 11.498 11.554 11.609 11.665 11.720 11.776 11.831 +11.887 11.943 11.998 12.054 12.109 12.165 12.220 12.276 12.331 12.387 +12.442 12.498 12.553 12.609 12.664 12.720 12.776 12.831 12.887 12.942 +12.998 13.053 13.109 13.164 13.220 13.275 13.331 13.386 13.442 13.497 + + +13.553 13.608 13.664 13.719 13.775 13.830 13.886 13.941 13.997 14.052 +14.108 14.163 14.219 14.274 14.330 14.385 14.441 14.496 14.552 14.607 +14.663 14.718 14.774 14.829 14.885 14.940 14.995 15.051 15.106 15.162 +15.217 15.273 15.328 15.383 15.439 15.494 15.550 15.605 15.661 15.716 +15.771 15.827 15.882 15.938 15.993 16.048 16.104 16.159 16.214 16.270 + + +16.325 16.380 16.436 16.491 16.547 16.602 16.657 16.713 16.768 16.823 +16.879 16.934 16.989 17.044 17.100 17.155 17.210 17.266 17.321 17.376 +17.432 17.487 17.542 17.597 17.653 17.708 17.763 17.818 17.874 17.929 +17.984 18.039 18.095 18.150 18.205 18.260 18.316 18.371 18.426 18.481 +18.537 18.592 18.647 18.702 18.757 18.813 18.868 18.923 18.978 19.033 + + +19.089 19.144 19.199 19.254 19.309 19.364 19.420 19.475 19.530 19.585 +19.640 19.695 19.751 19.806 19.861 19.916 19.971 20.026 20.081 20.137 +20.192 20.247 20.302 20.357 20.412 20.467 20.523 20.578 20.633 20.688 +20.743 20.798 20.853 20.909 20.964 21.019 21.074 21.129 21.184 21.239 +21.295 21.350 21.405 21.460 21.515 21.570 21.625 21.680 21.736 21.791 + + +21.846 21.901 21.956 22.011 22.066 22.122 22.177 22.232 22.287 22.342 +22.397 22.453 22.508 22.563 22.618 22.673 22.728 22.784 22.839 22.894 +22.949 23.004 23.060 23.115 23.170 23.225 23.280 23.336 23.391 23.446 +23.501 23.556 23.612 23.667 23.722 23.777 23.833 23.888 23.943 23.999 +24.054 24.109 24.164 24.220 24.275 24.330 24.386 24.441 24.496 24.552 + + +24.607 24.662 24.718 24.773 24.829 24.884 24.939 24.995 25.050 25.106 +25.161 25.217 25.272 25.327 25.383 25.438 25.494 25.549 25.605 25.661 +25.716 25.772 25.827 25.883 25.938 25.994 26.050 26.105 26.161 26.216 +26.272 26.328 26.383 26.439 26.495 26.551 26.606 26.662 26.718 26.774 +26.829 26.885 26.941 26.997 27.053 27.109 27.165 27.220 27.276 27.332 + + +27.388 27.444 27.500 27.556 27.612 27.668 27.724 27.780 27.836 27.893 +27.949 28.005 28.061 28.117 28.173 28.230 28.286 28.342 28.398 28.455 +28.511 28.567 28.624 28.680 28.736 28.793 28.849 28.906 28.962 29.019 +29.075 29.132 29.188 29.245 29.301 29.358 29.415 29.471 29.528 29.585 +29.642 29.698 29.755 29.812 29.869 29.926 29.983 30.039 30.096 30.153 + + +30.210 30.267 30.324 30.381 30.439 30.496 30.553 30.610 30.667 30.724 +30.782 30.839 30.896 30.954 31.011 31.068 31.126 31.183 31.241 31.298 +31.356 31.413 31.471 31.528 31.586 31.644 31.702 31.759 31.817 31.875 +31.933 31.991 32.048 32.106 32.164 32.222 32.280 32.338 32.396 32.455 +32.513 32.571 32.629 32.687 32.746 32.804 32.862 32.921 32.979 33.038 + + +33.096 33.155 33.213 33.272 33.330 33.389 33.448 33.506 33.565 33.624 +33.683 33.742 33.800 33.859 33.918 33.977 34.036 34.095 34.155 34.214 +34.273 34.332 34.391 34.451 34.510 34.569 34.629 34.688 34.748 34.807 +34.867 34.926 34.986 35.046 35.105 35.165 35.225 35.285 35.344 35.404 +35.464 35.524 35.584 35.644 35.704 35.764 35.825 35.885 35.945 36.005 + + +36.066 36.126 36.186 36.247 36.307 36.368 36.428 36.489 36.549 36.610 +36.671 36.732 36.792 36.853 36.914 36.975 37.036 37.097 37.158 37.219 +37.280 37.341 37.402 37.463 37.525 37.586 37.647 37.709 37.770 37.831 +37.893 37.954 38.016 38.078 38.139 38.201 38.262 38.324 38.386 38.448 +38.510 38.572 38.633 38.695 38.757 38.819 38.882 38.944 39.006 39.068 + + +39.130 39.192 39.255 39.317 39.379 39.442 39.504 39.567 39.629 39.692 +39.754 39.817 39.880 39.942 40.005 40.068 40.131 40.193 40.256 40.319 +40.382 40.445 40.508 40.571 40.634 40.697 40.760 40.823 40.886 40.950 +41.013 41.076 41.139 41.203 41.266 41.329 41.393 41.456 41.520 41.583 +41.647 41.710 41.774 41.837 41.901 41.965 42.028 42.092 42.156 42.219 + + +42.283 42.347 42.411 42.475 42.538 42.602 42.666 42.730 42.794 42.858 +42.922 diff --git a/src/bpt/bptTypeJdegF.data b/src/bpt/bptTypeJdegF.data new file mode 100644 index 000000000..f912cb87e --- /dev/null +++ b/src/bpt/bptTypeJdegF.data @@ -0,0 +1,213 @@ +! cvtTypeJdegF.data +"typeJdegF" 32 0 1200 4095 1.0 -350 1400 1 +! +-8.137 -8.127 -8.117 -8.106 -8.096 -8.085 -8.074 -8.063 -8.052 -8.041 +-8.030 -8.019 -8.008 -7.996 -7.985 -7.973 -7.962 -7.950 -7.938 -7.927 +-7.915 -7.903 -7.890 -7.878 -7.866 -7.854 -7.841 -7.829 -7.816 -7.803 +-7.791 -7.778 -7.765 -7.752 -7.739 -7.726 -7.712 -7.699 -7.686 -7.672 +-7.659 -7.645 -7.631 -7.618 -7.604 -7.590 -7.576 -7.562 -7.548 -7.533 + +-7.519 -7.505 -7.490 -7.476 -7.461 -7.447 -7.432 -7.417 -7.402 -7.387 +-7.372 -7.357 -7.342 -7.327 -7.311 -7.296 -7.281 -7.265 -7.250 -7.234 +-7.218 -7.202 -7.187 -7.171 -7.155 -7.139 -7.122 -7.106 -7.090 -7.074 +-7.057 -7.041 -7.024 -7.008 -6.991 -6.974 -6.958 -6.941 -6.924 -6.907 +-6.890 -6.873 -6.856 -6.838 -6.821 -6.804 -6.786 -6.769 -6.751 -6.734 + +-6.716 -6.698 -6.680 -6.663 -6.645 -6.627 -6.609 -6.591 -6.572 -6.554 +-6.536 -6.518 -6.499 -6.481 -6.462 -6.444 -6.425 -6.407 -6.388 -6.369 +-6.350 -6.331 -6.312 -6.293 -6.274 -6.255 -6.236 -6.217 -6.198 -6.178 +-6.159 -6.139 -6.120 -6.100 -6.081 -6.061 -6.041 -6.022 -6.002 -5.982 +-5.962 -5.942 -5.922 -5.902 -5.882 -5.861 -5.841 -5.821 -5.801 -5.780 + +-5.760 -5.739 -5.719 -5.698 -5.678 -5.657 -5.636 -5.615 -5.594 -5.574 +-5.553 -5.532 -5.511 -5.490 -5.468 -5.447 -5.426 -5.405 -5.383 -5.362 +-5.341 -5.319 -5.298 -5.276 -5.255 -5.233 -5.211 -5.190 -5.168 -5.146 +-5.124 -5.102 -5.080 -5.058 -5.036 -5.014 -4.992 -4.970 -4.948 -4.925 +-4.903 -4.881 -4.858 -4.836 -4.813 -4.791 -4.768 -4.746 -4.723 -4.700 + +-4.678 -4.655 -4.632 -4.609 -4.586 -4.563 -4.540 -4.517 -4.494 -4.471 +-4.448 -4.425 -4.402 -4.379 -4.355 -4.332 -4.309 -4.285 -4.262 -4.238 +-4.215 -4.191 -4.168 -4.144 -4.120 -4.097 -4.073 -4.049 -4.025 -4.001 +-3.978 -3.954 -3.930 -3.906 -3.882 -3.858 -3.833 -3.809 -3.785 -3.761 +-3.737 -3.712 -3.688 -3.664 -3.639 -3.615 -3.590 -3.566 -3.541 -3.517 + +-3.492 -3.468 -3.443 -3.418 -3.394 -3.369 -3.344 -3.319 -3.294 -3.270 +-3.245 -3.220 -3.195 -3.170 -3.145 -3.120 -3.094 -3.069 -3.044 -3.019 +-2.994 -2.968 -2.943 -2.918 -2.892 -2.867 -2.842 -2.816 -2.791 -2.765 +-2.740 -2.714 -2.689 -2.663 -2.637 -2.612 -2.586 -2.560 -2.534 -2.509 +-2.483 -2.457 -2.431 -2.405 -2.379 -2.353 -2.327 -2.301 -2.275 -2.249 + +-2.223 -2.197 -2.171 -2.144 -2.118 -2.092 -2.066 -2.039 -2.013 -1.987 +-1.960 -1.934 -1.908 -1.881 -1.855 -1.828 -1.802 -1.775 -1.748 -1.722 +-1.695 -1.669 -1.642 -1.615 -1.589 -1.562 -1.535 -1.508 -1.481 -1.455 +-1.428 -1.401 -1.374 -1.347 -1.320 -1.293 -1.266 -1.239 -1.212 -1.185 +-1.158 -1.131 -1.103 -1.076 -1.049 -1.022 -0.995 -0.967 -0.940 -0.913 + +-0.885 -0.858 -0.831 -0.803 -0.776 -0.748 -0.721 -0.694 -0.666 -0.639 +-0.611 -0.583 -0.556 -0.528 -0.501 -0.473 -0.445 -0.418 -0.390 -0.362 +-0.334 -0.307 -0.279 -0.251 -0.223 -0.195 -0.168 -0.140 -0.112 -0.084 +-0.056 -0.028 0.000 0.028 0.056 0.084 0.112 0.140 0.168 0.196 +0.224 0.253 0.281 0.309 0.337 0.365 0.394 0.422 0.450 0.478 + +0.507 0.535 0.563 0.592 0.620 0.648 0.677 0.705 0.734 0.762 +0.791 0.819 0.848 0.876 0.905 0.933 0.962 0.990 1.019 1.048 +1.076 1.105 1.134 1.162 1.191 1.220 1.248 1.277 1.306 1.335 +1.363 1.392 1.421 1.450 1.479 1.507 1.536 1.565 1.594 1.623 +1.652 1.681 1.710 1.739 1.768 1.797 1.826 1.855 1.884 1.913 + +1.942 1.971 2.000 2.029 2.058 2.088 2.117 2.146 2.175 2.204 +2.233 2.263 2.292 2.321 2.350 2.380 2.409 2.438 2.467 2.497 +2.526 2.555 2.585 2.614 2.644 2.673 2.702 2.732 2.761 2.791 +2.820 2.849 2.879 2.908 2.938 2.967 2.997 3.026 3.056 3.085 +3.115 3.145 3.174 3.204 3.233 3.263 3.293 3.322 3.352 3.381 + +3.411 3.441 3.470 3.500 3.530 3.560 3.589 3.619 3.649 3.678 +3.708 3.738 3.768 3.798 3.827 3.857 3.887 3.917 3.947 3.976 +4.006 4.036 4.066 4.096 4.126 4.156 4.186 4.216 4.245 4.275 +4.305 4.335 4.365 4.395 4.425 4.455 4.485 4.515 4.545 4.575 +4.605 4.635 4.665 4.695 4.725 4.755 4.786 4.816 4.846 4.876 + +4.906 4.936 4.966 4.996 5.026 5.057 5.087 5.117 5.147 5.177 +5.207 5.238 5.268 5.298 5.328 5.358 5.389 5.419 5.449 5.479 +5.509 5.540 5.570 5.600 5.630 5.661 5.691 5.721 5.752 5.782 +5.812 5.843 5.873 5.903 5.934 5.964 5.994 6.025 6.055 6.085 +6.116 6.146 6.176 6.207 6.237 6.268 6.298 6.328 6.359 6.389 + +6.420 6.450 6.481 6.511 6.541 6.572 6.602 6.633 6.663 6.694 +6.724 6.755 6.785 6.816 6.846 6.877 6.907 6.938 6.968 6.999 +7.029 7.060 7.090 7.121 7.151 7.182 7.212 7.243 7.274 7.304 +7.335 7.365 7.396 7.426 7.457 7.488 7.518 7.549 7.579 7.610 +7.641 7.671 7.702 7.732 7.763 7.794 7.824 7.855 7.885 7.916 + +7.947 7.977 8.008 8.039 8.069 8.100 8.131 8.161 8.192 8.223 +8.253 8.284 8.315 8.345 8.376 8.407 8.437 8.468 8.499 8.530 +8.560 8.591 8.622 8.652 8.683 8.714 8.745 8.775 8.806 8.837 +8.867 8.898 8.929 8.960 8.990 9.021 9.052 9.083 9.113 9.144 +9.175 9.206 9.236 9.267 9.298 9.329 9.359 9.390 9.421 9.452 + +9.483 9.513 9.544 9.575 9.606 9.636 9.667 9.698 9.729 9.760 +9.790 9.821 9.852 9.883 9.914 9.944 9.975 10.006 10.037 10.068 +10.098 10.129 10.160 10.191 10.222 10.252 10.283 10.314 10.345 10.376 +10.407 10.437 10.468 10.499 10.530 10.561 10.592 10.622 10.653 10.684 +10.715 10.746 10.777 10.807 10.838 10.869 10.900 10.931 10.962 10.992 + +11.023 11.054 11.085 11.116 11.147 11.177 11.208 11.239 11.270 11.301 +11.332 11.363 11.393 11.424 11.455 11.486 11.517 11.548 11.578 11.609 +11.640 11.671 11.702 11.733 11.764 11.794 11.825 11.856 11.887 11.918 +11.949 11.980 12.010 12.041 12.072 12.103 12.134 12.165 12.196 12.226 +12.257 12.288 12.319 12.350 12.381 12.411 12.442 12.473 12.504 12.535 + +12.566 12.597 12.627 12.658 12.689 12.720 12.751 12.782 12.813 12.843 +12.874 12.905 12.936 12.967 12.998 13.029 13.059 13.090 13.121 13.152 +13.183 13.214 13.244 13.275 13.306 13.337 13.368 13.399 13.430 13.460 +13.491 13.522 13.553 13.584 13.615 13.645 13.676 13.707 13.738 13.769 +13.800 13.830 13.861 13.892 13.923 13.954 13.985 14.015 14.046 14.077 + +14.108 14.139 14.170 14.200 14.231 14.262 14.293 14.324 14.355 14.385 +14.416 14.447 14.478 14.509 14.539 14.570 14.601 14.632 14.663 14.694 +14.724 14.755 14.786 14.817 14.848 14.878 14.909 14.940 14.971 15.002 +15.032 15.063 15.094 15.125 15.156 15.186 15.217 15.248 15.279 15.310 +15.340 15.371 15.402 15.433 15.464 15.494 15.525 15.556 15.587 15.617 + +15.648 15.679 15.710 15.741 15.771 15.802 15.833 15.864 15.894 15.925 +15.956 15.987 16.018 16.048 16.079 16.110 16.141 16.171 16.202 16.233 +16.264 16.294 16.325 16.356 16.387 16.417 16.448 16.479 16.510 16.540 +16.571 16.602 16.633 16.663 16.694 16.725 16.756 16.786 16.817 16.848 +16.879 16.909 16.940 16.971 17.001 17.032 17.063 17.094 17.124 17.155 + +17.186 17.217 17.247 17.278 17.309 17.339 17.370 17.401 17.432 17.462 +17.493 17.524 17.554 17.585 17.616 17.646 17.677 17.708 17.739 17.769 +17.800 17.831 17.861 17.892 17.923 17.953 17.984 18.015 18.046 18.076 +18.107 18.138 18.168 18.199 18.230 18.260 18.291 18.322 18.352 18.383 +18.414 18.444 18.475 18.506 18.537 18.567 18.598 18.629 18.659 18.690 + +18.721 18.751 18.782 18.813 18.843 18.874 18.905 18.935 18.966 18.997 +19.027 19.058 19.089 19.119 19.150 19.180 19.211 19.242 19.272 19.303 +19.334 19.364 19.395 19.426 19.456 19.487 19.518 19.548 19.579 19.610 +19.640 19.671 19.702 19.732 19.763 19.793 19.824 19.855 19.885 19.916 +19.947 19.977 20.008 20.039 20.069 20.100 20.131 20.161 20.192 20.222 + +20.253 20.284 20.314 20.345 20.376 20.406 20.437 20.467 20.498 20.529 +20.559 20.590 20.621 20.651 20.682 20.713 20.743 20.774 20.804 20.835 +20.866 20.896 20.927 20.958 20.988 21.019 21.049 21.080 21.111 21.141 +21.172 21.203 21.233 21.264 21.295 21.325 21.356 21.386 21.417 21.448 +21.478 21.509 21.540 21.570 21.601 21.631 21.662 21.693 21.723 21.754 + +21.785 21.815 21.846 21.877 21.907 21.938 21.968 21.999 22.030 22.060 +22.091 22.122 22.152 22.183 22.214 22.244 22.275 22.305 22.336 22.367 +22.397 22.428 22.459 22.489 22.520 22.551 22.581 22.612 22.643 22.673 +22.704 22.735 22.765 22.796 22.826 22.857 22.888 22.918 22.949 22.980 +23.010 23.041 23.072 23.102 23.133 23.164 23.194 23.225 23.256 23.286 + +23.317 23.348 23.378 23.409 23.440 23.471 23.501 23.532 23.563 23.593 +23.624 23.655 23.685 23.716 23.747 23.777 23.808 23.839 23.870 23.900 +23.931 23.962 23.992 24.023 24.054 24.085 24.115 24.146 24.177 24.207 +24.238 24.269 24.300 24.330 24.361 24.392 24.423 24.453 24.484 24.515 +24.546 24.576 24.607 24.638 24.669 24.699 24.730 24.761 24.792 24.822 + +24.853 24.854 24.915 24.946 24.976 25.007 25.038 25.069 25.099 25.130 +25.161 25.192 25.223 25.254 25.284 25.315 25.346 25.377 25.408 25.438 +25.469 25.500 25.531 25.562 25.593 25.623 25.654 25.685 25.716 25.747 +25.778 25.809 25.840 25.870 25.901 25.932 25.963 25.994 26.025 26.056 +26.087 26.118 26.148 26.179 26.210 26.241 26.272 26.303 26.334 26.365 + +26.396 26.427 26.458 26.489 26.520 26.551 26.582 26.613 26.644 26.675 +26.705 26.736 26.767 26.798 26.829 26.860 26.891 26.922 26.954 26.985 +27.016 27.047 27.078 27.109 27.140 27.171 27.202 27.233 27.264 27.295 +27.326 27.357 27.388 27.419 27.450 27.482 27.513 27.544 27.575 27.606 +27.637 27.668 27.699 27.731 27.762 27.793 27.824 27.855 27.886 27.917 + +27.949 27.980 28.011 28.042 28.073 28.105 28.136 28.167 28.198 28.230 +28.261 28.292 28.323 28.355 28.386 28.417 28.448 28.480 28.511 28.542 +28.573 28.605 28.636 28.667 28.699 28.730 28.761 28.793 28.824 28.855 +28.887 28.918 28.950 28.981 29.012 29.044 29.075 29.107 29.138 29.169 +29.201 29.232 29.264 29.295 29.327 29.358 29.390 29.421 29.452 29.484 + +29.515 29.547 29.578 29.610 29.642 29.673 29.705 29.736 29.768 29.799 +29.831 29.862 29.894 29.926 29.957 29.989 30.020 30.052 30.084 30.115 +30.147 30.179 30.210 30.242 30.274 30.305 30.337 30.369 30.400 30.432 +30.464 30.496 30.527 30.559 30.591 30.623 30.654 30.686 30.718 30.750 +30.782 30.813 30.845 30.877 30.909 30.941 30.973 31.005 31.036 31.068 + +31.100 31.132 31.164 31.196 31.228 31.260 31.292 31.324 31.356 31.388 +31.420 31.452 31.484 31.516 31.548 31.580 31.612 31.644 31.676 31.708 +31.740 31.772 31.804 31.836 31.868 31.901 31.933 31.965 31.997 32.029 +32.061 32.094 32.126 32.158 32.190 32.222 32.255 32.287 32.319 32.351 +32.384 32.416 32.448 32.480 32.513 32.545 32.577 32.610 32.642 32.674 + +32.707 32.739 32.772 32.804 32.836 32.869 32.901 32.934 32.966 32.999 +33.031 33.064 33.096 33.129 33.161 33.194 33.226 33.259 33.291 33.324 +33.356 33.389 33.422 33.454 33.487 33.519 33.552 33.585 33.617 33.650 +33.683 33.715 33.748 33.781 33.814 33.846 33.879 33.912 33.945 33.977 +34.010 34.043 34.076 34.109 34.141 34.174 34.207 34.240 34.273 34.306 + +34.339 34.372 34.405 34.437 34.470 34.503 34.536 34.569 34.602 34.635 +34.668 34.701 34.734 34.767 34.801 34.834 34.867 34.900 34.933 34.966 +34.999 35.032 35.065 35.099 35.132 35.165 35.198 35.231 35.265 35.298 +35.331 35.364 35.398 35.431 35.464 35.498 35.531 35.564 35.598 35.631 +35.664 35.698 35.731 35.764 35.798 35.831 35.865 35.898 35.932 35.965 + +35.999 36.032 36.066 36.099 36.133 36.166 36.200 36.233 36.267 36.301 +36.334 36.368 36.401 36.435 36.469 36.502 36.536 36.570 36.603 36.637 +36.671 36.705 36.738 36.772 36.806 36.840 36.873 36.907 36.941 36.975 +37.009 37.043 37.076 37.110 37.144 37.178 37.212 37.246 37.280 37.314 +37.348 37.382 37.416 37.450 37.484 37.518 37.552 37.586 37.620 37.654 + +37.688 37.722 37.756 37.790 37.825 37.859 37.893 37.927 37.961 37.995 +38.030 38.064 38.098 38.132 38.167 38.201 38.235 38.269 38.304 38.338 +38.372 38.407 38.441 38.475 38.510 38.544 38.578 38.613 38.647 38.682 +38.716 38.751 38.785 38.819 38.854 38.888 38.923 38.957 38.992 39.027 +39.061 39.096 39.130 39.165 39.199 39.234 39.269 39.303 39.338 39.373 + +39.407 39.442 39.477 39.511 39.546 39.581 39.615 39.650 39.685 39.720 +39.754 39.789 39.824 39.859 39.894 39.928 39.963 39.998 40.033 40.068 +40.103 40.138 40.172 40.207 40.242 40.277 40.312 40.347 40.382 40.417 +40.452 40.487 40.522 40.557 40.592 40.627 40.662 40.697 40.732 40.767 +40.802 40.837 40.872 40.908 40.943 40.978 41.013 41.048 41.083 41.118 + +41.154 41.189 41.224 41.259 41.294 41.329 41.365 41.400 41.435 41.470 +41.506 41.541 41.576 41.611 41.647 41.682 41.717 41.753 41.788 41.823 +41.859 41.894 41.929 41.965 42.000 42.035 42.071 42.106 42.142 42.177 +42.212 42.248 42.283 42.319 42.354 42.390 42.425 42.460 42.496 42.531 +42.567 42.602 42.638 42.673 42.709 42.744 42.780 42.815 42.851 42.886 +42.922 diff --git a/src/bpt/bptTypeKdegC.data b/src/bpt/bptTypeKdegC.data new file mode 100644 index 000000000..4136fa9b5 --- /dev/null +++ b/src/bpt/bptTypeKdegC.data @@ -0,0 +1,201 @@ +! cvtTypeKdegC.data +"typeKdegC" 0 0 1000 4095 .5 -270 1372 1 +! +-6.458 -6.457 -6.456 -6.455 -6.453 -6.452 -6.450 -6.448 -6.446 -6.444 +-6.441 -6.438 -6.435 -6.432 -6.429 -6.425 -6.421 -6.417 -6.413 -6.408 + +-6.404 -6.399 -6.394 -6.388 -6.382 -6.377 -6.371 -6.364 -6.358 -6.351 +-6.344 -6.337 -6.329 -6.322 -6.314 -6.306 -6.297 -6.289 -6.280 -6.271 +-6.262 -6.253 -6.243 -6.233 -6.223 -6.213 -6.202 -6.192 -6.181 -6.170 +-6.158 -6.147 -6.135 -6.123 -6.111 -6.099 -6.087 -6.074 -6.061 -6.048 +-6.035 -6.021 -6.007 -5.994 -5.980 -5.965 -5.951 -5.936 -5.922 -5.907 + +-5.891 -5.876 -5.860 -5.845 -5.829 -5.813 -5.796 -5.780 -5.763 -5.747 +-5.730 -5.712 -5.695 -5.678 -5.660 -5.642 -5.624 -5.606 -5.587 -5.569 +-5.550 -5.531 -5.512 -5.493 -5.474 -5.454 -5.434 -5.414 -5.394 -5.374 +-5.354 -5.333 -5.313 -5.292 -5.271 -5.249 -5.228 -5.207 -5.185 -5.163 +-5.141 -5.119 -5.097 -5.074 -5.051 -5.029 -5.006 -4.983 -4.959 -4.936 + +-4.912 -4.889 -4.865 -4.841 -4.817 -4.792 -4.768 -4.743 -4.719 -4.694 +-4.669 -4.644 -4.618 -4.593 -4.567 -4.541 -4.515 -4.489 -4.463 -4.437 +-4.410 -4.384 -4.357 -4.330 -4.303 -4.276 -4.248 -4.221 -4.193 -4.166 +-4.138 -4.110 -4.082 -4.053 -4.025 -3.997 -3.968 -3.939 -3.910 -3.881 +-3.852 -3.823 -3.793 -3.764 -3.734 -3.704 -3.674 -3.644 -3.614 -3.584 + +-3.553 -3.523 -3.492 -3.461 -3.430 -3.399 -3.368 -3.337 -3.305 -3.274 +-3.242 -3.211 -3.179 -3.147 -3.115 -3.082 -3.050 -3.018 -2.985 -2.953 +-2.920 -2.887 -2.854 -2.821 -2.788 -2.754 -2.721 -2.687 -2.654 -2.620 +-2.586 -2.552 -2.518 -2.484 -2.450 -2.416 -2.381 -2.347 -2.312 -2.277 +-2.243 -2.208 -2.173 -2.137 -2.102 -2.067 -2.032 -1.996 -1.961 -1.925 + +-1.889 -1.853 -1.817 -1.781 -1.745 -1.709 -1.673 -1.636 -1.600 -1.563 +-1.527 -1.490 -1.453 -1.416 -1.379 -1.342 -1.305 -1.268 -1.231 -1.193 +-1.156 -1.118 -1.081 -1.043 -1.005 -0.968 -0.930 -0.892 -0.854 -0.816 +-0.777 -0.739 -0.701 -0.662 -0.624 -0.585 -0.547 -0.508 -0.469 -0.431 +-0.392 -0.353 -0.314 -0.275 -0.236 -0.197 -0.157 -0.118 -0.079 -0.039 + +0.000 0.039 0.079 0.119 0.158 0.198 0.238 0.277 0.317 0.357 +0.397 0.437 0.477 0.517 0.557 0.597 0.637 0.677 0.718 0.758 +0.798 0.838 0.879 0.919 0.960 1.000 1.041 1.081 1.122 1.162 +1.203 1.244 1.285 1.325 1.366 1.407 1.448 1.489 1.529 1.570 +1.611 1.652 1.693 1.734 1.776 1.817 1.858 1.899 1.940 1.981 + +2.022 2.064 2.105 2.146 2.188 2.229 2.270 2.312 2.353 2.394 +2.436 2.477 2.519 2.560 2.601 2.643 2.684 2.726 2.767 2.809 +2.850 2.892 2.933 2.975 3.016 3.058 3.100 3.141 3.183 3.224 +3.266 3.307 3.349 3.390 3.432 3.473 3.515 3.556 3.598 3.639 +3.681 3.722 3.764 3.805 3.847 3.888 3.930 3.971 4.012 4.054 + +4.095 4.137 4.178 4.219 4.261 4.302 4.343 4.384 4.426 4.467 +4.508 4.549 4.590 4.632 4.673 4.714 4.755 4.796 4.837 4.878 +4.919 4.960 5.001 5.042 5.083 5.124 5.164 5.205 5.246 5.287 +5.327 5.368 5.409 5.450 5.490 5.531 5.571 5.612 5.652 5.693 +5.733 5.774 5.814 5.855 5.895 5.936 5.976 6.016 6.057 6.097 + +6.137 6.177 6.218 6.258 6.298 6.338 6.378 6.419 6.459 6.499 +6.539 6.579 6.619 6.659 6.699 6.739 6.779 6.819 6.859 6.899 +6.939 6.979 7.019 7.059 7.099 7.139 7.179 7.219 7.259 7.299 +7.338 7.378 7.418 7.458 7.498 7.538 7.578 7.618 7.658 7.697 +7.737 7.777 7.817 7.857 7.897 7.937 7.977 8.017 8.057 8.097 + +8.137 8.177 8.216 8.256 8.296 8.336 8.376 8.416 8.456 8.497 +8.537 8.577 8.617 8.657 8.697 8.737 8.777 8.817 8.857 8.898 +8.938 8.978 9.018 9.058 9.099 9.139 9.179 9.220 9.260 9.300 +9.341 9.381 9.421 9.462 9.502 9.543 9.583 9.624 9.664 9.705 +9.745 9.786 9.826 9.867 9.907 9.948 9.989 10.029 10.070 10.111 + +10.151 10.192 10.233 10.274 10.315 10.355 10.396 10.437 10.478 10.519 +10.560 10.600 10.641 10.682 10.723 10.764 10.805 10.846 10.887 10.928 +10.969 11.010 11.051 11.093 11.134 11.175 11.216 11.257 11.298 11.339 +11.381 11.422 11.463 11.504 11.546 11.587 11.628 11.669 11.711 11.752 +11.793 11.835 11.876 11.918 11.959 12.000 12.042 12.083 12.125 12.166 + +12.207 12.249 12.290 12.332 12.373 12.415 12.456 12.498 12.539 12.581 +12.623 12.664 12.706 12.747 12.789 12.831 12.872 12.914 12.955 12.997 +13.039 13.080 13.122 13.164 13.205 13.247 13.289 13.331 13.372 13.414 +13.456 13.497 13.539 13.581 13.623 13.665 13.706 13.748 13.790 13.832 +13.874 13.915 13.957 13.999 14.041 14.083 14.125 14.167 14.208 14.250 + +14.292 14.334 14.376 14.418 14.460 14.502 14.544 14.586 14.628 14.670 +14.712 14.754 14.796 14.838 14.880 14.922 14.964 15.006 15.048 15.090 +15.132 15.174 15.216 15.258 15.300 15.342 15.384 15.426 15.468 15.510 +15.552 15.594 15.636 15.679 15.721 15.763 15.805 15.847 15.889 15.931 +15.974 16.016 16.058 16.100 16.142 16.184 16.227 16.269 16.311 16.353 + +16.395 16.438 16.480 16.522 16.564 16.607 16.649 16.691 16.733 16.776 +16.818 16.860 16.902 16.945 16.987 17.029 17.072 17.114 17.156 17.199 +17.241 17.283 17.326 17.368 17.410 17.453 17.495 17.537 17.580 17.622 +17.664 17.707 17.749 17.792 17.834 17.876 17.919 17.961 18.004 18.046 +18.088 18.131 18.173 18.216 18.258 18.301 18.343 18.385 18.428 18.470 + +18.513 18.555 18.598 18.640 18.683 18.725 18.768 18.810 18.853 18.895 +18.938 18.980 19.023 19.065 19.108 19.150 19.193 19.235 19.278 19.320 +19.363 19.405 19.448 19.490 19.533 19.576 19.618 19.661 19.703 19.746 +19.788 19.831 19.873 19.916 19.959 20.001 20.044 20.086 20.129 20.172 +20.214 20.257 20.299 20.342 20.385 20.427 20.470 20.512 20.555 20.598 + +20.640 20.683 20.725 20.768 20.811 20.853 20.896 20.938 20.981 21.024 +21.066 21.109 21.152 21.194 21.237 21.280 21.322 21.365 21.407 21.450 +21.493 21.535 21.578 21.621 21.663 21.706 21.749 21.791 21.834 21.876 +21.919 21.962 22.004 22.047 22.090 22.132 22.175 22.218 22.260 22.303 +22.346 22.388 22.431 22.473 22.516 22.559 22.601 22.644 22.687 22.729 + +22.772 22.815 22.857 22.900 22.942 22.985 23.028 23.070 23.113 23.156 +23.198 23.241 23.284 23.326 23.369 23.411 23.454 23.497 23.539 23.582 +23.624 23.667 23.710 23.752 23.795 23.837 23.880 23.923 23.965 24.008 +24.050 24.093 24.136 24.178 24.221 24.263 24.306 24.348 24.391 24.434 +24.476 24.519 24.561 24.604 24.646 24.689 24.731 24.774 24.817 24.859 + +24.902 24.944 24.987 25.029 25.072 25.114 25.157 25.199 25.242 25.284 +25.327 25.369 25.412 25.454 25.497 25.539 25.582 25.624 25.666 25.709 +25.751 25.794 25.836 25.879 25.921 25.964 26.006 26.048 26.091 26.133 +26.176 26.218 26.260 26.303 26.345 26.387 26.430 26.472 26.515 26.557 +26.599 26.642 26.684 26.726 26.769 26.811 26.853 26.896 26.938 26.980 + +27.022 27.065 27.107 27.149 27.192 27.234 27.276 27.318 27.361 27.403 +27.445 27.487 27.529 27.572 27.614 27.656 27.698 27.740 27.783 27.825 +27.867 27.909 27.951 27.993 28.035 28.078 28.120 28.162 28.204 28.246 +28.288 28.330 28.372 28.414 28.456 28.498 28.540 28.583 28.625 28.667 +28.709 28.751 28.793 28.835 28.877 28.919 28.961 29.002 29.044 29.086 + +29.128 29.170 29.212 29.254 29.296 29.338 29.380 29.422 29.464 29.505 +29.547 29.589 29.631 29.673 29.715 29.756 29.798 29.840 29.882 29.924 +29.965 30.007 30.049 30.091 30.132 30.174 30.216 30.257 30.299 30.341 +30.383 30.424 30.466 30.508 30.549 30.591 30.632 30.674 30.716 30.757 +30.799 30.840 30.882 30.924 30.965 31.007 31.048 31.090 31.131 31.173 + +31.214 31.256 31.297 31.339 31.380 31.422 31.463 31.504 31.546 31.587 +31.629 31.670 31.712 31.753 31.794 31.836 31.877 31.918 31.960 32.001 +32.042 32.084 32.125 32.166 32.207 32.249 32.290 32.331 32.372 32.414 +32.455 32.496 32.537 32.578 32.619 32.661 32.702 32.743 32.784 32.825 +32.866 32.907 32.948 32.990 33.031 33.072 33.113 33.154 33.195 33.236 + +33.277 33.318 33.359 33.400 33.441 33.482 33.523 33.564 33.604 33.645 +33.686 33.727 33.768 33.809 33.850 33.891 33.931 33.972 34.013 34.054 +34.095 34.136 34.176 34.217 34.258 34.299 34.339 34.380 34.421 34.461 +34.502 34.543 34.583 34.624 34.665 34.705 34.746 34.787 34.827 34.868 +34.909 34.949 34.990 35.030 35.071 35.111 35.152 35.192 35.233 35.273 + +35.314 35.354 35.395 35.435 35.476 35.516 35.557 35.597 35.637 35.678 +35.718 35.758 35.799 35.839 35.880 35.920 35.960 36.000 36.041 36.081 +36.121 36.162 36.202 36.242 36.282 36.323 36.363 36.403 36.443 36.483 +36.524 36.564 36.604 36.644 36.684 36.724 36.764 36.804 36.844 36.885 +36.925 36.965 37.005 37.045 37.085 37.125 37.165 37.205 37.245 37.285 + +37.325 37.365 37.405 37.445 37.484 37.524 37.564 37.604 37.644 37.684 +37.724 37.764 37.803 37.843 37.883 37.923 37.963 38.002 38.042 38.082 +38.122 38.162 38.201 38.241 38.281 38.320 38.360 38.400 38.439 38.479 +38.519 38.558 38.598 38.638 38.677 38.717 38.756 38.796 38.836 38.875 +38.915 38.954 38.994 39.033 39.073 39.112 39.152 39.191 39.231 39.270 + +39.310 39.349 39.388 39.428 39.467 39.507 39.546 39.585 39.625 39.644 +39.703 39.743 39.782 39.821 39.861 39.900 39.939 39.979 40.018 40.057 +40.096 40.136 40.175 40.214 40.253 40.292 40.332 40.371 40.410 40.449 +40.488 40.527 40.566 40.605 40.645 40.684 40.723 40.762 40.801 40.840 +40.879 40.918 40.957 40.996 41.035 41.074 41.113 41.152 41.191 41.230 + +41.269 41.308 41.347 41.385 41.424 41.463 41.502 41.541 41.580 41.619 +41.657 41.696 41.735 41.774 41.813 41.851 41.890 41.929 41.968 42.006 +42.045 42.084 42.123 42.161 42.200 42.239 42.277 42.316 42.355 42.393 +42.432 42.470 42.509 42.548 42.586 42.625 42.663 42.702 42.740 42.779 +42.817 42.856 42.894 42.933 42.971 43.010 43.048 43.087 43.125 43.164 + +43.202 43.240 43.279 43.317 43.356 43.394 43.432 43.471 43.509 43.547 +43.585 43.624 43.662 43.700 43.739 43.777 43.815 43.853 43.891 43.930 +43.968 44.006 44.044 44.082 44.121 44.159 44.197 44.235 44.273 44.311 +44.349 44.387 44.425 44.463 44.501 44.539 44.577 44.615 44.653 44.691 +44.729 44.767 44.805 44.843 44.881 44.919 44.957 44.995 45.033 45.070 + +45.108 45.146 45.184 45.222 45.260 45.297 45.335 45.373 45.411 45.448 +45.486 45.524 45.561 45.599 45.637 45.675 45.712 45.750 45.787 45.825 +45.863 45.900 45.938 45.975 46.013 46.051 46.088 46.126 46.163 46.201 +46.238 46.275 46.313 46.350 46.388 46.425 46.463 46.500 46.537 46.575 +46.612 46.649 46.687 46.724 46.761 46.799 46.836 46.873 46.910 46.948 + +46.985 47.022 47.059 47.096 47.134 47.171 47.208 47.245 47.282 47.319 +47.356 47.393 47.430 47.468 47.505 47.542 47.579 47.616 47.653 47.689 +47.726 47.763 47.800 47.837 47.874 47.911 47.948 47.985 48.021 48.058 +48.095 48.132 48.169 48.205 48.242 48.279 48.316 48.352 48.389 48.426 +48.462 48.499 48.536 48.572 48.609 48.645 48.682 48.718 48.755 48.792 + +48.828 48.865 48.901 48.937 48.974 49.010 49.047 49.083 49.120 49.156 +49.192 49.229 49.265 49.301 49.338 49.374 49.410 49.446 49.483 49.519 +49.555 49.591 49.627 49.663 49.700 49.736 49.772 49.808 49.844 49.880 +49.916 49.952 49.988 50.024 50.060 50.096 50.132 50.168 50.204 50.240 +50.276 50.311 50.347 50.383 50.419 50.455 50.491 50.526 50.562 50.598 + +50.633 50.669 50.705 50.741 50.776 50.812 50.847 50.883 50.919 50.954 +50.990 51.025 51.061 51.096 51.132 51.167 51.203 51.238 51.274 51.309 +51.344 51.380 51.415 51.450 51.486 51.521 51.556 51.592 51.627 51.662 +51.697 51.733 51.768 51.803 51.838 51.873 51.908 51.943 51.979 52.014 +52.049 52.084 52.119 52.154 52.189 52.224 52.259 52.294 52.329 52.364 + +52.398 52.433 52.468 52.503 52.538 52.573 52.608 52.642 52.677 52.712 +52.747 52.781 52.816 52.851 52.886 52.920 52.955 52.989 53.024 53.059 +53.093 53.128 53.162 53.197 53.232 53.266 53.301 53.335 53.370 53.404 +53.439 53.473 53.507 53.542 53.576 53.611 53.645 53.679 53.714 53.748 +53.782 53.817 53.851 53.885 53.920 53.954 53.988 54.022 54.057 54.091 + +54.125 54.159 54.193 54.228 54.262 54.296 54.330 54.364 54.398 54.432 +54.466 54.501 54.535 54.569 54.603 54.637 54.671 54.705 54.739 54.773 +54.807 54.841 54.875 diff --git a/src/bpt/bptTypeKdegF.data b/src/bpt/bptTypeKdegF.data new file mode 100644 index 000000000..20210cc5a --- /dev/null +++ b/src/bpt/bptTypeKdegF.data @@ -0,0 +1,360 @@ +! cvtTypeKdegF.data +"typeKdegF" 32 0 1832 4095 1.0 -454 2500 1 +! + -6.458 -6.457 -6.457 -6.456 + +-6.456 -6.455 -6.454 -6.454 -6.453 -6.452 -6.451 -6.450 -6.449 -6.448 +-6.447 -6.445 -6.444 -6.443 -6.441 -6.440 -6.438 -6.436 -6.435 -6.433 +-6.431 -6.429 -6.427 -6.425 -6.423 -6.421 -6.419 -6.416 -6.414 -6.411 +-6.409 -6.406 -6.404 -6.401 -6.398 -6.395 -6.392 -6.389 -6.386 -6.383 +-6.380 -6.377 -6.373 -6.370 -6.366 -6.363 -6.359 -6.355 -6.352 -6.348 + +-6.344 -6.340 -6.336 -6.332 -6.328 -6.323 -6.319 -6.315 -6.310 -6.306 +-6.301 -6.296 -6.292 -6.287 -6.282 -6.277 -6.272 -6.267 -6.262 -6.257 +-6.251 -6.246 -6.241 -6.235 -6.230 -6.224 -6.219 -6.213 -6.207 -6.201 +-6.195 -6.189 -6.183 -6.177 -6.171 -6.165 -6.158 -6.152 -6.146 -6.139 +-6.133 -6.126 -6.119 -6.113 -6.106 -6.099 -6.092 -6.085 -6.078 -6.071 + +-6.064 -6.057 -6.049 -6.042 -6.035 -6.027 -6.020 -6.012 -6.004 -5.997 +-5.989 -5.981 -5.973 -5.965 -5.957 -5.949 -5.941 -5.933 -5.925 -5.917 +-5.908 -5.900 -5.891 -5.883 -5.874 -5.866 -5.857 -5.848 -5.839 -5.831 +-5.822 -5.813 -5.804 -5.795 -5.786 -5.776 -5.767 -5.758 -5.748 -5.739 +-5.730 -5.720 -5.711 -5.701 -5.691 -5.682 -5.672 -5.662 -5.652 -5.642 + +-5.632 -5.622 -5.612 -5.602 -5.592 -5.581 -5.571 -5.561 -5.550 -5.540 +-5.529 -5.519 -5.508 -5.497 -5.487 -5.476 -5.465 -5.454 -5.443 -5.432 +-5.421 -5.410 -5.399 -5.388 -5.376 -5.365 -5.354 -5.342 -5.331 -5.319 +-5.308 -5.296 -5.285 -5.273 -5.261 -5.249 -5.238 -5.226 -5.214 -5.202 +-5.190 -5.178 -5.165 -5.153 -5.141 -5.129 -5.116 -5.104 -5.092 -5.079 + +-5.067 -5.054 -5.041 -5.029 -5.016 -5.003 -4.990 -4.978 -4.965 -4.952 +-4.939 -4.926 -4.912 -4.899 -4.886 -4.873 -4.860 -4.846 -4.833 -4.819 +-4.806 -4.792 -4.779 -4.765 -4.752 -4.738 -4.724 -4.710 -4.697 -4.683 +-4.669 -4.655 -4.641 -4.627 -4.613 -4.598 -4.584 -4.570 -4.556 -4.541 +-4.527 -4.512 -4.498 -4.484 -4.469 -4.454 -4.440 -4.425 -4.410 -4.396 + +-4.381 -4.366 -4.351 -4.336 -4.321 -4.306 -4.291 -4.276 -4.261 -4.245 +-4.230 -4.215 -4.200 -4.184 -4.169 -4.153 -4.138 -4.122 -4.107 -4.091 +-4.075 -4.060 -4.044 -4.028 -4.012 -3.997 -3.981 -3.965 -3.949 -3.933 +-3.917 -3.901 -3.884 -3.868 -3.852 -3.836 -3.819 -3.803 -3.787 -3.770 +-3.754 -3.737 -3.721 -3.704 -3.688 -3.671 -3.654 -3.637 -3.621 -3.604 + +-3.587 -3.570 -3.553 -3.536 -3.519 -3.502 -3.485 -3.468 -3.451 -3.434 +-3.417 -3.399 -3.382 -3.365 -3.347 -3.330 -3.312 -3.295 -3.277 -3.260 +-3.242 -3.225 -3.207 -3.189 -3.172 -3.154 -3.136 -3.118 -3.100 -3.082 +-3.065 -3.047 -3.029 -3.010 -2.992 -2.974 -2.956 -2.938 -2.920 -2.902 +-2.883 -2.865 -2.847 -2.828 -2.810 -2.791 -2.773 -2.754 -2.736 -2.717 + +-2.699 -2.680 -2.661 -2.643 -2.624 -2.605 -2.586 -2.567 -2.549 -2.530 +-2.511 -2.492 -2.473 -2.454 -2.435 -2.416 -2.397 -2.377 -2.358 -2.339 +-2.320 -2.300 -2.281 -2.262 -2.243 -2.223 -2.204 -2.184 -2.165 -2.145 +-2.126 -2.106 -2.087 -2.067 -2.047 -2.028 -2.008 -1.988 -1.968 -1.949 +-1.929 -1.909 -1.889 -1.869 -1.849 -1.829 -1.809 -1.789 -1.769 -1.749 + +-1.729 -1.709 -1.689 -1.669 -1.648 -1.628 -1.608 -1.588 -1.567 -1.547 +-1.527 -1.506 -1.486 -1.465 -1.445 -1.424 -1.404 -1.383 -1.363 -1.342 +-1.322 -1.301 -1.280 -1.260 -1.239 -1.218 -1.197 -1.177 -1.156 -1.135 +-1.114 -1.093 -1.072 -1.051 -1.031 -1.010 -0.989 -0.968 -0.946 -0.925 +-0.904 -0.883 -0.862 -0.841 -0.820 -0.799 -0.777 -0.756 -0.735 -0.714 + +-0.692 -0.671 -0.650 -0.628 -0.607 -0.585 -0.564 -0.543 -0.521 -0.500 +-0.478 -0.457 -0.435 -0.413 -0.392 -0.370 -0.349 -0.327 -0.305 -0.284 +-0.262 -0.240 -0.218 -0.197 -0.175 -0.153 -0.131 -0.109 -0.088 -0.066 +-0.044 -0.022 0.000 0.022 0.044 0.066 0.088 0.110 0.132 0.154 +0.176 0.198 0.220 0.242 0.264 0.286 0.308 0.331 0.353 0.375 + +0.397 0.419 0.441 0.464 0.486 0.508 0.530 0.553 0.575 0.597 +0.619 0.642 0.664 0.686 0.709 0.731 0.753 0.776 0.798 0.821 +0.843 0.865 0.888 0.910 0.933 0.955 0.978 1.000 1.023 1.045 +1.068 1.090 1.113 1.135 1.158 1.181 1.203 1.226 1.248 1.271 +1.294 1.316 1.339 1.362 1.384 1.407 1.430 1.452 1.475 1.498 + +1.520 1.543 1.566 1.589 1.611 1.634 1.657 1.680 1.703 1.725 +1.748 1.771 1.794 1.817 1.839 1.862 1.885 1.908 1.931 1.954 +1.977 2.000 2.022 2.045 2.068 2.091 2.114 2.137 2.160 2.183 +2.206 2.229 2.252 2.275 2.298 2.321 2.344 2.367 2.390 2.413 +2.436 2.459 2.482 2.505 2.528 2.551 2.574 2.597 2.620 2.643 + +2.666 2.689 2.712 2.735 2.758 2.781 2.804 2.827 2.850 2.873 +2.896 2.920 2.943 2.966 2.989 3.012 3.035 3.058 3.081 3.104 +3.127 3.150 3.173 3.196 3.220 3.243 3.266 3.289 3.312 3.335 +3.358 3.381 3.404 3.427 3.450 3.473 3.496 3.519 3.543 3.566 +3.589 3.612 3.635 3.658 3.681 3.704 3.727 3.750 3.773 3.796 + +3.819 3.842 3.865 3.888 3.911 3.934 3.957 3.980 4.003 4.026 +4.049 4.072 4.095 4.118 4.141 4.164 4.187 4.210 4.233 4.256 +4.279 4.302 4.325 4.348 4.371 4.394 4.417 4.439 4.462 4.485 +4.508 4.531 4.554 4.577 4.600 4.622 4.645 4.668 4.691 4.714 +4.737 4.759 4.782 4.805 4.828 4.851 4.873 4.896 4.919 4.942 + +4.964 4.987 5.010 5.033 5.055 5.078 5.101 5.124 5.146 5.169 +5.192 5.214 5.237 5.260 5.282 5.305 5.327 5.350 5.373 5.395 +5.418 5.440 5.463 5.486 5.508 5.531 5.553 5.576 5.598 5.621 +5.643 5.666 5.688 5.711 5.733 5.756 5.778 5.801 5.823 5.846 +5.868 5.891 5.913 5.936 5.958 5.980 6.003 6.025 6.048 6.070 + +6.092 6.115 6.137 6.160 6.182 6.204 6.227 6.249 6.271 6.294 +6.316 6.338 6.361 6.383 6.405 6.428 6.450 6.472 6.494 6.517 +6.539 6.561 6.583 6.606 6.628 6.650 6.672 6.695 6.717 6.739 +6.761 6.784 6.806 6.828 6.850 6.873 6.895 6.917 6.939 6.961 +6.984 7.006 7.028 7.050 7.072 7.094 7.117 7.139 7.161 7.183 + +7.205 7.228 7.250 7.272 7.294 7.316 7.338 7.361 7.383 7.405 +7.427 7.449 7.471 7.494 7.516 7.538 7.560 7.582 7.604 7.627 +7.649 7.671 7.693 7.715 7.737 7.760 7.782 7.804 7.826 7.848 +7.870 7.893 7.915 7.937 7.959 7.981 8.003 8.026 8.048 8.070 +8.092 8.114 8.137 8.159 8.181 8.203 8.225 8.248 8.270 8.292 + +8.314 8.336 8.359 8.381 8.403 8.425 8.448 8.470 8.492 8.514 +8.537 8.559 8.581 8.603 8.626 8.648 8.670 8.692 8.715 8.737 +8.759 8.782 8.804 8.826 8.849 8.871 8.893 8.916 8.938 8.960 +8.983 9.005 9.027 9.050 9.072 9.094 9.117 9.139 9.161 9.184 +9.206 9.229 9.251 9.273 9.296 9.318 9.341 9.363 9.385 9.408 + +9.430 9.453 9.475 9.498 9.520 9.543 9.565 9.588 9.610 9.633 +9.655 9.678 9.700 9.723 9.745 9.768 9.790 9.813 9.835 9.858 +9.880 9.903 9.926 9.948 9.971 9.993 10.016 10.038 10.061 10.084 +10.106 10.129 10.151 10.174 10.197 10.219 10.242 10.265 10.287 10.310 +10.333 10.355 10.378 10.401 10.423 10.446 10.469 10.491 10.514 10.537 + +10.560 10.582 10.605 10.628 10.650 10.673 10.696 10.719 10.741 10.764 +10.787 10.810 10.833 10.855 10.878 10.901 10.924 10.947 10.969 10.992 +11.015 11.038 11.061 11.083 11.106 11.129 11.152 11.175 11.198 11.221 +11.243 11.266 11.289 11.312 11.335 11.358 11.381 11.404 11.426 11.449 +11.472 11.495 11.518 11.541 11.564 11.587 11.610 11.633 11.656 11.679 + +11.702 11.725 11.748 11.770 11.793 11.816 11.839 11.862 11.885 11.908 +11.931 11.954 11.977 12.000 12.023 12.046 12.069 12.092 12.115 12.138 +12.161 12.184 12.207 12.230 12.254 12.277 12.300 12.323 12.346 12.369 +12.392 12.415 12.438 12.461 12.484 12.507 12.530 12.553 12.576 12.599 +12.623 12.646 12.669 12.692 12.715 12.738 12.761 12.784 12.807 12.831 + +12.854 12.877 12.900 12.923 12.946 12.969 12.992 13.016 13.039 13.062 +13.085 13.108 13.131 13.154 13.178 13.201 13.224 13.247 13.270 13.293 +13.317 13.340 13.363 13.386 13.409 13.433 13.456 13.479 13.502 13.525 +13.549 13.572 13.595 13.618 13.641 13.665 13.688 13.711 13.734 13.757 +13.781 13.804 13.827 13.850 13.874 13.897 13.920 13.943 13.967 13.990 + +14.013 14.036 14.060 14.083 14.106 14.129 14.153 14.176 14.199 14.222 +14.246 14.269 14.292 14.316 14.339 14.362 14.385 14.409 14.432 14.455 +14.479 14.502 14.525 14.548 14.572 14.595 14.618 14.642 14.665 14.688 +14.712 14.735 14.758 14.782 14.805 14.828 14.852 14.875 14.898 14.922 +14.945 14.968 14.992 15.015 15.038 15.062 15.085 15.108 15.132 15.155 + +15.178 15.202 15.225 15.248 15.272 15.295 15.318 15.342 15.365 15.389 +15.412 15.435 15.459 15.482 15.505 15.529 15.552 15.576 15.599 15.622 +15.646 15.669 15.693 15.716 15.739 15.763 15.786 15.810 15.833 15.856 +15.880 15.903 15.927 15.950 15.974 15.997 16.020 16.044 16.067 16.091 +16.114 16.138 16.161 16.184 16.208 16.231 16.255 16.278 16.302 16.325 + +16.349 16.372 16.395 16.419 16.442 16.466 16.489 16.513 16.536 16.560 +16.583 16.607 16.630 16.654 16.677 16.700 16.724 16.747 16.771 16.794 +16.818 16.841 16.865 16.888 16.912 16.935 16.959 16.982 17.006 17.029 +17.053 17.076 17.100 17.123 17.147 17.170 17.194 17.217 17.241 17.264 +17.288 17.311 17.335 17.358 17.382 17.406 17.429 17.453 17.476 17.500 + +17.523 17.547 17.570 17.594 17.617 17.641 17.664 17.688 17.711 17.735 +17.759 17.782 17.806 17.829 17.853 17.876 17.900 17.923 17.947 17.971 +17.994 18.018 18.041 18.065 18.088 18.112 18.136 18.159 18.183 18.206 +18.230 18.253 18.277 18.301 18.324 18.348 18.371 18.395 18.418 18.442 +18.466 18.489 18.513 18.536 18.560 18.584 18.607 18.631 18.654 18.678 + +18.702 18.725 18.749 18.772 18.796 18.820 18.843 18.867 18.890 18.914 +18.938 18.961 18.985 19.008 19.032 19.056 19.079 19.103 19.127 19.150 +19.174 19.197 19.221 19.245 19.268 19.292 19.316 19.339 19.363 19.386 +19.410 19.434 19.457 19.481 19.505 19.528 19.552 19.576 19.599 19.623 +19.646 19.670 19.694 19.717 19.741 19.765 19.788 19.812 19.836 19.859 + +19.883 19.907 19.930 19.954 19.978 20.001 20.025 20.049 20.072 20.096 +20.120 20.143 20.167 20.190 20.214 20.238 20.261 20.285 20.309 20.332 +20.356 20.380 20.403 20.427 20.451 20.474 20.498 20.522 20.545 20.569 +20.593 20.616 20.640 20.664 20.688 20.711 20.735 20.759 20.782 20.806 +20.830 20.853 20.877 20.901 20.924 20.948 20.972 20.995 21.019 21.043 + +21.066 21.090 21.114 21.137 21.161 21.185 21.208 21.232 21.256 21.280 +21.303 21.327 21.351 21.374 21.398 21.422 21.445 21.469 21.493 21.516 +21.540 21.564 21.587 21.611 21.635 21.659 21.682 21.706 21.730 21.753 +21.777 21.801 21.824 21.848 21.872 21.895 21.919 21.943 21.966 21.990 +22.014 22.038 22.061 22.085 22.109 22.132 22.156 22.180 22.203 22.227 + +22.251 22.274 22.298 22.322 22.346 22.369 22.393 22.417 22.440 22.464 +22.488 22.511 22.535 22.559 22.582 22.606 22.630 22.654 22.677 22.701 +22.725 22.748 22.772 22.796 22.819 22.843 22.867 22.890 22.914 22.938 +22.961 22.985 23.009 23.032 23.056 23.080 23.104 23.127 23.151 23.175 +23.198 23.222 23.246 23.269 23.293 23.317 23.340 23.364 23.388 23.411 + +23.435 23.459 23.482 23.506 23.530 23.553 23.577 23.601 23.624 23.648 +23.672 23.695 23.719 23.743 23.766 23.790 23.814 23.837 23.861 23.885 +23.908 23.932 23.956 23.979 24.003 24.027 24.050 24.074 24.098 24.121 +24.145 24.169 24.192 24.216 24.240 24.263 24.287 24.311 24.334 24.358 +24.382 24.405 24.429 24.453 24.476 24.500 24.523 24.547 24.571 24.594 + +24.618 24.642 24.665 24.689 24.713 24.736 24.760 24.783 24.807 24.831 +24.854 24.878 24.902 24.925 24.949 24.972 24.996 25.020 25.043 25.067 +25.091 25.114 25.138 25.161 25.185 25.209 25.232 25.256 25.279 25.303 +25.327 25.350 25.374 25.397 25.421 25.445 25.468 25.492 25.515 25.539 +25.563 25.586 25.610 25.633 25.657 25.681 25.704 25.728 25.751 25.775 + +25.799 25.822 25.846 25.869 25.893 25.916 25.940 25.964 25.987 26.011 +26.034 26.058 26.081 26.105 26.128 26.152 26.176 26.199 26.223 26.246 +26.270 26.293 26.317 26.340 26.364 26.387 26.411 26.435 26.458 26.482 +26.505 26.529 26.552 26.576 26.599 26.623 26.646 26.670 26.693 26.717 +26.740 26.764 26.787 26.811 26.834 26.858 26.881 26.905 26.928 26.952 + +26.975 26.999 27.022 27.046 27.069 27.093 27.116 27.140 27.163 27.187 +27.210 27.234 27.257 27.281 27.304 27.328 27.351 27.375 27.398 27.422 +27.445 27.468 27.492 27.515 27.539 27.562 27.586 27.609 27.633 27.656 +27.679 27.703 27.726 27.750 27.773 27.797 27.820 27.843 27.867 27.890 +27.914 27.937 27.961 27.984 28.007 28.031 28.054 28.078 28.101 28.124 + +28.148 28.171 28.195 28.218 28.241 28.265 28.288 28.311 28.335 28.358 +28.382 28.405 28.428 28.452 28.475 28.498 28.522 28.545 28.569 28.592 +28.615 28.639 28.662 28.685 28.709 28.732 28.755 28.779 28.802 28.825 +28.849 28.872 28.895 28.919 28.942 28.965 28.988 29.012 29.035 29.058 +29.082 29.105 29.128 29.152 29.175 29.198 29.221 29.245 29.268 29.291 + +29.315 29.338 29.361 29.384 29.408 29.431 29.454 29.477 29.501 29.524 +29.547 29.570 29.594 29.617 29.640 29.663 29.687 29.710 29.733 29.756 +29.780 29.803 29.826 29.849 29.872 29.896 29.919 29.942 29.965 29.989 +30.012 30.035 30.058 30.081 30.104 30.128 30.151 30.174 30.197 30.220 +30.244 30.267 30.290 30.313 30.336 30.359 30.383 30.406 30.429 30.452 + +30.475 30.498 30.521 30.545 30.568 30.591 30.614 30.637 30.660 30.683 +30.706 30.730 30.753 30.776 30.799 30.822 30.845 30.868 30.891 30.914 +30.937 30.961 30.984 31.007 31.030 31.053 31.076 31.099 31.122 31.145 +31.168 31.191 31.214 31.237 31.260 31.283 31.306 31.329 31.353 31.376 +31.399 31.422 31.445 31.468 31.491 31.514 31.537 31.560 31.583 31.606 + +31.629 31.652 31.675 31.698 31.721 31.744 31.767 31.790 31.813 31.836 +31.859 31.882 31.905 31.927 31.950 31.973 31.996 32.019 32.042 32.065 +32.088 32.111 32.134 32.157 32.180 32.203 32.226 32.249 32.272 32.294 +32.317 32.340 32.363 32.386 32.409 32.432 32.455 32.478 32.501 32.523 +32.546 32.569 32.592 32.615 32.638 32.661 32.683 32.706 32.729 32.752 + +32.775 32.798 32.821 32.843 32.866 32.889 32.912 32.935 32.958 32.980 +33.003 33.026 33.049 33.072 33.094 33.117 33.140 33.163 33.186 33.208 +33.231 33.254 33.277 33.300 33.322 33.345 33.368 33.391 33.413 33.436 +33.459 33.482 33.504 33.527 33.550 33.573 33.595 33.618 33.641 33.664 +33.686 33.709 33.732 33.754 33.777 33.800 33.823 33.845 33.868 33.891 + +33.913 33.936 33.959 33.981 34.004 34.027 34.049 34.072 34.095 34.117 +34.140 34.163 34.185 34.208 34.231 34.253 34.276 34.299 34.321 34.344 +34.366 34.389 34.412 34.434 34.457 34.480 34.502 34.525 34.547 34.570 +34.593 34.615 34.638 34.660 34.683 34.705 34.728 34.751 34.773 34.796 +34.818 34.841 34.863 34.886 34.909 34.931 34.954 34.976 34.999 35.021 + +35.044 35.066 35.089 35.111 35.134 35.156 35.179 35.201 35.224 35.246 +35.269 35.291 35.314 35.336 35.359 35.381 35.404 35.426 35.449 35.471 +35.494 35.516 35.539 35.561 35.583 35.606 35.628 35.651 35.673 35.696 +35.718 35.741 35.763 35.785 35.808 35.830 35.853 35.875 35.897 35.920 +35.942 35.965 35.987 36.009 36.032 36.054 36.077 36.099 36.121 36.144 + +36.166 36.188 36.211 36.233 36.256 36.278 36.300 36.323 36.345 36.367 +36.390 36.412 36.434 36.457 36.479 36.501 36.524 36.546 36.568 36.590 +36.613 36.635 36.657 36.680 36.702 36.724 36.746 36.769 36.791 36.813 +36.836 36.858 36.880 36.902 36.925 36.947 36.969 36.991 37.014 37.036 +37.058 37.080 37.103 37.125 37.147 37.169 37.191 37.214 37.236 37.258 + +37.280 37.303 37.325 37.347 37.369 37.391 37.413 37.436 37.458 37.480 +37.502 37.524 37.547 37.569 37.591 37.613 37.635 37.657 37.679 37.702 +37.724 37.746 37.768 37.790 37.812 37.834 37.857 37.879 37.901 37.923 +37.945 37.967 37.989 38.011 38.033 38.055 38.078 38.100 38.122 38.144 +38.166 38.188 38.210 38.232 38.254 38.276 38.298 38.320 38.342 38.364 + +38.387 38.409 38.431 38.453 38.475 38.497 38.519 38.541 38.563 38.585 +38.607 38.629 38.651 38.673 38.695 38.717 38.739 38.761 38.783 38.805 +38.827 38.849 38.871 38.893 38.915 38.937 38.959 38.981 39.003 39.024 +39.046 39.068 39.090 39.112 39.134 39.156 39.178 39.200 39.222 39.244 +39.266 39.288 39.310 39.331 39.353 39.375 39.397 39.419 39.441 39.463 + +39.485 39.507 39.529 39.550 39.572 39.594 39.616 39.638 39.660 39.682 +39.703 39.725 39.747 39.769 39.791 39.813 39.835 39.856 39.878 39.900 +39.922 39.944 39.965 39.987 40.009 40.031 40.053 40.075 40.096 40.118 +40.140 40.162 40.183 40.205 40.227 40.249 40.271 40.292 40.314 40.336 +40.358 40.379 40.401 40.423 40.445 40.466 40.488 40.510 40.532 40.553 + +40.575 40.597 40.619 40.640 40.662 40.684 40.705 40.727 40.749 40.770 +40.792 40.814 40.836 40.857 40.879 40.901 40.922 40.944 40.966 40.987 +41.009 41.031 41.052 41.074 41.096 41.117 41.139 41.161 41.182 41.204 +41.225 41.247 41.269 41.290 41.312 41.334 41.355 41.377 41.398 41.420 +41.442 41.463 41.485 41.506 41.528 41.550 41.571 41.593 41.614 41.636 + +41.657 41.679 41.701 41.722 41.744 41.765 41.787 41.808 41.830 41.851 +41.873 41.895 41.916 41.938 41.959 41.981 42.002 42.024 42.045 42.067 +42.088 42.110 42.131 42.153 42.174 42.196 42.217 42.239 42.260 42.282 +42.303 42.325 42.346 42.367 42.389 42.410 42.432 42.453 42.475 42.496 +42.518 42.539 42.560 42.582 42.603 42.625 42.646 42.668 42.689 42.710 + +42.732 42.753 42.775 42.796 42.817 42.839 42.860 42.882 42.903 42.924 +42.946 42.967 42.989 43.010 43.031 43.053 43.074 43.095 43.117 43.138 +43.159 43.181 43.202 43.223 43.245 43.266 43.287 43.309 43.330 43.351 +43.373 43.394 43.415 43.436 43.458 43.479 43.500 43.522 43.543 43.564 +43.585 43.607 43.628 43.649 43.671 43.692 43.713 43.734 43.756 43.777 + +43.798 43.819 43.841 43.862 43.883 43.904 43.925 43.947 43.968 43.989 +44.010 44.031 44.053 44.074 44.095 44.116 44.137 44.159 44.180 44.201 +44.222 44.243 44.265 44.286 44.307 44.328 44.349 44.370 44.391 44.413 +44.434 44.455 44.476 44.497 44.518 44.539 44.560 44.582 44.603 44.624 +44.645 44.666 44.687 44.708 44.729 44.750 44.771 44.793 44.814 44.835 + +44.856 44.877 44.898 44.919 44.940 44.961 44.982 45.003 45.024 45.045 +45.066 45.087 45.108 45.129 45.150 45.171 45.192 45.213 45.234 45.255 +45.276 45.297 45.318 45.339 45.360 45.381 45.402 45.423 45.444 45.465 +45.486 45.507 45.528 45.549 45.570 45.591 45.612 45.633 45.654 45.675 +45.695 45.716 45.737 45.758 45.779 45.800 45.821 45.842 45.863 45.884 + +45.904 45.925 45.946 45.967 45.988 46.009 46.030 46.051 46.071 46.092 +46.113 46.134 46.155 46.176 46.196 46.217 46.238 46.259 46.280 46.300 +46.321 46.342 46.363 46.384 46.404 46.425 46.446 46.467 46.488 46.508 +46.529 46.550 46.571 46.591 46.612 46.633 46.654 46.674 46.695 46.716 +46.737 46.757 46.778 46.799 46.819 46.840 46.861 46.881 46.902 46.923 + +46.944 46.964 46.985 47.006 47.026 47.047 47.068 47.088 47.109 47.130 +47.150 47.171 47.191 47.212 47.233 47.253 47.274 47.295 47.315 47.336 +47.356 47.377 47.398 47.418 47.439 47.459 47.480 47.500 47.521 47.542 +47.562 47.583 47.603 47.624 47.644 47.665 47.685 47.706 47.726 47.747 +47.767 47.788 47.808 47.829 47.849 47.870 47.890 47.911 47.931 47.952 + +47.972 47.993 48.013 48.034 48.054 48.075 48.095 48.116 48.136 48.156 +48.177 48.197 48.218 48.238 48.258 48.279 48.299 48.320 48.340 48.360 +48.381 48.401 48.422 48.442 48.462 48.483 48.503 48.523 48.544 48.564 +48.584 48.605 48.625 48.645 48.666 48.686 48.706 48.727 48.747 48.767 +48.787 48.808 48.828 48.848 48.869 48.889 48.909 48.929 48.950 48.970 + +48.990 49.010 49.031 49.051 49.071 49.091 49.111 49.132 49.152 49.172 +49.192 49.212 49.233 49.253 49.273 49.293 49.313 49.333 49.354 49.374 +49.394 49.414 49.434 49.454 49.474 49.495 49.515 49.535 49.555 49.575 +49.595 49.615 49.635 49.655 49.675 49.696 49.716 49.736 49.756 49.776 +49.796 49.816 49.836 49.856 49.876 49.896 49.916 49.936 49.956 49.976 + +49.996 50.016 50.036 50.056 50.076 50.096 50.116 50.136 50.156 50.176 +50.196 50.216 50.236 50.256 50.276 50.296 50.315 50.335 50.355 50.375 +50.395 50.415 50.435 50.455 50.475 50.494 50.514 50.534 50.554 50.574 +50.594 50.614 50.633 50.653 50.673 50.693 50.713 50.733 50.752 50.772 +50.792 50.812 50.832 50.851 50.871 50.891 50.911 50.930 50.950 50.970 + +50.990 51.009 51.029 51.049 51.069 51.088 51.108 51.128 51.148 51.167 +51.187 51.207 51.226 51.246 51.266 51.285 51.305 51.325 51.344 51.364 +51.384 51.403 51.423 51.443 51.462 51.482 51.501 51.521 51.541 51.560 +51.580 51.599 51.619 51.639 51.658 51.678 51.697 51.717 51.736 51.756 +51.776 51.795 51.815 51.834 51.854 51.873 51.893 51.912 51.932 51.951 + +51.971 51.990 52.010 52.029 52.049 52.068 52.088 52.107 52.127 52.146 +52.165 52.185 52.204 52.224 52.243 52.263 52.282 52.301 52.321 52.340 +52.360 52.379 52.398 52.418 52.437 52.457 52.476 52.495 52.515 52.534 +52.553 52.573 52.592 52.611 52.631 52.650 52.669 52.689 52.708 52.727 +52.747 52.766 52.785 52.805 52.824 52.843 52.862 52.882 52.901 52.920 + +52.939 52.959 52.978 52.997 53.016 53.036 53.055 53.074 53.093 53.113 +53.132 53.151 53.170 53.189 53.209 53.228 53.247 53.266 53.285 53.304 +53.324 53.343 53.362 53.381 53.400 53.419 53.439 53.458 53.477 53.496 +53.515 53.534 53.553 53.572 53.592 53.611 53.630 53.649 53.668 53.687 +53.706 53.725 53.744 53.763 53.782 53.801 53.821 53.840 53.859 53.878 + +53.897 53.916 53.935 53.954 53.973 53.992 54.011 54.030 54.049 54.068 +54.087 54.106 54.125 54.144 54.163 54.182 54.201 54.220 54.239 54.258 +54.277 54.296 54.315 54.334 54.353 54.372 54.391 54.410 54.429 54.447 +54.466 54.485 54.504 54.523 54.542 54.561 54.580 54.599 54.618 54.637 +54.656 54.675 54.694 54.712 54.731 54.750 54.769 54.788 54.807 54.826 + +54.845 diff --git a/src/bpt/cvtTable.h b/src/bpt/cvtTable.h new file mode 100644 index 000000000..cf362c4fb --- /dev/null +++ b/src/bpt/cvtTable.h @@ -0,0 +1,37 @@ +/*************************************************************************\ +* 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 + * Breakpoint Tables + * + * Author: Marty Kraimer + * Date: 11-7-90 + */ + +#ifndef INCcvtTableh +#define INCcvtTableh 1 + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global Routines*/ +epicsShareFunc long epicsShareAPI cvtEngToRawBpt( + double *pval,short linr,short init,void **ppbrk,short *plbrk); + +epicsShareFunc long epicsShareAPI cvtRawToEngBpt( + double *pval,short linr,short init,void **ppbrk, short *plbrk); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/bpt/makeBpt.c b/src/bpt/makeBpt.c new file mode 100644 index 000000000..140055ac1 --- /dev/null +++ b/src/bpt/makeBpt.c @@ -0,0 +1,426 @@ +/*************************************************************************\ +* 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: Marty Kraimer + * Date: 9/28/95 + * Replacement for old bldCvtTable + */ + +#include +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "ellLib.h" +#include "cvtTable.h" + +#define MAX_LINE_SIZE 160 +#define MAX_BREAKS 100 +struct brkCreateInfo { + double engLow; /* Lowest value desired: engineering units */ + double engHigh; /* Highest value desired: engineering units */ + double rawLow; /* Raw value for EngLow */ + double rawHigh; /* Raw value for EngHigh */ + double accuracy; /* accuracy desired in engineering units */ + double tblEngFirst;/* First table value: engineering units */ + double tblEngLast; /* Last table value: engineering units */ + double tblEngDelta;/* Change per table entry: eng units */ + long nTable; /* number of table entries */ + /* (last-first)/delta + 1 */ + double *pTable; /* addr of data table */ +} brkCreateInfo; + +typedef struct brkInt { /* breakpoint interval */ + double raw; /* raw value for beginning of interval */ + double slope; /* slope for interval */ + double eng; /* converted value for beginning of interval */ +} brkInt; + +brkInt brkint[MAX_BREAKS]; + +static int create_break(struct brkCreateInfo *pbci, brkInt *pabrkInt, + int max_breaks, int *n_breaks); +static char inbuf[MAX_LINE_SIZE]; +static int linenum=0; + +typedef struct dataList{ + struct dataList *next; + double value; +}dataList; + +static int getNumber(char **pbeg, double *value) +{ + int nchars=0; + + while(isspace((int)**pbeg) && **pbeg!= '\0') (*pbeg)++; + if(**pbeg == '!' || **pbeg == '\0') return(-1); + if(sscanf(*pbeg,"%lf%n",value,&nchars)!=1) return(-1); + *pbeg += nchars; + return(0); +} + +static void errExit(char *pmessage) +{ + fprintf(stderr, "%s\n", pmessage); + fflush(stderr); + exit(-1); +} + +int main(int argc, char **argv) +{ + char *pbeg; + char *pend; + double value; + char *pname = NULL; + dataList *phead; + dataList *pdataList; + dataList *pnext; + double *pdata; + long ndata; + int nBreak,len,n; + char *outFilename; + char *pext; + FILE *outFile; + FILE *inFile; + char *plastSlash; + + + if(argc<2) { + fprintf(stderr,"usage: makeBpt file.data [outfile]\n"); + exit(-1); + } + if (argc==2) { + plastSlash = strrchr(argv[1],'/'); + plastSlash = (plastSlash ? plastSlash+1 : argv[1]); + outFilename = calloc(1,strlen(plastSlash)+2); + if(!outFilename) { + fprintf(stderr,"calloc failed\n"); + exit(-1); + } + strcpy(outFilename,plastSlash); + pext = strstr(outFilename,".data"); + if(!pext) { + fprintf(stderr,"Input file MUST have .data extension\n"); + exit(-1); + } + strcpy(pext,".dbd"); + } else { + outFilename = calloc(1,strlen(argv[2])+1); + if(!outFilename) { + fprintf(stderr,"calloc failed\n"); + exit(-1); + } + strcpy(outFilename,argv[2]); + } + inFile = fopen(argv[1],"r"); + if(!inFile) { + fprintf(stderr,"Error opening %s\n",argv[1]); + exit(-1); + } + outFile = fopen(outFilename,"w"); + if(!outFile) { + fprintf(stderr,"Error opening %s\n",outFilename); + exit(-1); + } + while(fgets(inbuf,MAX_LINE_SIZE,inFile)) { + linenum++; + pbeg = inbuf; + while(isspace((int)*pbeg) && *pbeg!= '\0') pbeg++; + if(*pbeg == '!' || *pbeg == '\0') continue; + while(*pbeg!='"' && *pbeg!= '\0') pbeg++; + if(*pbeg!='"' ) errExit("Illegal Header"); + pbeg++; pend = pbeg; + while(*pend!='"' && *pend!= '\0') pend++; + if(*pend!='"') errExit("Illegal Header"); + len = pend - pbeg; + if(len<=1) errExit("Illegal Header"); + pname = calloc(len+1,sizeof(char)); + if(!pname) { + fprintf(stderr,"calloc failed while processing line %d\n",linenum); + exit(-1); + } + strncpy(pname,pbeg,len); + pname[len]='\0'; + pbeg = pend + 1; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.engLow = value; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.rawLow = value; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.engHigh = value; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.rawHigh = value; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.accuracy = value; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.tblEngFirst = value; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.tblEngLast = value; + if(getNumber(&pbeg,&value)) errExit("Illegal Header"); + brkCreateInfo.tblEngDelta = value; + goto got_header; + } + errExit("Illegal Header"); +got_header: + phead = pnext = 0; + ndata = 0; + errno = 0; + while(fgets(inbuf,MAX_LINE_SIZE,inFile)) { + double value; + + linenum++; + pbeg = inbuf; + while(!getNumber(&pbeg,&value)) { + ndata++; + pdataList = (dataList *)calloc(1,sizeof(dataList)); + if(!pdataList) { + fprintf(stderr,"calloc failed (after header)" + " while processing line %d\n",linenum); + exit(-1); + } + if(!phead) + phead = pdataList; + else + pnext->next = pdataList; + pdataList->value = value; + pnext = pdataList; + } + } + if(!pname) { + errExit("create_break failed: no name specified\n"); + } + brkCreateInfo.nTable = ndata; + pdata = (double *)calloc(brkCreateInfo.nTable,sizeof(double)); + if(!pdata) { + fprintf(stderr,"calloc failed for table length %ld\n",brkCreateInfo.nTable); + exit(-1); + } + pnext = phead; + for(n=0; nvalue; + pdataList = pnext; + pnext = pnext->next; + free((void *)pdataList); + } + brkCreateInfo.pTable = pdata; + if(create_break(&brkCreateInfo,&brkint[0],MAX_BREAKS,&nBreak)) + errExit("create_break failed\n"); + fprintf(outFile,"breaktable(%s) {\n",pname); + for(n=0; npTable; + long ntable = pbci->nTable; + double ilow, + ihigh, + tbllow, + tblhigh, + slope, + offset; + int ibeg, + iend, + i, + inc, + imax, + n; + double rawBeg, + engBeg, + rawEnd, + engEnd, + engCalc, + engActual, + error; + int valid, + all_ok, + expanding; + /* make checks to ensure that brkCreateInfo makes sense */ + if (pbci->engLow >= pbci->engHigh) { + errExit("create_break: engLow >= engHigh"); + return (-1); + } + if ((pbci->engLow < pbci->tblEngFirst) + || (pbci->engHigh > pbci->tblEngLast)) { + errExit("create_break: engLow > engHigh"); + return (-1); + } + if (pbci->tblEngDelta <= 0.0) { + errExit("create_break: tblEngDelta <= 0.0"); + return (-1); + } + if (ntable < 3) { + errExit("raw data must have at least 3 elements"); + return (-1); + } +/*************************************************************************** + Convert Table to raw values + * + * raw and table values are assumed to be related by an equation of the form: + * + * raw = slope*table + offset + * + * The following algorithm converts each table value to raw units + * + * 1) Finds the locations in Table corresponding to engLow and engHigh + * Note that these locations need not be exact integers + * 2) Interpolates to obtain table values corresponding to engLow and enghigh + * we now have the equations: + * rawLow = slope*tblLow + offset + * rawHigh = slope*tblHigh + offset + * 4) Solving these equations for slope and offset gives: + * slope=(rawHigh-rawLow)/(tblHigh-tblLow) + * offset=rawHigh-slope*tblHigh + * 5) for each table value set table[i]=table[i]*slope+offset + *************************************************************************/ + /* Find engLow in Table and then compute tblLow */ + ilow = (pbci->engLow - pbci->tblEngFirst) / (pbci->tblEngDelta); + i = (int) ilow; + if (i >= ntable - 1) + i = ntable - 2; + tbllow = table[i] + (table[i + 1] - table[i]) * (ilow - (double) i); + /* Find engHigh in Table and then compute tblHigh */ + ihigh = (pbci->engHigh - pbci->tblEngFirst) / (pbci->tblEngDelta); + i = (int) ihigh; + if (i >= ntable - 1) + i = ntable - 2; + tblhigh = table[i] + (table[i + 1] - table[i]) * (ihigh - (double) i); + /* compute slope and offset */ + slope = (pbci->rawHigh - pbci->rawLow) / (tblhigh - tbllow); + offset = pbci->rawHigh - slope * tblhigh; + /* convert table to raw units */ + for (i = 0; i < ntable; i++) + table[i] = table[i] * slope + offset; + +/***************************************************************************** + * Now create break point table + * + * The algorithm does the following: + * + * It finds one breakpoint interval at a time. For each it does the following: + * + * 1) Use a relatively large portion of the remaining table as an interval + * 2) It attempts to use the entire interval as a breakpoint interval + * Success is determined by the following algorithm: + * a) compute the slope using the entire interval + * b) for each table entry in the interval determine the eng value + * using the slope just determined. + * c) compare the computed value with eng value associated with table + * d) if all table entries are within the accuracy desired then success. + * 3) If successful then attempt to expand the interval and try again. + * Note that it is expanded by up to 1/10 of the table size. + * 4) If not successful reduce the interval by 1 and try again. + * Once the interval is being decreased it will never be increased again. + * 5) The algorithm will ultimately fail or will have determined the optimum + * breakpoint interval + *************************************************************************/ + + /* Must start with table entry corresponding to engLow; */ + i = (int) ilow; + if (i >= ntable - 1) + i = ntable - 2; + rawBeg = table[i] + (table[i + 1] - table[i]) * (ilow - (double) i); + engBeg = pbci->engLow; + ibeg = (int) (ilow); /* Make sure that ibeg > ilow */ + if( ibeg < ilow ) ibeg = ibeg + 1; + /* start first breakpoint interval */ + n = 1; + pbrkInt = pabrkInt; + pbrkInt->raw = rawBeg; + pbrkInt->eng = engBeg; + /* determine next breakpoint interval */ + while ((engBeg <= pbci->engHigh) && (ibeg < ntable - 1)) { + /* determine next interval to try. Up to 1/10 full range */ + rawEnd = rawBeg; + engEnd = engBeg; + iend = ibeg; + inc = (int) ((ihigh - ilow) / 10.0); + if (inc < 1) + inc = 1; + valid = TRUE; + /* keep trying intervals until cant do better */ + expanding = TRUE; /* originally we are trying larger and larger + * intervals */ + while (valid) { + imax = iend + inc; + if (imax >= ntable) { + /* don't go past end of table */ + imax = ntable - 1; + inc = ntable - iend - 1; + expanding = FALSE; + } + if (imax > (int) (ihigh + 1.0)) { /* Don't go to far past + * engHigh */ + imax = (int) (ihigh + 1.0); + inc = (int) (ihigh + 1.0) - iend; + expanding = FALSE; + } + if (imax <= ibeg) + break; /* failure */ + rawEnd = table[imax]; + engEnd = pbci->tblEngFirst + (double) imax *(pbci->tblEngDelta); + slope = (engEnd - engBeg) / (rawEnd - rawBeg); + all_ok = TRUE; + for (i = ibeg + 1; i <= imax; i++) { + engCalc = engBeg + slope * (table[i] - rawBeg); + engActual = pbci->tblEngFirst + ((double) i) * (pbci->tblEngDelta); + error = engCalc - engActual; + if (error < 0.0) + error = -error; + if (error >= pbci->accuracy) { + /* we will be trying smaller intervals */ + expanding = FALSE; + /* just decrease inc and let while(valid) try again */ + inc--; + all_ok = FALSE; + break; + } + } /* end for */ + if (all_ok) { + iend = imax; + /* if not expanding we found interval */ + if (!expanding) + break; + /* will automatically try larger interval */ + } + } /* end while(valid) */ + /* either we failed or optimal interval has been found */ + if ((iend <= ibeg) && (iend < (int) ihigh)) { + errExit("Could not meet accuracy criteria"); + return (-1); + } + pbrkInt->slope = slope; + /* get ready for next breakpoint interval */ + if (n++ >= max_breaks) { + errExit("Break point table too large"); + return (-1); + } + ibeg = iend; + pbrkInt++; + rawBeg = rawEnd; + engBeg = engEnd; + pbrkInt->raw = rawBeg; + pbrkInt->eng = engBeg + (pbrkInt->raw - rawBeg) * slope; + } + pbrkInt->slope = 0.0; + *n_breaks = n; + return (0); +} diff --git a/src/bpt/menuConvert.dbd b/src/bpt/menuConvert.dbd new file mode 100644 index 000000000..1e1e16c4c --- /dev/null +++ b/src/bpt/menuConvert.dbd @@ -0,0 +1,26 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuConvert) { + choice(menuConvertNO_CONVERSION,"NO CONVERSION") + choice(menuConvertSLOPE,"SLOPE") + choice(menuConvertLINEAR,"LINEAR") + choice(menuConverttypeKdegF,"typeKdegF") + choice(menuConverttypeKdegC,"typeKdegC") + choice(menuConverttypeJdegF,"typeJdegF") + choice(menuConverttypeJdegC,"typeJdegC") + choice(menuConverttypeEdegF,"typeEdegF(ixe only)") + choice(menuConverttypeEdegC,"typeEdegC(ixe only)") + choice(menuConverttypeTdegF,"typeTdegF") + choice(menuConverttypeTdegC,"typeTdegC") + choice(menuConverttypeRdegF,"typeRdegF") + choice(menuConverttypeRdegC,"typeRdegC") + choice(menuConverttypeSdegF,"typeSdegF") + choice(menuConverttypeSdegC,"typeSdegC") +} diff --git a/src/ca/CASG.cpp b/src/ca/CASG.cpp new file mode 100644 index 000000000..16573d1bc --- /dev/null +++ b/src/ca/CASG.cpp @@ -0,0 +1,314 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + */ + +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "syncGroup.h" +#include "oldAccess.h" +#include "cac.h" +#include "sgAutoPtr.h" + +casgRecycle::~casgRecycle () {} + +CASG::CASG ( epicsGuard < epicsMutex > & guard, ca_client_context & cacIn ) : + client ( cacIn ), magic ( CASG_MAGIC ) +{ + client.installCASG ( guard, *this ); +} + +CASG::~CASG () +{ +} + +void CASG::destructor ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + + if ( this->verify ( guard ) ) { + this->reset ( guard ); + this->client.uninstallCASG ( guard, *this ); + this->magic = 0; + } + else { + this->printFormated ( "cac: attempt to destroy invalid sync group ignored\n" ); + } + this->~CASG (); +} + +bool CASG::verify ( epicsGuard < epicsMutex > & ) const +{ + return ( this->magic == CASG_MAGIC ); +} + +/* + * CASG::block () + */ +int CASG::block ( + epicsGuard < epicsMutex > * pcbGuard, + epicsGuard < epicsMutex > & guard, + double timeout ) +{ + epicsTime cur_time; + epicsTime beg_time; + double delay; + double remaining; + int status; + + guard.assertIdenticalMutex ( this->client.mutexRef() ); + + // prevent recursion nightmares by disabling blocking + // for IO from within a CA callback. + if ( epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { + return ECA_EVDISALLOW; + } + + if ( timeout < 0.0 ) { + return ECA_TIMEOUT; + } + + cur_time = epicsTime::getCurrent (); + + this->client.flush ( guard ); + + beg_time = cur_time; + delay = 0.0; + + while ( 1 ) { + if ( this->ioPendingList.count() == 0u ) { + status = ECA_NORMAL; + break; + } + + remaining = timeout - delay; + if ( remaining <= CAC_SIGNIFICANT_DELAY ) { + /* + * Make sure that we take care of + * recv backlog at least once + */ + status = ECA_TIMEOUT; + break; + } + + if ( pcbGuard ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < epicsMutex > uncbGuard ( *pcbGuard ); + this->sem.wait ( remaining ); + } + } + else { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->sem.wait ( remaining ); + } + + /* + * force a time update + */ + cur_time = epicsTime::getCurrent (); + + delay = cur_time - beg_time; + } + + this->reset ( guard ); + + return status; +} + +void CASG::reset ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + this->destroyCompletedIO ( guard ); + this->destroyPendingIO ( guard ); +} + +// lock must be applied +void CASG::destroyCompletedIO ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + syncGroupNotify * pNotify; + while ( ( pNotify = this->ioCompletedList.get () ) ) { + pNotify->destroy ( guard, * this ); + } +} + +void CASG::destroyPendingIO ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + while ( syncGroupNotify * pNotify = this->ioPendingList.first () ) { + pNotify->cancel ( guard ); + // cancel must release the guard while + // canceling put callbacks so we + // must double check list membership + if ( pNotify->ioPending ( guard ) ) { + this->ioPendingList.remove ( *pNotify ); + } + else { + this->ioCompletedList.remove ( *pNotify ); + } + pNotify->destroy ( guard, *this ); + } +} + +void CASG::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->client.mutexRef () ); + this->show ( guard, level ); +} + +void CASG::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + ::printf ( "Sync Group: id=%u, magic=%u, opPend=%u\n", + this->getId (), this->magic, this->ioPendingList.count () ); + if ( level ) { + ::printf ( "\tPending" ); + tsDLIterConst < syncGroupNotify > notifyPending = + this->ioPendingList.firstIter (); + while ( notifyPending.valid () ) { + notifyPending->show ( guard, level - 1u ); + notifyPending++; + } + ::printf ( "\tCompleted" ); + tsDLIterConst < syncGroupNotify > notifyCompleted = + this->ioCompletedList.firstIter (); + while ( notifyCompleted.valid () ) { + notifyCompleted->show ( guard, level - 1u ); + notifyCompleted++; + } + } +} + +bool CASG::ioComplete ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + this->destroyCompletedIO ( guard ); + return this->ioPendingList.count () == 0u; +} + +void CASG::put ( epicsGuard < epicsMutex > & guard, chid pChan, + unsigned type, arrayElementCount count, const void * pValue ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + sgAutoPtr < syncGroupWriteNotify > pNotify ( guard, *this, this->ioPendingList ); + pNotify = syncGroupWriteNotify::factory ( + this->freeListWriteOP, *this, pChan ); + pNotify->begin ( guard, type, count, pValue ); + pNotify.release (); +} + +void CASG::get ( epicsGuard < epicsMutex > & guard, chid pChan, + unsigned type, arrayElementCount count, void *pValue ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + sgAutoPtr < syncGroupReadNotify > pNotify ( guard, *this, this->ioPendingList ); + pNotify = syncGroupReadNotify::factory ( + this->freeListReadOP, *this, pChan, pValue ); + pNotify->begin ( guard, type, count ); + pNotify.release (); +} + +void CASG::completionNotify ( + epicsGuard < epicsMutex > & guard, syncGroupNotify & notify ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + this->ioPendingList.remove ( notify ); + this->ioCompletedList.add ( notify ); + if ( this->ioPendingList.count () == 0u ) { + this->sem.signal (); + } +} + +void CASG::recycleSyncGroupWriteNotify ( + epicsGuard < epicsMutex > & guard, syncGroupWriteNotify & io ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + this->freeListWriteOP.release ( & io ); +} + +void CASG::recycleSyncGroupReadNotify ( + epicsGuard < epicsMutex > & guard, syncGroupReadNotify & io ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + this->freeListReadOP.release ( & io ); +} + +int CASG :: printFormated ( const char *pformat, ... ) +{ + va_list theArgs; + int status; + + va_start ( theArgs, pformat ); + + status = this->client.varArgsPrintFormated ( pformat, theArgs ); + + va_end ( theArgs ); + + return status; +} + +void CASG::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char * pContext, + const char * pFileName, unsigned lineNo ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + if ( status != ECA_CHANDESTROY ) { + this->client.exception ( + guard, status, pContext, pFileName, lineNo ); + } +} + +void CASG::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, + unsigned type, arrayElementCount count, unsigned op ) +{ + guard.assertIdenticalMutex ( this->client.mutexRef() ); + if ( status != ECA_CHANDESTROY ) { + this->client.exception ( + guard, status, pContext, pFileName, + lineNo, chan, type, count, op ); + } +} + +void * CASG::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void CASG::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} diff --git a/src/ca/CAref.html b/src/ca/CAref.html new file mode 100644 index 000000000..17855839e --- /dev/null +++ b/src/ca/CAref.html @@ -0,0 +1,4457 @@ + + + + + EPICS R3.14 Channel Access Reference Manual + + + + +
    + +

    EPICS R3.14 Channel Access Reference Manual

    +
    + Jeffrey O. Hill +
    + +

    Los Alamos National +Laboratory, SNS Division

    +
    + Ralph Lange +
    + +

    Helmholtz-Zentrum +Berlin (BESSY II)

    + +

    Copyright © 2009 +Helmholtz-Zentrum Berlin für Materialien und Energie GmbH.
    +Copyright © 2002 The University of Chicago, as Operator of Argonne National +Laboratory.
    +Copyright © 2002 The Regents of the University of California, as Operator of +Los Alamos National Laboratory.
    +Copyright © 2002 Berliner Speicherringgesellschaft für Synchrotronstrahlung +GmbH.

    + +

    EPICS BASE is +distributed subject to a Software License Agreement found +in the file LICENSE that is included with this distribution.

    + +

    W3C-Amaya Valid HTML 4.01!

    + +

    Modified on +Date: Wed 2010-11-24 13:31:47 -0500

    +
    + +

    Table of Contents

    + +

    Configuration

    + + +

    Building an Application

    + + +

    Command Line Utilities

    + + +

    Command Line Tools

    + + +

    Troubleshooting

    + + +

    Function Call Interface Guidelines

    + + +

    Functionality Index

    + + +

    Function Call Interface Index

    + + +

    Deprecated Function Call Interface Function Index

    + + +

    Return Codes

    +
    + +

    Configuration

    + +

    Why Reconfigure Channel Access

    + +

    Typically reasons to reconfigure EPICS Channel Access:

    +
      +
    • Two independent control systems must share a network without fear of + interaction
    • +
    • A test system must not interact with an operational system
    • +
    • Use of address lists instead of broadcasts for name resolution and server + beacons
    • +
    • Control system occupies multiple IP subnets
    • +
    • Nonstandard client disconnect time outs or server beacon intervals
    • +
    • Specify the local time zone
    • +
    • Transport of large arrays
    • +
    + +

    EPICS Environment Variables

    + +

    All Channel Access (CA) configuration occurs through EPICS environment +variables. When searching for an EPICS environment variable EPICS first looks +in the environment using the ANSI C getenv() call. If no matching variable +exists then the default specified in the EPICS build system configuration files +is used.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRangeDefault
    EPICS_CA_ADDR_LIST{N.N.N.N N.N.N.N:P ...}<none>
    EPICS_CA_AUTO_ADDR_LIST{YES, NO}YES
    EPICS_CA_NAME_SERVERS{N.N.N.N N.N.N.N:P ...}<none>
    EPICS_CA_CONN_TMOr > 0.1 seconds30.0
    EPICS_CA_BEACON_PERIODr > 0.1 seconds15.0
    EPICS_CA_REPEATER_PORTi > 50005065
    EPICS_CA_SERVER_PORTi > 50005064
    EPICS_CA_MAX_ARRAY_BYTESi >= 1638416384
    EPICS_CA_MAX_SEARCH_PERIODr > 60 seconds300
    EPICS_TS_MIN_WEST-720 < i <720 minutes360
    + +

    Environment variables are set differently depending on the command line +shell that is in use.

    + + + + + + + + + + + + + + + + + + + + + + + + +
    C shellsetenv EPICS_CA_ADDR_LIST 1.2.3.4
    bashexport EPICS_CA_ADDR_LIST=1.2.3.4
    vxWorks shellputenv ( "EPICS_CA_ADDR_LIST=1.2.3.4" )
    DOS command lineset EPICS_CA_ADDR_LIST=1.2.3.4
    Windows NT / 2000 / XPcontrol panel / system / environment tab
    + +

    CA and Wide Area Networks

    + +

    Normally in a local area network (LAN) environment CA discovers the address +of the host for an EPICS process variable by broadcasting frames containing a +list of channel names (CA search messages) and waiting for responses from the +servers that host the channels identified. Likewise CA clients efficiently +discover that CA servers have recently joined the LAN or disconnected from the +LAN by monitoring periodically broadcasted beacons sent out by the servers. +Since hardware broadcasting requires special hardware capabilities, we are +required to provide additional configuration information when EPICS is extended +to operate over a wide area network (WAN).

    + +

    IP Network Administration Background Information

    + +

    Channel Access is implemented using internet protocols (IP). IP addresses +are divided into host and network portions. The boundary between each portion +is determined by the IP netmask. Portions of the IP address corresponding to +zeros in the netmask specify the hosts address within an IP subnet. Portions of +the IP address corresponding to binary ones in the netmask specify the address +of a host's IP subnet. Normally the scope of a broadcasted frame will be +limited to one IP subnet. Addresses with the host address portion set to all +zeros or all ones are special. Modern IP kernel implementations reserve +destination addresses with the host portion set to all ones for the purpose of +addressing broadcasts to a particular subnet. In theory we can issue a +broadcast frame on any broadcast capable LAN within the interconnected internet +by specifying the proper subnet address combined with a host portion set to all +ones. In practice these "directed broadcasts" are frequently limited by the +default router configuration. The proper directed broadcast address required to +reach a particular host can be obtained by logging into that host and typing +the command required by your local operating environment. Ignore the loop back +interface and use the broadcast address associated with an interface connected +to a path through the network to your client. Typically there will be only one +Ethernet interface.

    + + + + + + + + + + + + + + + + +
    UNIXifconfig -a
    vxWorksifShow
    Windowsipconfig
    + +

    IP ports are positive integers. The IP address, port number, and protocol +type uniquely identify the source and destination of a particular frame +transmitted between computers. Servers are typically addressed by a well known +port number. Clients are assigned a unique ephemeral port number during +initialization. IP ports below 1024 are reserved for servers that provide +standardized facilities such as mail or file transfer. Port number between 1024 +and 5000 are typically reserved for ephemeral port number assignments.

    + +

    IP port numbers

    + +

    The two default IP port numbers used by Channel Access may be reconfigured. +This might occur when a site decides to set up two or more completely +independent control systems that will share the same network. For instance, a +site might set up an operational control system and a test control system on +the same network. In this situation it is desirable for the test system and the +operational system to use identical PV names without fear of collision. A site +might also configure the CA port numbers because some other facility is already +using the default port numbers. The default Channel Access port numbers have +been registered with IANA.

    + + + + + + + + + + + + + + + + + + + + +
    PurposeDefaultEnvironment Variable
    CA Server5064EPICS_CA_SERVER_PORT
    CA Beacons (sent to CA repeater daemon)5065EPICS_CA_REPEATER_PORT
    + +

    If a client needs to communicate with two servers that are residing at +different port numbers then an extended syntax may be used with the +EPICS_CA_ADDR_LIST environment variable. See WAN +Environment below.

    + +

    Firewalls

    + +

    If you want channel access clients on a machine to be able to see +beacons and replies to broadcast PV search requests you need to permit +inbound UDP packets with source port EPICS_CA_SERVER_PORT (default is 5064) +or destination port EPICS_CA_REPEATER_PORT (default is 5065). On systems +using iptables this can be accomplished by rules like

    + +
    +     -A INPUT -s 192.168.0.0/22 -p udp --sport 5064 -j ACCEPT
    +     -A INPUT -s 192.168.0.0/22 -p udp --dport 5065 -j ACCEPT
    +
    + +

    If you want channel access servers (e.g. "soft IOCs") on a machine to be +able to see clients you need to permit inbound TCP or UDP packets with source +port EPICS_CA_SERVER_PORT (default is 5064). On systems using iptables this +can be accomplished by rules like

    + +
    +     -A INPUT -s 192.168.0.0/22 -p udp --dport 5064 -j ACCEPT
    +     -A INPUT -s 192.168.0.0/22 -p tcp --dport 5064 -j ACCEPT
    +
    + +

    In all cases the "-s 192.168.0.0/22" specifies the range of addresses from +which you wish to accept packets.

    + +

    WAN Environment

    + +

    When the CA client library connects a channel it must first determine the IP +address of the server the channels Process Variable resides on. To accomplish +this the client sends name resolution (search) requests to a list of server +destination addresses. These server destination addresses can be IP unicast +addresses (individual host addresses) or IP broadcast addresses. Each name +resolution (search) request contains a list of Process Variable names.If one of +the servers reachable by this address list knows the IP address of a CA server +that can service one or more of the specified Process Variables, then it sends +back a response containing the server's IP address and port number.

    + +

    During initialization CA builds the list of server destination addresses +used when sending CA client name resolution (search) requests. This table is +initialized by introspecting the network interfaces attached to the host. For +each interface found that is attached to a broadcast capable IP subnet, the +broadcast address of that subnet is added to the list. For each point to point +interface found, the destination address of that link is added to the list. +This automatic server address list initialization can be disabled if the EPICS +environment variable EPICS_CA_AUTO_ADDR_LIST exists and its value is either +of "no" or "NO". The typical default is to enable network interface +introspection driven initialization with EPICS_CA_AUTO_ADDR_LIST set to "YES" +or "yes".

    + +

    Following network interface introspection, any IP addresses specified in the +EPICS environment variable EPICS_CA_ADDR_LIST are added to the list of +destination addresses for CA client name resolution requests. In an EPICS +system crossing multiple subnets the EPICS_CA_ADDR_LIST must be set so that CA +name resolution (search requests) frames pass from CA clients to the targeted +CA servers unless a CA proxy (gateway) is installed. The addresses in +EPICS_CA_ADDR_LIST may be dotted IP addresses or host names if the local OS has +support for host name to IP address translation. When multiple names are added +to EPICS_CA_ADDR_LIST they must be separated by white space. There is no +requirement that the addresses specified in the EPICS_CA_ADDR_LIST be +broadcast addresses, but this will often be the most convenient choice.

    + +

    For any IP addresses specified in the EPICS environment variable +EPICS_CA_NAME_SERVERS, TCP connections are opened and used for CA client name +resolution requests. (Thus, broadcast addresses are not allowed in +EPICS_CA_NAME_SERVERS.) When used in combination with an empty +EPICS_CA_ADDR_LIST and EPICS_CA_AUTO_ADDR_LIST set to "NO", Channel Access can +be run without using UDP for name resolution. Such an TCP-only mode allows for +Channel Access to work e.g. through SSH tunnels.

    + + + + + + + + + + + + + + + + +
    C shellsetenv EPICS_CA_ADDR_LIST "1.2.3.255 8.9.10.255"
    bashexport EPICS_CA_ADDR_LIST="1.2.3.255 8.9.10.255"
    vxWorksputenv ( "EPICS_CA_ADDR_LIST=1.2.3.255 8.9.10.255" )
    + +

    If a client needs to communicate with two servers that are residing at +different port numbers then an extended syntax may be used with the +EPICS_CA_ADDR_LIST environment variable. Each host name or IP address in the +EPICS_CA_ADDR_LIST may be immediately followed by a colon and an IP port number +without intervening whitespace. Entries that do not specify a port number will +default to EPICS_CA_SERVER_PORT.

    + + + + + + + + +
    C shellsetenv EPICS_CA_ADDR_LIST "1.2.3.255 8.9.10.255:10000"
    + +

    Routing Restrictions on vxWorks Systems

    + +

    Frequently vxWorks systems boot by default with routes limiting access only +to the local subnet. If a EPICS system is operating in a WAN environment it may +be necessary to configure routes into the vxWorks system which enable a vxWorks +based CA server to respond to requests originating outside it's subnet. These +routing restrictions can also apply to vxWorks base CA clients communicating +with off subnet servers. An EPICS system manager can implement an rudimentary, +but robust, form of access control for a particular host by not providing +routes in that host that reach outside of a limited set of subnets. See +"routeLib" in the vxWorks reference manual.

    + +

    Disconnect Time Out Interval

    + +

    If the CA client library does not see a beacon from a server that it is +connected to for EPICS_CA_CONN_TMO seconds then an state-of-health message is +sent to the server over TCP/IP. If this state-of-health message isn't promptly +replied to then the client library will conclude that channels communicating +with the server are no longer responsive and inform the CA client side +application via function callbacks. The parameter EPICS_CA_CONN_TMO is +specified in floating point seconds. The default is typically 30 seconds. For +efficient operation it is recommended that EPICS_CA_CONN_TMO be set to no less +than twice the value specified for EPICS_CA_BEACON_PERIOD.

    + +

    Prior to EPICS R3.14.5 an unresponsive server implied an immediate TCP +circuit disconnect, immediate resumption of UDP based search requests, and +immediate attempts to reconnect. There was concern about excessive levels of +additional activity when servers are operated close to the edge of resource +limitations. Therefore with version R3.14.5 and greater the CA client library +continues to inform client side applications when channels are unresponsive, +but does not immediately disconnect the TCP circuit. Instead the CA client +library postpones circuit shutdown until receiving indication of circuit +disconnect from the IP kernel. This can occur either because a server is +restarted or because the IP kernel's internal TCP circuit inactivity keep alive +timer has expired after a typically long duration (as is appropriate for IP +based systems that need to avoid thrashing during periods of excessive load). +The net result is less search and TCP circuit setup and shutdown activity +suring periods of excessive load.

    + +

    Dynamic Changes in the CA Client Library Search +Interval

    + +

    The CA client library will continuously attempt to connect any CA channels +that an application has created until it is successful. The library +periodically queries the server destination address list described above with +name resolution requests for any unresolved channels. Since this address list +frequently contains broadcast addresses, and because nonexistent process +variable names are frequently configured, or servers may be temporarily +unavailable, then it is necessary for the CA client library internals to +carefully schedule these requests in time to avoid introducing excessive load +on the network and the servers.

    + +

    When the CA client library has many channels to connect, and most of its +name resolution requests are responded to, then it sends name resolution +requests at an interval that is twice the estimated round trip interval for the +set of servers responding, or at the minimum delay quantum for the operating +system - whichever is greater. The number of UDP frames per interval is also +dynamically adjusted based on the past success rates.

    + +

    If a name resolution request is not responded to, then the client library +doubles the delay between name resolution attempts and reduces the number of +requests per interval. The maximum delay between attempts is limited by +EPICS_CA_MAX_SEARCH_PERIOD (see Configuring the Maximum +Search Period). Note however that prior to R3.14.7, if the client library +did not receive any responses over a long interval it stoped sending name +resolution attempts altogether until a beacon anomaly was detected (see +below).

    + +

    The CA client library continually estimates the beacon period of all server +beacons received. If a particular server's beacon period becomes significantly +shorter or longer then the client is said to detect a beacon anomaly. The +library boosts the search interval for unresolved channels when a beacon +anomaly is seen or when any successful search response is received, +but with a longer initial interval between requests than is used when the +application creates a channel. Creation of a new channel does not +(starting with EPICS R3.14.7) change the interval used when searching for +preexisting unresolved channels. The program "casw" prints a message on +standard out for each CA client beacon anomaly detect event.

    + +

    See also When a Client Does not See the Server's +Beacon.

    + +

    Configuring the Maximum Search +Period

    + +

    The rate at which name resolution (search) requests are sent exponentially +backs off to a plateau rate. The value of this plateau has an impact on network +traffic because it determines the rate that clients search for channel names +that are miss-spelled or otherwise don't exist in a server. Furthermore, for +clients that are unable to see the beacon from a new server, the plateau rate +may also determine the maximum interval that the client will wait until +discovering a new server.

    + +

    Starting with EPICS R3.14.7 this maximum search rate interval plateau in +seconds is determined by the EPICS_CA_MAX_SEARCH_PERIOD environment +variable.

    + +

    See also When a Client Does not See the Server's +Beacon.

    + +

    The CA Repeater

    + +

    When several client processes run on the same host it is not possible for +all of them to directly receive a copy of the server beacon messages when the +beacon messages are sent to unicast addresses, or when legacy IP kernels are +still in use. To avoid confusion over these restrictions a special UDP server, +the CA Repeater, is automatically spawned by the CA client library when it is +not found to be running. This program listens for server beacons sent to the +UDP port specified in the EPICS_CA_REPEATER_PORT parameter and fans any beacons +received out to any CA client program running on the same host that have +registered themselves with the CA Repeater. If the CA Repeater is not already +running on a workstation, then the "caRepeater" program must be in your path +before using the CA client library for the first time.

    + +

    If a host based IOC is run on the same workstation with standalone CA client +processes, then it is probably best to start the caRepeater process when the +workstation is booted. Otherwise it is possible for the standalone CA client +processes to become dependent on a CA repeater started within the confines of +the host based IOC. As long as the host based IOC continues to run there is +nothing wrong with this situation, but problems could arise if this host based +IOC process exits before the standalone client processes which are relying on +its CA repeater for services exit.

    + +

    Since the repeater is intended to be shared by multiple clients then it +could be argued that it makes less sense to set up a CA repeater that listens +for beacons on only a subset of available network interfaces. In the worst case +situation the client library might see beacon anomalies from servers that it is +not interested in. Modifications to the CA repeater forcing it to listen only +on a subset of network interfaces might be considered for a future release if +there appear to be situations that require it.

    + +

    Configuring the Time Zone

    + +

    Note: Starting with EPICS R3.14 all of the libraries in the EPICS base +distribution rely on facilities built into the operating system to determine +the correct time zone. Nevertheless, several programs commonly used with EPICS +still use the original "tssubr" library and therefore they still rely on proper +configuration of EPICS_TS_MIN_WEST.

    + +

    While the CA client library does not translate inbetween the local time and +the time zone independent internal storage of EPICS time stamps, many EPICS +client side applications call core EPICS libraries which provide these +services. To set the correct time zone users must compute the number of +positive minutes west of GMT (maximum 720 inclusive) or the negative number of +minutes east of GMT (minimum -720 inclusive). This integer value is then placed +in the variable EPICS_TS_MIN_WEST.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Time ZoneEPICS_TS_MIN_WEST
    USA Eastern300
    USA Central360
    USA Mountain420
    USA Pacific480
    Alaska540
    Hawaii600
    Japan-540
    China-420
    Germany-120
    United Kingdom0
    + +

    Configuring the Maximum Array Size

    + +

    Starting with version R3.14 the environment variable +EPICS_CA_MAX_ARRAY_BYTES determines the size of the largest array that may pass +through CA. Prior to this version only arrays smaller than 16k bytes could be +transfered. The CA libraries maintains a free list of 16384 byte network +buffers that are used for ordinary communication. If EPICS_CA_MAX_ARRAY_BYTES +is larger than 16384 then a second free list of larger data buffers is +established and used only after a client send its first large array request.

    + +

    The CA client library uses EPICS_CA_MAX_ARRAY_BYTES to determines the +maximum array that it will send or receive. Likewise, the CA server uses +EPICS_CA_MAX_ARRAY_BYTES to determine the maximum array that it may send or +receive. The client does not influence the server's message size quotas and +visa versa. In fact the value of EPICS_CA_MAX_ARRAY_BYTES need not be the same +in the client and the server. If the server receives a request which is too +large to read or respond to in entirety then it sends an exception message to +the client. Likewise, if the CA client library receives a request to send an +array larger than EPICS_CA_MAX_ARRAY_BYTES it will return ECA_TOLARGE.

    + +

    A common mistake is to correctly calculate the maximum datum size in bytes +by multiplying the number of elements by the size of a single element, but +neglect to add additional bytes for the compound data types (for example +DBR_GR_DOUBLE) commonly used by the more sophisticated client side +applications. Based on this confusion, one could arrive at the conclusion +that EPICS_CA_MAX_ARRAY_BYTES might have been better named +EPICS_CA_MAX_DATUM_BYTES, or that the software should be changed internally to +round the users request up by the size of the maximum scalar datum (nothing has +been done to address this issue so far).

    + +

    Configuring a CA Server

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRangeDefault
    EPICS_CAS_SERVER_PORTi > 5000EPICS_CA_SERVER_PORT
    EPICS_CAS_AUTO_BEACON_ADDR_LIST{YES, NO}EPICS_CA_AUTO_ADDR_LIST
    EPICS_CAS_BEACON_ADDR_LIST{N.N.N.NN.N.N.N:P...}EPICS_CA_ADDR_LIST1
    EPICS_CAS_BEACON_PERIODr > 0.1 secondsEPICS_CA_BEACON_PERIOD
    EPICS_CAS_BEACON_PORTi > 5000EPICS_CA_REPEATER_PORT
    EPICS_CAS_INTF_ADDR_LIST{N.N.N.NN.N.N.N:P...}<none>
    EPICS_CAS_IGNORE_ADDR_LIST{N.N.N.NN.N.N.N:P...}<none>
    + +

    Server Port

    + +

    The server configures its port number from the EPICS_CAS_SERVER_PORT +environment variable if it is specified. Otherwise the EPICS_CA_SERVER_PORT +environment variable determines the server's port number. Two servers can share +the same UDP port number on the same machine, but there are restrictions - see +a discussion of unicast addresses and two servers sharing +the same UDP port on the same host.

    + +

    Server Beacons

    + +

    The EPICS_CAS_BEACON_PERIOD parameter determines the server's beacon period +and is specified in floating point seconds. The default is typically 15 +seconds. See also EPICS_CA_CONN_TMO and Dynamic Changes in the CA Client Library Search +Interval.

    + +

    CA servers build a list of addresses to send beacons to during +initialization. If EPICS_CAS_AUTO_BEACON_ADDR_LIST has the value "YES" then the +beacon address list will be automatically configured to contain the broadcast +addresses of all LAN interfaces found in the host and the destination address +of all point-to-point interfaces found in the host. However, if the user also +defines EPICS_CAS_INTF_ADDR_LIST then beacon address list automatic +configuration is constrained to the network interfaces specified therein, and +therefore only the broadcast addresses of the specified LAN interfaces, and the +destination addresses of all specified point-to-point links, will be +automatically configured.

    + +

    If EPICS_CAS_BEACON_ADDR_LIST is defined then its contents will be used to +augment any automatic configuration of the beacon address list. Individual +entries in EPICS_CAS_BEACON_ADDR_LIST may override the destination port number +if ":nnn" follows the host name or IP address there. Alternatively, when both +EPICS_CAS_BEACON_ADDR_LIST and EPICS_CAS_INTF_ADDR_LIST are not defined then +the contents of EPICS_CA_ADDR_LIST is used to augment the list. Otherwise, the +list is not augmented.

    + +

    The EPICS_CAS_BEACON_PORT parameter specifies the destination port for +server beacons. The only exception to this occurs when ports are specified +in EPICS_CAS_BEACON_ADDR_LIST or possibly in EPICS_CA_ADDR_LIST. If +EPICS_CAS_BEACON_PORT is not specified then beacons are sent to the port +specified in EPICS_CA_REPEATER_PORT.

    + +

    Binding a Server to a Limited Set of Network Interfaces

    + +

    The parameter EPICS_CAS_INTF_ADDR_LIST allows a ca server to bind itself to, +and therefore accept messages only over, a limited set of the local host's +network interfaces (each specified by it's IP address). On UNIX systems type +"netstat -i" (type "ipconfig" on windows) to see a list of the local host's +network interfaces. Specifically, UDP search messages addressed to both the IP +addresses in EPICS_CAS_INTF_ADDR_LIST and also to the broadcast addresses of +the corresponding LAN interfaces will be accepted by the server. By default, +the CA server is accessible from all network interfaces configured into its +host. In R3.14 and previous releases the CA server employed by iocCore does +not implement this feature.

    + +

    Ignoring Process Variable Name Resolution Requests From Certain Hosts

    + +

    Name resolution requests originating from any of the IP addresses specified +in the EPICS_CAS_IGNORE_ADDR_LIST parameter are not replied to.In R3.14 and +previous releases the CA server employed by iocCore does not implement this +feature.

    + +

    Client Configuration that also Applies to Servers

    + +

    See also Configuring the Maximum Array Size.

    + +

    See also Routing Restrictions on vxWorks Systems.

    +
    + +

    Building an Application

    + +

    Required Header (.h) Files

    + +

    An application that uses the CA client library functions described in this +document will need to include the cadef.h header files as follows.

    + +

    #include "cadef.h"

    + +

    This header file is located at "<EPICS base>/include/". It includes +many other header files (operating system specific and otherwise), and +therefore the application must also specify "<EPICS +base>/include/os/<arch>" in its header file search path.

    + +

    Required Libraries

    + +

    An application that uses the Channel Access Client Library functions +described in this document will need to link with the EPICS CA Client Library +and also the EPICS Common Library. The EPICS CA Client Library calls the EPICS +Common Library. The following table shows the names of these libraries on UNIX +and Windows systems.

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    UNIX ObjectUNIX ShareableWindows ObjectWindows Shareable
    EPICS CA Client Librarylibca.alibca.soca.libca.dll
    +
    + EPICS Common Library
    +
    libCom.alibCom.soCom.libCom.dll
    + +

    + +

    The above libraries are located in "<EPICS +base>/lib/<architechture>".

    + +

    Compiler and System Specific Build +Options

    + +

    If you do not use the EPICS build environemnt (layered make files) then it +may be helpful to run one of the EPICS make files and watch the compile/link +lines. This may be the simplest way to capture the latest system and compiler +specific options required by your build environment. I have included some +snapshots of typical build lines below, but expect some risk of this +information becoming dated.

    + +

    Typical Linux Build Options

    + +

    /usr/bin/gcc -c -D_POSIX_C_SOURCE=199506L -D_POSIX_THREADS +-D_XOPEN_SOURCE=500 -DOSITHREAD_USE_DEFAULT_STACK -D_X86_ -DUNIX -D_BSD_SOURCE +-Dlinux -D_REENTRANT -ansi -O3 -Wall -I. -I.. -I../../../include/os/Linux +-I../../../include ../acctst.c

    + +

    /usr/bin/g++ -o acctst +-L/home/user/epicsR3.14/epics/base/lib/linux-x86/ +-Wl,-rpath,/mnt/bogart_home/hill/epicsR3.14/epics/base/lib/linux-x86 +acctstMain.o acctst.o -lca -lCom

    + +

    Typical Solaris Build Options

    + +

    /opt/SUNWspro/bin/cc -c -D_POSIX_C_SOURCE=199506L -D_XOPEN_SOURCE=500 +-DOSITHREAD_USE_DEFAULT_STACK -DUNIX -DSOLARIS=9 -mt -D__EXTENSIONS__ -Xc -v +-xO4 -I. -I.. -I./../../../include/os/solaris -I./../../../include +../acctst.c

    + +

    /opt/SUNWspro/bin/CC -o acctst +-L/home/phoebus1/JHILL/epics/base/lib/solaris-sparc/ -mt -z ignore -z combreloc +-z lazyload -R/home/disk1/user/epics/base/lib/solaris-sparc acctstMain.o +acctst.o -lca -lCom

    + +

    Typical Windows Build Options

    + +

    cl -c /nologo /D__STDC__=0 /Ox /GL /W3 /w44355 /MD -I. -I.. +-I..\\..\\..\\include\\os\\WIN32 -I..\\..\\..\\include ..\\acctst.c

    + +

    link -nologo /LTCG /incremental:no /opt:ref /release /version:3.14 +-out:acctst.exe acctstMain.obj acctst.obj +d:/user/R3.14.clean/epics/base/lib/WIN32-x86/ca.lib +d:/user/R3.14.clean/epics/base/lib/WIN32-x86/

    + +

    Typical vxWorks Build Options

    + +

    /usr/local/xcomp/ppc/bin/ccppc -c -D_POSIX_SOURCE -DCPU=PPC603 +-DvxWorks -include /home/vx/tornado20/target/h/vxWorks.h -ansi -O3 -Wall +-mcpu=603 -mstrict-align -fno-builtin -I. -I.. -I../../../include/os/vxWorks +-I../../../include -I/home/vx/tornado20/target/h ../acctst.c

    + +

    Other Systems and Compilers

    + +

    Contributions gratefully accepted.

    +
    + +

    Command Line Utilities

    + +

    acctst

    +
    acctst <PV name> [progress logging level] [channel duplication count] 
    +                 [test repetition count] [enable preemptive callback]
    + +

    Description

    + +

    Channel Access Client Library regression test.

    + +

    The PV used with the test must be native type DBR_DOUBLE or DBR_FLOAT, and +modified only by acctst while the test is running. Therefore, periodically +scanned hardware attached analog input records do not work well. Test failure +is indicated if the program stops prior to printing "test complete". If +unspecified the progress logging level is zero, and no messages are printed +while the test is progressing. If unspecified, the channel duplication count is +20000. If unspecified, the test repetition count is once only. If unspecified, +preemptive callback is disabled.

    + +

    catime

    +
    catime <PV name> [channel count] [append number to pv name if true]
    + +

    Description

    + +

    Channel Access Client Library performance test.

    + +

    If unspecified, the channel count is 10000. If the "append number to pv name +if true" argument is specified and it is greater than zero then the channel +names in the test are numbered as follows.

    + +

    <PV name>000000, <PV name>000001, ... <PV name>nnnnnn

    + +

    casw

    +
    casw [-i <interest level>]
    + +

    Description

    + +

    CA server "beacon anomaly" logging.

    + +

    CA server beacon anomalies occur when a new server joins the network, a +server is rebooted, network connectivity to a server is reestablished, or if a +server's CPU exits a CPU load saturated state.

    + +

    CA clients with unresolved channels reset their search request scheduling +timers whenever they see a beacon anomaly.

    + +

    This program can be used to detect situations where there are too many +beacon anomalies. IP routing configuration problems may result in false beacon +anomalies that might cause CA clients to use unnecessary additional network +bandwidth and server CPU load when searching for unresolved channels.

    + +

    If there are no new CA servers appearing on the network, and network +connectivity remains constant, then casw should print no messages at all. At +higher interest levels the program prints a message for every beacon that is +received, and anomalous entries are flagged with a star.

    + +

    caEventRate

    +
    caEventRate <PV name> [subscription count]
    + +

    Description

    + +

    Connect to the specified PV, subscribe for monitor updates the specified +number of times (default once), and periodically log the current sampled event +rate, average event rate, and the standard deviation of the event rate in Hertz +to standard out.

    + +

    ca_test

    +
    ca_test <PV name> [value to be written]
    + +

    Description

    + +

    If a value is specified it is written to the PV. Next, the current value of +the PV is converted to each of the many external data type that can be +specified at the CA client library interface, and each of these is formated and +then output to the console.

    +
    + +

    Command Line Tools

    + +

    caget

    +
    caget [options] <PV name> ...
    + +

    Description

    + +

    Get and print value for PV(s).

    + +

    The values for one or multiple PVs are read and printed to stdout. The +DBR_... format in which the data is read, the output format, and a number of +details of how integer and float values are represented can be controlled using +command line options.

    + +

    When getting multiple PVs, their order on the command line is retained in +the output.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OptionDescription
    -hPrint usage information
    CA options:
    -w <sec>Wait time, specifies longer CA timeout, default is 1.0 second
    -cAsynchronous get (use ca_get_callback instead of ca_get)
    -p <prio>CA priority (0-99, default 0=lowest)
    Format and data type options:
    Default output format is "name value"
    -tTerse mode - print only value, without name
    -aWide mode "name timestamp value stat sevr" (read PVs as + DBR_TIME_xxx)
    -nPrint DBF_ENUM values as number (default are enum strings)
    -d <type>Request specific dbr type; use string (DBR_ prefix may be omitted) + +

    or number of one of the following types:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DBR_STRING0DBR_STS_FLOAT9DBR_TIME_LONG19DBR_CTRL_SHORT29
    DBR_INT1DBR_STS_ENUM10DBR_TIME_DOUBLE20DBR_CTRL_INT29
    DBR_SHORT1DBR_STS_CHAR11DBR_GR_STRING21DBR_CTRL_FLOAT30
    DBR_FLOAT2DBR_STS_LONG12DBR_GR_SHORT22DBR_CTRL_ENUM31
    DBR_ENUM3DBR_STS_DOUBLE13DBR_GR_INT22DBR_CTRL_CHAR32
    DBR_CHAR4DBR_TIME_STRING14DBR_GR_FLOAT23DBR_CTRL_LONG33
    DBR_LONG5DBR_TIME_INT15DBR_GR_ENUM24DBR_CTRL_DOUBLE34
    DBR_DOUBLE6DBR_TIME_SHORT15DBR_GR_CHAR25DBR_STSACK_STRING37
    DBR_STS_STRING7DBR_TIME_FLOAT16DBR_GR_LONG26DBR_CLASS_NAME38
    DBR_STS_SHORT8DBR_TIME_ENUM17DBR_GR_DOUBLE27
    DBR_STS_INT8DBR_TIME_CHAR18DBR_CTRL_STRING28
    +
    Arrays:
    Value format: Print number of requested values, then list of + values
    Default:Print all values
    -# <count>Print first <count> elements of an array
    -SPrint array of char as a string (long string)
    Floating point type format:
    Default:Use %g format
    -e <nr>Use %e format, with <nr> digits after the decimal point
    -f <nr>Use %f format, with <nr> digits after the decimal point
    -g <nr>Use %g format, with <nr> digits after the decimal point
    -sGet value as string (honors server-side precision)
    -lxRound to long integer and print as hex number
    -loRound to long integer and print as octal number
    -lbRound to long integer and print as binary number
    Integer number format:
    Default:Print as decimal number
    -0xPrint as hex number
    -0oPrint as octal number
    -0bPrint as binary number
    + +

    camonitor

    +
    camonitor [options] <PV name> ...
    + +

    Description

    + +

    Subscribe to and print value updates for PV(s).

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OptionDescription
    -hPrint usage information
    CA options:
    -w <sec>Wait time, specifies longer CA timeout, default is 1.0 second
    -m <mask>Specify CA event mask to use, with <mask> being any combination + of 'v' (value), 'a' (alarm), 'l' (log), 'p' (property). Default: va
    -p <prio>CA priority (0-99, default 0=lowest)
    Timestamps:
    Default:Print absolute timestamps (as reported by CA server)
    -t <key>Specify timestamp source(s) and type, with <key> containing
    + 's' = CA server (remote) timestamps
    + 'c' = CA client (local) timestamps (shown in '()'s)
    + 'n' = no timestamps
    + 'r' = relative timestamps (time elapsed since start of program)
    + 'i' = incremental timestamps (time elapsed since last update)
    + 'I' = incremental timestamps (time elapsed since last update, by + channel)
    Enum Format:
    -nPrint DBF_ENUM values as number (default are enum strings)
    Arrays:
    Value format: Print number of requested values, then list of + values
    Default:Print all values
    -# <count>Print first <count> elements of an array
    -SPrint array of char as a string (long string)
    Floating point type format:
    Default:Use %g format
    -e <nr>Use %e format, with <nr> digits after the decimal point
    -f <nr>Use %f format, with <nr> digits after the decimal point
    -g <nr>Use %g format, with <nr> digits after the decimal point
    -sGet value as string (honors server-side precision)
    -lxRound to long integer and print as hex number
    -loRound to long integer and print as octal number
    -lbRound to long integer and print as binary number
    Integer number format:
    Default:Print as decimal number
    -0xPrint as hex number
    -0oPrint as octal number
    -0bPrint as binary number
    + +

    caput

    +
    caput [options] <PV name> <value>
    +caput -a [options] <PV name> <no of elements> <value> ...
    + +

    Description

    + +

    Put value to a PV.

    + +

    The specified value is written to the PV (as a string). The PV value is read +before and after the write operation and printed as "Old" and "new" values on +stdout.

    + +

    The array variant writes an array to the specified PV. The first numeric +argument specifying the number of array elements is kept for compatibility with +the array data format of caget - the actual number of values specified on the +command line is used.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OptionDescription
    -hPrint usage information
    CA options:
    -w <sec>Wait time, specifies longer CA timeout, default is 1.0 second
    -cAsynchronous put (use ca_put_callback and wait for completion)
    -p <prio>CA priority (0-99, default 0=lowest)
    Format options:
    -tTerse mode - print only sucessfully written value, without name
    Enum Format:
    Auto - try value as ENUM string, then as index number
    -nForce interpretation of values as numbers
    -sForce interpretation of values as strings
    Arrays:
    -aPut array data
    Value format: Print number of requested values, then list of + values
    -SPut string as an array of char (long string)
    + +

    cainfo

    +
    cainfo [options] <PV name> ...
    + +

    Description

    + +

    Get and print channel and connection information for PV(s).

    + +

    All available Channel Access related information about PV(s) is printed to +stdout.

    + +

    The -s option allows to specify an interest level for calling Channel +Access' internal report function ca_client_status(), that prints lots of +internal informations on stdout, including environment settings, used CA ports +etc.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OptionDescription
    -hPrint usage information
    CA options:
    -w <sec>Wait time, specifies longer CA timeout, default is 1.0 second
    -s <level>Call ca_client_status with the specified interest level
    -p <prio>CA priority (0-99, default 0=lowest)
    + +

    excas

    + +

    excas [options]

    + +

    This is an example CA server that is sometimes used for testing purposes. An +example server can be created with the makeBaseApp perl script, as descibed in +the application Developer's Guide.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    OptionDescription
    -d <uuuu>set level uuuu for debug messages, where uuuu is an positive integer + number
    -p <aaaa>prefix all of the PV names below with aaaa changing, for example, the + name of "bill" to "xyz:bill"
    -t <n.n>set execution time where n.n is a positive real number
    -c <uuuu>set the numbered alias count
    -s <nnn>the default, nnn is one, enables periodic scanning of the PV + replacing the PV with its value added with a small random change, when + nnn is zero it turns off this type of periodic scanning
    -ss <nnn>the default, nnn is one, enables synchronous scanning, and if nnn is + zero it turns on asynchronous scanning
    -ad <n.n>set the delay before asynchronous operations complete (defaults to + 0.1 seconds)
    -an <nnn>set the maximum number of simultaneous asynchronous operations + (defaults to 1000)
    + +

    The example server has a compile time fixed set of example variables.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Process Variable NameNumber of ElementsIO TypeData TypeHigh LimitLow LimitScan Period
    jane1Synchronousfloat point, 64 bits10.00.00.1 Seconds, random noise changes
    fred1Synchronousfloat point, 64 bits10.0-10.02.0 Seconds, random noise changes
    janet1Asynchronousfloat point, 64 bits10.00.00.1 Seconds, random noise changes
    freddy1Asynchronousfloat point, 64 bits10.0-10.02.0 Seconds, random noise changes
    alan100Synchronousfloat point, 64 bits10.0-10.02.0 Seconds, random noise changes
    albert1000Synchronousfloat point, 64 bits10.0-10.020.0 Seconds, random noise changes
    boot1Synchronousenumerated, 16 bits10.0-10.0changed only by client
    booty1Asynchronousenumerated, 16 bits10.0-10.01.0 Seconds, random noise changes
    bill1Synchronousfloat point, 64 bits10.0-10.0changed only by client
    billy1Asynchronousfloat point, 64 bits10.0-10.0changed only by client
    bloaty100000Synchronousfloat point, 64 bits10.0-10.0changed only by client
    + +

    Bugs

    + +

    Not all of the options listed above have been tested recently.

    +
    + +

    Troubleshooting

    + +

    When Clients Do Not Connect to Their Server

    + +

    Client and Server Broadcast Addresses Dont +Match

    + +

    Verify that the broadcast addresses are identical on the server's host and +on the client's host. This can be checked on UNIX with "netstat -i" or +"ifconfig -a"; on vxWorks with ifShow; and on windows with ipconfig. It is +normal for the broadcast addresses to not be identical if the client and server +are not directly attached to the same IP subnet, and in this situation the +EPICS_CA_ADDR_LIST must be set. Otherwise, if the client and server are +intended to be on the same IP subnet, then the problem may be that the IP +netmask is incorrectly set in the network interface configuration. On most +operating systems, when the host's IP address is configured, the host's IP +subnet mask is also configured.

    + +

    Client Isn't Configured to Use the Server's Port

    + +

    Verify that the client and server are using the same UDP port. Check the +server's port by running "netstat -a | grep nnn" where nnn is the port number +configured in the client. If you do not set EPICS_CA_SERVER_PORT or +EPICS_CAS_SERVER_PORT then the default port will be 5064.

    + +

    Unicast Addresses in the EPICS_CA_ADDR_LIST Does not +Reliably Contact Servers Sharing the Same UDP Port on the Same Host

    + +

    Two servers can run on the same host with the same server port number, but +there are restrictions. If the host has a modern IP kernel it is possible to +have two or more servers share the same UDP port. It is not possible for these +servers to run on the same host using the same TCP port. If the CA server +library detects that a server is attempting to start on the same port as an +existing CA server then both servers will use the same UDP port, and the 2nd +server will be allocated an ephemeral TCP port. Clients can be configured to +use the same port number for both servers. They will locate the 2nd server via +the shared UDP port, and transparently connect to the 2nd server's ephemeral +TCP port. Be aware however that If there are two server's running on the same +host sharing the same UDP port then they will both receive UDP search requests +sent as broadcasts, but unfortunately (due to a weakness of most IP kernel +implementations) only one of the servers will typically receive UDP search +requests sent to unicast addresses (i.e. a single specific host's ip +address).

    + +

    Client Does not See Server's Beacons

    + +

    Two conclusions deserve special emphasis. First, if a client does not +see the server's beacons, then it will use additional network and server +resources sending periodic state-of-health messages. Second, if a +client does not see a newly introduced server's beacon, then it will take up to +EPICS_CA_MAX_SEARCH_PERIOD to find that newly introduced server. Also, +starting with EPICS R3.14.7 the client library does not suspend +searching for a channel after 100 unsuccessful attempts until a beacon anomaly +is seen. Therefore, if the client library is from before version R3.14.7 of +EPICS and it timed out attempting to find a server whoose beacon cant be seen +by the client library then the client application might need to be restarted in +order to connect to this new beacon-out-of-range server. The typical situation +where a client would not see the server's beacon might be when the client isnt +on the same IP subnet as the server, and the client's EPICS_CA_ADDR_LIST was +modified to include a destination address for the server, but the server's +beacon address list was not modified so that it's beacons are received by the +client.

    + +

    A Server's IP Address Was Changed

    + +

    When communication over a virtual circuit times out, then each channel +attached to the circuit enters a disconnected state and the disconnect callback +handler specified for the channel is called. However, the circuit is not +disconnected until TCP/IP's internal, typically long duration, keep alive timer +expires. The disconnected channels remain attached to the beleaguered circuit +and no attempt is made to search for, or to reestablish, a new circuit. If, at +some time in the future, the circuit becomes responsive again, then the +attached channels enter a connected state again and reconnect call back +handlers are called. Any monitor subscriptions that received an update message +while the channel was disconnected are also refreshed. If at any time the +library receives an indication from the operating system that a beleaguered +circuit has shutdown or was disconnected then the library will immediately +reattempt to find servers for each channel and connect circuits to them.

    + +

    A well known negative side effect of the above behavior is that CA clients +will wait the full (typically long) duration of TCP/IP's internal keep alive +timer prior to reconnecting under the following scenario (all of the following +occur):

    +
      +
    • An server's (IOC's) operating system crashes (or is abruptly turned off) + or a vxWorks system is stopped by any means
    • +
    • This operating system does not immediately reboot using the same IP + address
    • +
    • A duplicate of the server (IOC) is started appearing at a different IP + address
    • +
    + +

    It is unlikely that any rational organization will advocate the above +scenario in a production system. Nevertheless, there are opportunities +for users to become confused during control system development, but it +is felt that the robustness improvements justify isolated confusion during the +system integration and checkout activities where the above scenarios are most +likely to occur.

    + +

    Contrast the above behavior with the CA client library behavior of releases +prior to R3.14.5 where the beleaguered circuit was immediately closed when +communication over it timed out. Any attached channels were immediately +searched for, and after successful search responses arrived then attempts were +made to build a new circuit. This behavior could result in undesirable resource +consumption resulting from periodic circuit setup and teardown overhead +(thrashing) during periods of CPU / network / IP kernel buffer congestion.

    + +

    Put Requests Just Prior to Process +Termination Appear to be Ignored

    + +

    Short lived CA client applications that issue a CA put request and then +immediately exit the process (return from main or call +exit) may find that there request isn't executed. To guarantee +that the request is sent call ca_flush followed by +ca_context_destroy prior to terminating the process.

    + +

    ENOBUFS Messages

    + +

    Many Berkley UNIX derived Internet Protocol (IP) kernels use a memory +management scheme with a fixed sized low level memory allocation quantum called +an "mbuf". Messages about "ENOBUFS" are an indication that your IP kernel is +running low on mbuf buffers. An IP kernel mbuf starvation situation may lead to +temporary IP communications stalls or reduced throughput. This issue has to +date been primarily associated with vxWorks systems where mbuf starvation on +earlier vxWorks versions is rumored to lead to permanent IP communications +stalls which are resolved only by a system reboot. IP kernels that use mbufs +frequently allow the initial and maximum number of mbufs to be configured. +Consult your OS's documentation for configuration procedures which vary between +OS and even between different versions of the same OS.

    + +

    Contributing Circumstances

    +
      +
    • The total number of connected clients is high. Each active socket + requires dedicated mbufs for protocol control blocks, and for any data that + might be pending in the operating system for transmission to Channel Access + or to the network at a given instant. If you increase the vxWorks limit on + the maximum number of file descriptors then it may also be necessary to + increase the size of the mbuf pool.
    • +
    +
      +
    • The server has multiple connections where the server's sustained event + (monitor subscription update) production rate is higher than the client's + or the network's sustained event consumption rate. This ties up a per + socket quota of mbufs for data that are pending transmission to the client + via the network. In particular, if there are multiple clients that + subscribe for monitor events but do not call ca_pend_event() or ca_poll() + to process their CA input queue, then a significant mbuf consuming backlog + can occur in the server.
    • +
    +
      +
    • The server does not get a chance to run (because some other higher + priority thread is running) and the CA clients are sending a high volume of + data over TCP or UDP. This ties up a quota of mbufs for each socket in the + server that isn't being reduced by the server's socket read system + calls.
    • +
    +
      +
    • The server has multiple stale connections. Stale connections occur when a + client is abruptly turned off or disconnected from the network, and an + internal "keepalive" timer has not yet expired for the virtual circuit in + the operating system, and therefore mbufs may be dedicated to unused + virtual circuits. This situation is made worse if there are active monitor + subscriptions associated with stale connections which will rapidly increase + the number of dedicated mbufs to the quota available for each circuit.
    • +
    • When sites switch to the vxWorks 5.4 IP kernel they frequently run into + network pool exhaustion problems. This may be because the original vxWorks + IP kernel expanded the network pool as needed at runtime while the new + kernel's pool is statically configured at compile time, and does + not expand as needed at runtime. Also, at certain sites problems + related to vxWorks network driver pool exhaustion have also been reported + (this can also result in ENOBUF diagnostic messages).
    • +
    + +

    Related Diagnostics

    +
      +
    • The EPICS command "casr [interest level]" displays information about the + CA server and how many clients are connected.
    • +
    • The vxWorks command "inetstatShow" indicates how many bytes are pending + in mbufs and indirectly (based on the number of circuits listed) how many + mbuf based protocol control blocks have been consumed. The vxWorks commands + (availability depending on vxWorks version) mbufShow, netStackSysPoolShow, + and netStackDataPoolShow indicate how much space remains in the network + stack pool.
    • +
    • The RTEMS command "netstat [interest level]" displays network information + including mbuf consumption statistics.
    • +
    + +

    Server Subscription Update Queuing

    + +

    If the subscription update producer in the server produces subscription +updates faster than the subscription update consumer in the client consumes +them, then events have to be discarded if the buffering in the server +isn't allowed to grow to an infinite size. This is a law of nature +- based on queuing theory of course.

    + +

    What is done depends on the version of the CA server. All server versions +place quotas on the maximum number of subscription updates allowed on the +subscription update queue at any given time. If this limit is reached, an +intervening update is discarded in favor of a more recent update. Depending on +the version of the server, rapidly updating subscriptions are or are not +allowed to cannibalize the quotas of slow updating subscriptions in limited +ways. Nevertheless, there is always room on the queue for at least one update +for each subscription. This guarantees that the most recent update is always +sent.

    + +

    Adding further complication, the CA client library also implements a +primitive type of flow control. If the client library sees that it is reading a +large number of messages one after another w/o intervening delay it knows that +it is not consuming events as fast as they are produced. In that situation it +sends a message telling the server to temporarily stop sending subscription +update messages. When the client catches up it sends another message asking the +server to resume with subscription updates. This prevents slow clients from +getting time warped, but also guarantees that intervening events are discarded +until the slow client catches up.

    + +

    There is currently no message on the IOC's console when a +particular client is slow on the uptake. A message of this type used to exist +many years ago, but it was a source of confusion (and what we will call +message noise) so it was removed.

    + +

    There is unfortunately no field in the protocol allowing the server to +indicate that an intervening subscription update was discarded. We should +probably add that capability in a future version. Such a feature would, for +example, be beneficial when tuning an archiver installation.

    +
    + +

    Function Call Interface General Guidelines

    + +

    Flushing and Blocking

    + +

    Significant performance gains can be realized when the CA client library +doesn't wait for a response to return from the server after each request. All +requests which require interaction with a CA server are accumulated (buffered) +and not forwarded to the IOC until one of ca_flush_io, ca_pend_io, +ca_pend_event, or ca_sg_pend are called allowing several operations to be +efficiently sent over the network together. Any process variable values written +into your program's variables by ca_get() should not be referenced by your +program until ECA_NORMAL has been received from ca_pend_io().

    + +

    Status Codes

    + +

    If successful, the routines described here return the status code +ECA_NORMAL. Unsuccessful status codes returned from the client library are +listed with each routine in this manual. Operations that appear to be valid to +the client can still fail in the server. Writing the string "off" to a floating +point field is an example of this type of error. If the server for a channel is +located in a different address space than the client then the ca_xxx() +operations that communicate with the server return status indicating the +validity of the request and whether it was successfully enqueued to the server, +but communication of completion status is deferred until a user callback is +called, or lacking that an exception handler is called. An error number and the +error's severity are embedded in CA status (error) constants. Applications +shouldn't test the success of a CA function call by checking to see if the +returned value is zero as is the UNIX convention. Below are several methods to +test CA function returns. See ca_signal() and SEVCHK +for more information on this topic.

    +
    status = ca_XXXX(); 
    +SEVCHK( status, "ca_XXXX() returned failure status"); 
    +
    +if ( status & CA_M_SUCCESS ) { 
    +        printf ( "The requested ca_XXXX() operation didn't complete successfully"); 
    +} 
    +
    +if ( status != ECA_NORMAL ) { 
    +        printf("The requested ca_XXXX() operation didn't complete successfully because \"%s\"\n",
    +                ca_message ( status ) ); 
    +}
    + +

    Channel Access Data Types

    + +

    CA channels form a virtual circuit between a process variable (PV) and a +client side application program. It is possible to connect a wide variety of +data sources into EPICS using the CA server library. When a CA channel +communicates with an EPICS Input Output Controller (IOC) then a field is a +specialization of a PV, and an EPICS record is a plug compatible function block +that contains fields, and the meta data below frequently are mapped onto +specific fields within the EPICS records by the EPICS record support (see the +EPICS Application Developer Guide).

    + +

    Arguments of type chtype specifying the data type you wish to transfer. They +expect one of the set of DBR_XXXX data type codes defined in db_access.h. There +are data types for all of the C primitive types, and there are also compound (C +structure) types that include various process variable properties such as +units, limits, time stamp, or alarm status. The primitive C types follow a +naming convention where the C typedef dbr_xxxx_t corresponds to the DBR_XXXX +data type code. The compound (C structure) types follow a naming convention +where the C structure tag dbr_xxxx corresponds to the DBR_XXXX data type code. +The following tables provides more details on the structure of the CA data type +space. Since data addresses are passed to the CA client library as typeless +"void *" pointers then care should be taken to ensure that you have passed the +correct C data type corresponding to the DBR_XXXX type that you have specified. +Architecture independent types are provided in db_access.h to assist +programmers in writing portable code. For example "dbr_short_t" should be used +to send or receive type DBR_SHORT. Be aware that type name DBR_INT has been +deprecated in favor of the less confusing type name DBR_SHORT. In practice, +both the DBR_INT type code and the DBR_SHORT type code refer to a 16 bit +integer type, and are functionally equivalent.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Channel Access Primitive Data Types
    CA Type CodePrimitive C Data TypeData Size
    DBR_CHARdbr_char_t8 bit character
    DBR_SHORTdbr_short_t16 bit integer
    DBR_ENUMdbr_enum_t16 bit unsigned integer
    DBR_LONGdbr_long_t32 bit signed integer
    DBR_FLOATdbr_float_t32 bit IEEE floating point
    DBR_DOUBLEdbr_double_t64 bit IEEE floating point
    DBR_STRINGdbr_string_t40 character string
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Structure of the Channel Access Data Type Space
    CA Type CodeRead / WritePrimitive C Data TypeProcess Variable Properties
    DBR_<PRIMITIVE TYPE>RWdbr_<primitive type>_tvalue
    DBR_STS_<PRIMITIVE TYPE>Rstruct dbr_sts_<primitive type>value, alarm status, and alarm severity
    DBR_TIME_<PRIMITIVE TYPE>Rstruct dbr_time_<primitive type>value, alarm status, alarm severity, and time stamp
    DBR_GR_<PRIMITIVE TYPE>Rstruct dbr_gr_<primitive type>value, alarm status, alarm severity, units, display precision, and + graphic limits
    DBR_CTRL_<PRIMITIVE TYPE>Rstruct dbr_ctrl_<primitive type>value, alarm status, alarm severity, units, display precision, + graphic limits, and control limits
    DBR_PUT_ACKTWdbr_put_ackt_tUsed for global alarm acknowledgement. Do transient alarms have to be + acknowledged? (0,1) means (no, yes).
    DBR_PUT_ACKSWdbr_put_acks_tUsed for global alarm acknowledgement. The highest alarm severity to + acknowledge. If the current alarm severity is less then or equal to + this value the alarm is acknowledged.
    DBR_STSACK_STRINGRstruct dbr_stsack_stringvalue, alarm status, alarm severity, ackt, acks
    DBR_CLASS_NAMERdbr_class_name_tname of enclosing interface (name of the record if channel is + attached to EPICS run time database)
    + +

    + +

    Channel value arrays can also be included within the structured CA data +types. If more than one element is requested, then the individual elements can +be accessed in an application program by indexing a pointer to the value field +in the DBR_XXX structure. For example, the following code computes the sum of +the elements in a array process variable and prints its time stamp. The dbr_size_n function can be used to determine the correct +number of bytes to reserve when there are more than one value field elements in +a structured CA data type.

    +
    #include <stdio.h>
    +#include <stdlib.h>
    +
    +#include "cadef.h"
    +
    +int main ( int argc, char ** argv )
    +{
    +    struct dbr_time_double * pTD;
    +    const dbr_double_t * pValue;
    +    unsigned nBytes;
    +    unsigned elementCount;
    +    char timeString[32];
    +    unsigned i;    
    +    chid chan;
    +    double sum;
    +    int status;
    +
    +    if ( argc != 2 ) {
    +        fprintf ( stderr, "usage: %s <channel name>", argv[0] );
    +        return -1;
    +    }
    +
    +    status = ca_create_channel ( argv[1], 0, 0, 0, & chan );
    +    SEVCHK ( status, "ca_create_channel()" );
    +    status = ca_pend_io ( 15.0 );
    +    if ( status != ECA_NORMAL ) {
    +        fprintf ( stderr, "\"%s\" not found.\n", argv[1] );
    +        return -1;
    +    }
    +
    +    elementCount = ca_element_count ( chan );
    +    nBytes = dbr_size_n ( DBR_TIME_DOUBLE, elementCount );
    +    pTD = ( struct dbr_time_double * ) malloc ( nBytes );
    +    if ( ! pTD ) {
    +        fprintf ( stderr, "insufficient memory to complete request\n" );
    +        return -1;
    +    }
    +
    +    status = ca_array_get ( DBR_TIME_DOUBLE, elementCount, chan, pTD );
    +    SEVCHK ( status, "ca_array_get()" );
    +    status = ca_pend_io ( 15.0 );
    +    if ( status != ECA_NORMAL ) {
    +        fprintf ( stderr, "\"%s\" didnt return a value.\n", argv[1] );
    +        return -1;
    +    }
    +
    +    pValue = & pTD->value;
    +    sum = 0.0;
    +    for ( i = 0; i < elementCount; i++ ) {
    +        sum += pValue[i];
    +    }
    +
    +    epicsTimeToStrftime ( timeString, sizeof ( timeString ),
    +        "%a %b %d %Y %H:%M:%S.%f", & pTD->stamp );
    +
    +    printf ( "The sum of elements in %s at %s was %f\n", 
    +        argv[1], timeString, sum );
    +
    +    ca_clear_channel ( chan );
    +    ca_task_exit ();
    +    free ( pTD );
    +
    +    return 0;
    +}
    + +

    User Supplied Callback Functions

    + +

    Certain CA client initiated requests asynchronously execute an application +supplied call back in the client process when a response arrives. The functions +ca_put_callback, ca_get_callback, and ca_add_event all request notification of +asynchronous completion via this mechanism. The event_handler_args +structure is passed by value to the application supplied +callback. In this structure the dbr field is a void pointer to any +data that might be returned. The status field will be +set to one of the CA error codes in caerr.h and will indicate the status of the +operation performed in the IOC. If the status field isn't set to ECA_NORMAL or +data isn't normally returned from the operation (i.e. put call back) then you +should expect that the dbr field will be set to a nill pointer +(zero). The fields usr, chid, and type +are set to the values specified when the request was made by the application. +The "dbr" pointer, and any data that it points to, are valid only when +executing within the user's callback function.

    +
    typedef struct event_handler_args {
    +    void            *usr;   /* user argument supplied with request */
    +    chanId          chid;   /* channel id */
    +    long            type;   /* the type of the item returned */ 
    +    long            count;  /* the element count of the item returned */
    +    const void      *dbr;   /* a pointer to the item returned */
    +    int             status; /* ECA_XXX status of the requested op from the server */
    +} evargs;
    +
    +void myCallback ( struct event_handler_args args )
    +{
    +    if ( args.status != ECA_NORMAL ) {
    +    }
    +    if ( args.type == DBR_TIME_DOUBLE ) {
    +         const struct dbr_time_double * pTD =
    +              ( const struct dbr_time_double * ) args.dbr;
    +    }
    +}
    + +

    Channel Access Exceptions

    + +

    When the server detects a failure, and there is no client call back function +attached to the request, an exception handler is executed in the client. +The default exception handler prints a message on the console and exits if the +exception condition is severe. Certain internal exceptions within the CA client +library, and failures detected by the SEVCHK macro may also cause the exception +handler to be invoked. To modify this behavior see ca_add_exception_event().

    + +

    Server and Client Share the Same Address Space on The Same +Host

    + +

    If the Process Variable's server and it's client are colocated within the +same memory address space and the same host then the ca_xxx() operations bypass +the server and directly interact with the server tool component (commonly the +IOC's function block database). In this situation the ca_xxx() routines +frequently return the completion status of the requested operation directly to +the caller with no opportunity for asynchronous notification of failure via an +exception handler. Likewise, callbacks may be directly invoked by the CA +library functions that request them.

    + +

    Arrays

    + +

    For routines that require an argument specifying the number of array +elements, no more than the process variable's maximum native element count may +be requested. The process variable's maximum native element count is available +from ca_element_count() when the channel is connected. If fewer elements than +the process variable's native element count are requested, the requested values +will be fetched beginning at element zero. By default CA limits the number of +elements in an array to be no more than approximately 16k divided by the size +of one element in the array. Starting with EPICS R3.14 the maximum array size +may be configured in the client and in the server.

    + +

    Connection Management

    + +

    Application programs should assume that CA servers may be restarted, and +that network connectivity is transient. When you create a CA channel its +initial connection state will most commonly be disconnected. If the Process +Variable's server is available the library will immediately initiate the +necessary actions to make a connection with it. Otherwise, the client library +will monitor the state of servers on the network and connect or reconnect with +the process variable's server as it becomes available. After the channel +connects the application program can freely perform IO operations through the +channel, but should expect that the channel might disconnect at any time due to +network connectivity disruptions or server restarts.

    + +

    Three methods can be used to determine if a channel is connected: the +application program might call ca_state to +obtain the current connection state, block in ca_pend_io until the channel connects, or install +a connection callback handler when it calls ca_create_channel. The ca_pend_io approach is best suited to simple +command line programs with short runtime duration, and the connection callback +method is best suited to toolkit components with long runtime duration. Use of +ca_state is appropriate only in programs +that prefer to poll for connection state changes instead of opting for +asynchronous notification. The ca_pend_io function blocks only for +channels created specifying a nill connection handler callback function. The +user's connection state change function will be run immediately from within +ca_create_channel if the CA +client and CA server are both hosted within the same address space (within the +same process).

    + +

    Thread Safety and Preemptive Callback to User Code

    + +

    Starting with EPICS R3.14 the CA client libraries are fully thread safe on +all OS (in past releases the library was thread safe only on vxWorks). When the +client library is initialized the programmer may specify if preemptive callback +is to be enabled. Preemptive callback is disabled by default. If preemptive +callback is enabled, then the user's callback functions might be called by +CA's auxiliary threads when the main initiating channel access thread is not +inside of a function in the channel access client library. Otherwise, the +user's callback functions will be called only when the main initiating channel +access thread is executing inside of the CA client library. When the CA client +library invokes a user's callback function, it will always wait for the current +callback to complete prior to executing another callback function. Programmers +enabling preemptive callback should be familiar with using mutex locks to +create a reliable multi-threaded program.

    + +

    To set up a traditional single threaded client, you will need code like this +(see ca_context_create and CA Client Contexts and Application Specific Auxiliary +Threads) .

    + +

    SEVCHK ( ca_context_create(ca_disable_preemptive_callback ), +"application pdq calling ca_context_create" );

    + +

    To set up a preemptive callback enabled CA client context you will need code +like this (see ca_context_create and CA Client Contexts and Application Specific Auxiliary +Threads).

    + +

    SEVCHK ( ca_context_create(ca_enable_preemptive_callback ), +"application pdq calling ca_context_create" );

    + +

    CA Client Contexts and Application Specific Auxiliary +Threads

    + +

    It is often necessary for several CA client side tools running in the same +address space (process) to be independent of each other. For example, the +database CA links and the sequencer are designed to not use the same CA client +library threads, network circuits, and data structures. Each thread that calls +ca_context_create() for the first time either +directly or implicitly when calling any CA library function for the first +time, creates a CA client library context. A CA client library context contains +all of the threads, network circuits, and data structures required to connect +and communicate with the channels that a CA client application has created. The +priority of auxiliary threads spawned by the CA client library are at fixed +offsets from the priority of the thread that called ca_context_create(). An application specific +auxiliary thread can join a CA context by calling ca_attach_context() using the CA context +identifier that was returned from ca_current_context() when it is called by the +thread that created the context which needs to be joined. A context which is to +be joined must be preemptive - it must be created using ca_context_create(ca_enable_preemptive_callback). +It is not possible to attach a thread to a non-preemptive CA context created +explicitly or implicitly with +ca_create_context(ca_disable_preemptive_callback). Once a thread has joined +with a CA context it need only make ordinary ca_xxxx() library calls to use the +context.

    + + +

    A CA client library context can be shut down and cleaned up, after +destroying any channels or application specific threads that are attached to +it, by calling ca_context_destroy(). The +context may be created and destroyed by different threads as long as they are +both part of the same context.

    + +

    Polling the CA Client Library From Single Threaded +Applications

    + +

    If preemptive call back is not enabled, then for proper operation CA must +periodically be polled to take care of background activity. This requires that +your application must either wait in one of ca_pend_event(), ca_pend_io(), or +ca_sg_block() or alternatively it must call ca_poll() at least every 100 +milli-seconds. In single threaded applications a file descriptor manager like +Xt or the interface described in fdManager.h can be used to monitor both mouse +clicks and also CA's file descriptors so that ca_poll() can be called +immediately when CA server messages arrives over the network.

    + +

    Avoid Emulating Bad Practices that May Still be +Common

    + +

    With the embryonic releases of EPICS it was a common practice to examine a +channel's connection state, its native type, and its native element count by +directly accessing fields in a structure using a pointer stored in type +chid. Likewise, a user private pointer in the per channel +structure was also commonly set by directly accessing fields in the channel +structure. A number of difficulties arise from this practice, which has long +since been deprecated. For example, prior to release 3.13 it was recognized +that transient changes in certain private fields in the per channel structure +would make it difficult to reliably test the channels connection state using +these private fields directly. Therefore, in release 3.13 the names of certain +fields were changed to discourage this practice. Starting with release 3.14 +codes written this way will not compile. Codes intending to maintain the +highest degree of portability over a wide range of EPICS versions should be +especially careful. For example you should replace all instances off +channel_id->count with +ca_element_count(channel_id). This approach should be reliable on +all versions of EPICS in use today. The construct ca_puser(chid) = +xxxx is particularly problematic. The best mechanisms for setting the +per channel private pointer will be to pass the user private pointer in when +creating the channel. This approach is implemented on all versions. Otherwise, +you can also use ca_set_puser(CHID,PUSER), but this function is +available only after the first official (post beta) release of EPICS 3.13.

    + +

    Calling CA Functions from the vxWorks Shell +Thread

    + +

    Calling CA functions from the vxWorks shell thread is a somewhat +questionable practice for the following reasons.

    +
      +
    • The vxWorks shell thread runs at the very highest priority in the system + and therefore socket calls are made at a priority that is above the + priority of tNetTask - a practice that has caused the WRS IP kernel + to get sick in the past. That symptom was observed some time ago, but we + don't know if WRS has fixed the problem.
    • +
    +
      +
    • The vxWorks shell thread runs at the very highest priority in the system + and therefore certain CA auxiliary threads will not get the priorities that + are requested for them. This might cause problems only when in a CPU + saturation situations.
    • +
    +
      +
    • If the code does not call ca_context_destroy (ca_task_exit in past + releases) then resources are left dangling.
    • +
    +
      +
    • In EPICS R3.13 the CA client library installed vxWorks task exit handlers + behaved strangely if CA functions were called from the vxWorks shell, + ca_task_exit() wasn't called, and the vxWorks shell restarted. In + EPICS R3.14 vxWorks task exit handlers are not installed and therefore + cleanup is solely the responsibility of the user. With EPICS R3.14 the user + must call ca_context_destroy or ca_task_exit to clean up on vxWorks. This + is the same behavior as on all other OS.
    • +
    + +

    Calling CA Functions from POSIX signal +handlers

    + +

    As you might expect, it isnt safe to call the CA client library from a POSIX +signal handler. Likewise, it isnt safe to call the CA client library from +interrupt context.

    +
    + +

    Function Call Reference

    + +

    ca_context_create()

    +
    #include <cadef.h>
    +enum ca_preemptive_callback_select
    +    { ca_disable_preemptive_callback, ca_enable_preemptive_callback };
    +int ca_context_create ( enum ca_preemptive_callback_select SELECT );
    + +

    Description

    + +

    This function, or ca_attach_context(), +should be called once from each thread prior to making any of the other Channel +Access calls. If one of the above is not called before making other CA calls +then a non-preemptive context is created by default, and future attempts to +create a preemptive context for the current threads will fail.

    + +

    If ca_disable_preemptive_callback is specified then additional +threads are not allowed to join the CA context using +ca_context_attach() because allowing other threads to join implies that CA +callbacks will be called preemptively from more than one thread.

    + +

    Arguments

    +
    +
    SELECT
    +
    This argument specifies if preemptive invocation of callback functions + is allowed. If so your callback functions might be called when the thread + that calls this routine is not executing in the CA client library. There + are two implications to consider.
    +

    First, if preemptive callback mode is enabled the developer must + provide mutual exclusion protection for his data structures. In this mode + it's possible for two threads to touch the application's data structures + at once: this might be the initializing thread (the thread that called + ca_context_create) and also a private thread created by the CA client + library for the purpose of receiving network messages and calling + callbacks. It might be prudent for developers who are unfamiliar with + mutual exclusion locking in a multi-threaded environment to specify + ca_disable_preemptive_callback.

    +

    Second, if preemptive callback mode is enabled the application is no + longer burdened with the necessity of periodically polling the CA client + library in order that it might take care of its background activities. If + ca_enable_preemptive_callback is specified then CA client + background activities, such as connection management, will proceed even + if the thread that calls this routine is not executing in the CA client + library. Furthermore, in preemptive callback mode callbacks might be + called with less latency because the library is not required to wait + until the initializing thread (the thread that called ca_context_create) + is executing within the CA client library.

    +
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_ALLOCMEM - Failed, unable to allocate space in pool

    + +

    ECA_NOTTHREADED - Current thread is already a member of a non-preemptive +callback CA context (possibly created implicitly)

    + +

    See Also

    + +

    ca_context_destroy()

    + +

    ca_context_destroy()

    +
    #include <cadef.h>
    +void ca_context_destroy();
    + +

    Description

    + +

    Shut down the calling thread's channel access client context and free any +resources allocated. Detach the calling thread from any CA client context.

    + +

    Any user-created threads that have attached themselves to the CA context +must stop using it prior to its being destroyed. A program running in an IOC +context must delete all of its channels prior to calling ca_context_destroy() +to avoid a crash.

    + +

    A CA client application that calls epicsExit() must install an +EPICS exit handler that calls ca_context_destroy() only after first +calling ca_create_context(). This will guarantee that the EPICS exit handlers +get called in the correct order.

    + +

    On many OS that execute programs in a process based environment the +resources used by the client library such as sockets and allocated memory are +automatically released by the system when the process exits and +ca_context_destroy() hasn't been called, but on light weight systems such as +vxWorks or RTEMS no cleanup occurs unless the application call +ca_context_destroy().

    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    See Also

    + +

    ca_context_create()

    + +

    ca_create_channel()

    +
    #include <cadef.h>
    +typedef void ( *pCallBack ) (
    +         struct connection_handler_args );
    +int ca_create_channel
    +(
    +        const char     *PROCESS_VARIABLE_NAME, 
    +        caCh           *USERFUNC, 
    +        void           *PUSER,
    +        capri          priority,
    +        chid           *PCHID
    +);
    + +

    Description

    + +

    This function creates a CA channel. The CA client library will attempt to +establish and maintain a virtual circuit between the caller's application and a +named process variable in a CA server. Each call to ca_create_channel allocates +resources in the CA client library and potentially also a CA server. The +function ca_clear_channel() is used to release these resources. If successful, +the routine writes a channel identifier into the user's variable of type +"chid". This identifier can be used with any channel access call that operates +on a channel.

    + +

    The circuit may be initially connected or disconnected depending on the +state of the network and the location of the channel. A channel will only enter +a connected state after server's address is determined, and only if channel +access successfully establishes a virtual circuit through the network to the +server. Channel access routines that send a request to a server will return +ECA_DISCONNCHID if the channel is currently disconnected.

    + +

    There are two ways to obtain asynchronous notification when a channel enters +a connected state.

    +
      +
    • The first and simplest method requires that you call ca_pend_io(), and + wait for successful completion, prior to using a channel that was created + specifying a nil connection call back function pointer.
    • +
    • The second method requires that you register a connection handler by + supplying a valid connection callback function pointer. This connection + handler is called whenever the connection state of the channel changes. If + you have installed a connection handler then ca_pend_io() will not + block waiting for the channel to enter a connected state.
    • +
    + +

    The function ca_state(CHID) can be used to test the connection state of a +channel. Valid connections may be isolated from invalid ones with this function +if ca_pend_io() times out.

    + +

    Due to the inherently transient nature of network connections the order of +connection call backs relative to the order that ca_create_channel() calls are +made by the application can't be guaranteed, and application programs may need +to be prepared for a connected channel to enter a disconnected state at any +time.

    + +

    Example

    + +

    See caExample.c in the example application created by makeBaseApp.pl.

    + +

    Arguments

    +
    +
    PROCESS_VARIABLE_NAME
    +
    A nil terminated process variable name string. EPICS process control + function block database variable names are of the form "<record + name>.<field name>". If the field name and the period separator + are omitted then the "VAL" field is implicit. For example "RFHV01" and + "RFHV01.VAL" reference the same EPICS process control function block + database variable.
    +
    +
    +
    USERFUNC
    +
    Optional address of the user's call back function to be run when the + connection state changes. Casual users of channel access may decide to + set this field to nil or 0 if they do not need to have a call back + function run in response to each connection state change event. +

    The following structure is passed by value to the user's + connection connection callback function. The op field will + be set by the CA client library to CA_OP_CONN_UP when the + channel connects, and to CA_OP_CONN_DOWN when the channel + disconnects. See ca_puser if the + PUSER argument is required in your callback + handler.

    +
    struct  ca_connection_handler_args {
    +    chanId  chid;  /* channel id */
    +    long    op;    /* one of CA_OP_CONN_UP or CA_OP_CONN_DOWN */
    +};
    +
    +
    +
    +
    PUSER
    +
    The value of this void pointer argument is retained in + storage associated with the specified channel. See the MACROS manual page + for reading and writing this field. Casual users of channel access may + wish to set this field to nil or 0.
    +
    +
    +
    PRIORITY
    +
    The priority level for dispatch within the server or network with 0 + specifying the lowest dispatch priority and 99 the highest. This + parameter currently does not impact dispatch priorities within the + client, but this might change in the future. The abstract priority range + specified is mapped into an operating system specific range of priorities + within the server. This parameter is ignored if the server is running on + a network or operating system that does not have native support for + prioritized delivery or execution respectively. Specifying many different + priorities within the same program can increase resource consumption in + the client and the server because an independent virtual circuit, and + associated data structures, is created for each priority that is used on + a particular server.
    +
    +
    +
    PCHID
    +
    The user supplied channel identifier storage is overwritten with a + channel identifier if this routine is successful.
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADTYPE - Invalid DBR_XXXX type

    + +

    ECA_STRTOBIG - Unusually large string

    + +

    ECA_ALLOCMEM - Unable to allocate memory

    + +

    ca_clear_channel()

    +
    #include <cadef.h>
    +int ca_clear_channel (chid CHID);
    + +

    Description

    + +

    Shutdown and reclaim resources associated with a channel created by +ca_create_channel().

    + +

    All remote operation requests such as the above are accumulated (buffered) +and not forwarded to the IOC until one of ca_flush_io, ca_pend_io, +ca_pend_event, or ca_sg_pend are called. This allows several requests to be +efficiently sent over the network in one message.

    + +

    Clearing a channel does not cause its disconnect handler to be called, but +clearing a channel does shutdown and reclaim any channel state change event +subscriptions (monitors) registered with the channel.

    + +

    Arguments

    +
    +
    CHID
    +
    Identifies the channel to delete.
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADCHID - Corrupted CHID

    + +

    ca_put()

    +
    #include <cadef.h>
    +int ca_put ( chtype TYPE, 
    +        chid CHID, void *PVALUE ); 
    +int ca_array_put ( chtype TYPE, 
    +        unsigned long COUNT, 
    +        chid CHID, const void *PVALUE);
    +typedef void ( *pCallBack ) (struct event_handler_args );
    +int ca_put_callback ( chtype TYPE, 
    +        chid CHID, const void *PVALUE, 
    +        pCallBack PFUNC, void *USERARG ); 
    +int ca_array_put_callback ( chtype TYPE, 
    +        unsigned long COUNT, 
    +        chid CHID, const void *PVALUE, 
    +        pCallBack PFUNC, void *USERARG );
    + +

    Description

    + +

    Write a scalar or array value to a process variable.

    + +

    When ca_array_put or ca_put are invoked the client will receive no response +unless the request can not be fulfilled in the server. If unsuccessful an +exception handler is run on the client side.

    + +

    When ca_array_put_callback are invoked the user supplied asynchronous call +back is called only after the initiated write operation, and all actions +resulting from the initiating write operation, complete.

    + +

    If unsuccessful the call back function is invoked indicating failure status. +

    + +

    If the channel disconnects before a put callback request can be completed, +then the client's call back function is called with failure status, but this +does not guarantee that the server did not receive and process the request +before the disconnect. If a connection is lost and then resumed outstanding ca +put requests are not automatically reissued following reconnect.

    + +

    All of these functions return ECA_DISCONN if the channel is currently +disconnected.

    + +

    All put requests are accumulated (buffered) and not forwarded to the IOC +until one of ca_flush_io, ca_pend_io, ca_pend_event, or ca_sg_pend are called. +This allows several requests to be efficiently combined into one message.

    + +

    Description (IOC Database Specific)

    + +

    A CA put request causes the record to process if the record's SCAN field is +set to passive, and the field being written has it's process passive attribute +set to true. If such a record is already processing when a put request is +initiated the specified field is written immediately, and the record is +scheduled to process again as soon as it finishes processing. Earlier instances +of multiple put requests initiated while the record is being processing may be +discarded, but the last put request initiated is always written and +processed.

    + +

    A CA put callback request causes +the record to process if the record's SCAN field is set to passive, and the +field being written has it's process passive attribute set to true. For such a +record, the user's put callback function is not called until after the record, +and any records that the record links to, finish processing. If such a record +is already processing when a put callback request is initiated the put callback request is postponed until the +record, and any records it links to, finish processing.

    + +

    If the record's SCAN field is not set to passive, or the field being written +has it's process passive attribute set to false then the CA put or CA put +callback request cause the specified field to be immediately written, but they +do not cause the record to be processed.

    + +

    Arguments

    +
    +
    TYPE
    +
    The external type of the supplied value to be written. Conversion will + occur if this does not match the native type. Specify one from the set of + DBR_XXXX in db_access.h
    +
    +
    +
    COUNT
    +
    Element count to be written to the specified channel. This must match + the array pointed to by PVALUE.
    +
    +
    +
    CHID
    +
    Channel identifier
    +
    +
    +
    PVALUE
    +
    Pointer to a value or array of values provided by the application to be + written to the channel.
    +
    +
    +
    PFUNC
    +
    address of user supplied callback function to be + run when the requested operation completes
    +
    +
    +
    USERARG
    +
    pointer sized variable retained and then passed back to user supplied + function above
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADCHID - Corrupted CHID

    + +

    ECA_BADTYPE - Invalid DBR_XXXX type

    + +

    ECA_BADCOUNT - Requested count larger than native element count

    + +

    ECA_STRTOBIG - Unusually large string supplied

    + +

    ECA_NOWTACCESS - Write access denied

    + +

    ECA_ALLOCMEM - Unable to allocate memory

    + +

    ECA_DISCONN - Channel is disconnected

    + +

    See Also

    + +

    ca_flush_io()

    + +

    ca_pend_event()

    + +

    ca_sg_array_put()

    + +

    ca_get()

    +
    #include <cadef.h>
    +int ca_get ( chtype TYPE,
    +        chid CHID, void *PVALUE );
    +int ca_array_get ( chtype TYPE, unsigned long COUNT,
    +        chid CHID, void *PVALUE );
    +typedef void ( *pCallBack ) (struct event_handler_args );
    +int ca_get_callback ( chtype TYPE,
    +        chid CHID, pCallBack USERFUNC, void *USERARG);
    +int ca_array_get_callback ( chtype TYPE, unsigned long COUNT,
    +        chid CHID,
    +        pCallBack USERFUNC, void *USERARG );
    + +

    Description

    + +

    Read a scalar or array value from a process variable.

    + +

    When ca_get or ca_array_get are invoked the returned channel value cant be +assumed to be stable in the application supplied buffer until after ECA_NORMAL +is returned from ca_pend_io. If a connection is lost outstanding ca get +requests are not automatically reissued following reconnect.

    + +

    When ca_get_callback or ca_array_get_callback are invoked a value is read +from the channel and then the user's callback is invoked with a pointer to the +retrieved value. Note that ca_pend_io will not block for the delivery of values +requested by ca_get_callback. If the channel disconnects before a ca get +callback request can be completed, then the clients call back function is +called with failure status.

    + +

    All of these functions return ECA_DISCONN if the channel is currently +disconnected.

    + +

    All get requests are accumulated (buffered) and not forwarded to the IOC +until one of ca_flush_io, ca_pend_io, ca_pend_event, or ca_sg_pend are called. +This allows several requests to be efficiently sent over the network in one +message.

    + +

    Description (IOC Database Specific)

    + +

    A CA get or CA get callback request causes the record's field to be read +immediately independent of whether the record is currently being processed or +not. There is currently no mechanism in place to cause a record to be processed +when a CA get request is initiated.

    + +

    Example

    + +

    See caExample.c in the example application created by makeBaseApp.pl.

    + +

    Arguments

    +
    +
    TYPE
    +
    The external type of the user variable to return the value into. + Conversion will occur if this does not match the native type. Specify one + from the set of DBR_XXXX in db_access.h
    +
    +
    +
    COUNT
    +
    Element count to be read from the specified channel. Must match the + array pointed to by PVALUE. For ca_array_get_callback a count of zero + means use the current element count from the server.
    +
    +
    +
    CHID
    +
    Channel identifier
    +
    +
    +
    PVALUE
    +
    Pointer to an application supplied buffer where the current value of + the channel is to be written.
    +
    +
    +
    USERFUNC
    +
    Address of user supplied callback function to be + run when the requested operation completes.
    +
    +
    +
    USERARG
    +
    Pointer sized variable retained and then passed back to user supplied + call back function above.
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADTYPE - Invalid DBR_XXXX type

    + +

    ECA_BADCHID - Corrupted CHID

    + +

    ECA_BADCOUNT - Requested count larger than native element count

    + +

    ECA_GETFAIL - A local database get failed

    + +

    ECA_NORDACCESS - Read access denied

    + +

    ECA_ALLOCMEM - Unable to allocate memory

    + +

    ECA_DISCONN - Channel is disconnected

    + +

    See Also

    + +

    ca_pend_io()

    + +

    ca_pend_event()

    + +

    ca_sg_array_get()

    + +

    ca_create_subscription()

    +
    #include <cadef.h>
    +typedef void ( *pCallBack ) (
    +        struct event_handler_args );
    +int ca_create_subscription ( chtype TYPE, 
    +        unsigned long COUNT, chid CHID, 
    +        unsigned long MASK, pCallBack USERFUNC, void *USERARG, 
    +        evid *PEVID );
    + +

    Description

    + +

    Register a state change subscription and specify a call back function to be +invoked whenever the process variable undergoes significant state changes. A +significant change can be a change in the process variable's value, alarm +status, or alarm severity. In the process control function block database the +deadband field determines the magnitude of a significant change for for the +process variable's value. Each call to this function consumes resources in the +client library and potentially a CA server until one of ca_clear_channel or +ca_clear_event is called.

    + +

    Subscriptions may be installed or canceled against both connected and +disconnected channels. The specified USERFUNC is called once immediately after +the subscription is installed with the process variable's current state if the +process variable is connected. Otherwise, the specified USERFUNC is called +immediately after establishing a connection (or reconnection) with the process +variable. The specified USERFUNC is called immediately with the process +variable's current state from within ca_add_event() if the client and the +process variable share the same address space.

    + +

    If a subscription is installed on a channel in a disconnected state then the +requested count will be set to the native maximum element count of the channel +if the requested count is larger.

    + +

    All subscription requests such as the above are accumulated (buffered) and +not forwarded to the IOC until one of ca_flush_io, ca_pend_io, ca_pend_event, +or ca_sg_pend are called. This allows several requests to be efficiently sent +over the network in one message.

    + +

    If at any time after subscribing, read access to the specified process +variable is lost, then the call back will be invoked immediately indicating +that read access was lost via the status argument. When read access is restored +normal event processing will resume starting always with at least one update +indicating the current state of the channel.

    + +

    A better name for this function might have been ca_subscribe.

    + +

    Example

    + +

    See caMonitor.c in the example application created by makeBaseApp.pl.

    + +

    Arguments

    +
    +
    TYPE
    +
    The type of value presented to the call back funstion. Conversion will + occur if it does not match native type. Specify one from the set of + DBR_XXXX in db_access.h
    +
    +
    +
    COUNT
    +
    The element count to be read from the specified channel. A count of + zero means use the current element count from the server.
    +
    +
    +
    CHID
    +
    channel identifier
    +
    +
    +
    USRERFUNC
    +
    The address of user supplied callback function to + be invoked with each subscription update.
    +
    +
    +
    USERARG
    +
    pointer sized variable retained and passed back to user callback + function
    +
    +
    +
    RESERVED
    +
    Reserved for future use. Specify 0.0 to remain upwardly compatible.
    +
    +
    +
    PEVID
    +
    This is a pointer to user supplied event id which is overwritten if + successful. This event id can later be used to clear a specific + event. This option may may be omitted by passing a nil pointer.
    +
    +
    +
    MASK
    +
    A mask with bits set for each of the event trigger types requested. The + event trigger mask must be a bitwise or of one or more of the + following constants. +
      +
    • DBE_VALUE - Trigger events when the channel value exceeds the + monitor dead band
    • +
    • DBE_ARCHIVE (or DBE_LOG) - Trigger events when the channel value + exceeds the archival dead band
    • +
    • DBE_ALARM - Trigger events when the channel alarm state changes
    • +
    • DBE_PROPERTY - Trigger events when a channel property changes.
    • +
    +

    For functions above that do not include a trigger specification, + events will be triggered when there are significant changes in the + channel's value or when there are changes in the channel's alarm state. + This is the same as "DBE_VALUE | DBE_ALARM."

    +
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADCHID - Corrupted CHID

    + +

    ECA_BADTYPE - Invalid DBR_XXXX type

    + +

    ECA_ALLOCMEM - Unable to allocate memory

    + +

    ECA_ADDFAIL - A local database event add failed

    + +

    See Also

    + +

    ca_pend_event()

    + +

    ca_flush_io()

    + +

    ca_clear_subscription()

    +
    #include <cadef.h>
    +int ca_clear_subscription ( evid EVID );
    + +

    Description

    + +

    Cancel a subscription.

    + +

    All ca_clear_event() requests such as the above are accumulated (buffered) +and not forwarded to the server until one of ca_flush_io, ca_pend_io, +ca_pend_event, or ca_sg_pend are called. This allows several requests to be +efficiently sent together in one message.

    + +

    Arguments

    +
    +
    EVID
    +
    event id returned by ca_add_event()
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADCHID - Corrupted CHID

    + +

    See Also

    + +

    ca_create_subscription()

    + +

    ca_pend_io()

    +
    #include <cadef.h>
    +int ca_pend_io ( double TIMEOUT );
    + +

    Description

    + +

    This function flushes the send buffer and then blocks until outstanding ca_get requests complete, and until channels created +specifying nill connection handler function pointers connect for the first +time.

    +
      +
    • If ECA_NORMAL is returned then it can be safely assumed that all + outstanding ca_get requests have completed + successfully and channels created specifying nill connection handler + function pointers have connected for the first time.
    • +
    • If ECA_TIMEOUT is returned then it must be assumed for all previous ca_get requests and properly qualified first time + channel connects have failed.
    • +
    + +

    If ECA_TIMEOUT is returned then get requests may be reissued followed by a +subsequent call to ca_pend_io(). Specifically, the function will block only for +outstanding ca_get requests issued, and also any channels +created specifying a nill connection handler function pointer, after the last +call to ca_pend_io() or ca client context creation whichever is later. Note +that ca_create_channel requests generally +should not be reissued for the same process variable unless ca_clear_channel is called first.

    + +

    If no ca_get or connection state change events are +outstanding then ca_pend_io() will flush the send buffer and return immediately +without processing any outstanding channel access background +activities.

    + +

    The delay specified to ca_pend_io() should take into account worst case +network delays such as Ethernet collision exponential back off until +retransmission delays which can be quite long on overloaded networks.

    + +

    Unlike ca_pend_event, this routine will +not process CA's background activities if none of the selected IO requests are +pending.

    + +

    Arguments

    +
    +
    TIMEOUT
    +
    Specifies the time out interval. A TIMEOUT interval of + zero specifies forever.
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_TIMEOUT - Selected IO requests didnt complete before specified +timeout

    + +

    ECA_EVDISALLOW - Function inappropriate for use within an event handler

    + +

    See Also

    + +

    ca_get()

    + +

    ca_create_channel()

    + +

    ca_test_io()

    + +

    ca_test_io()

    +
    #include <cadef.h>
    +int ca_test_io();
    + +

    Description

    + +

    This function tests to see if all ca_get requests are +complete and channels created specifying a nill connection callback function +pointer are connected. It will report the status of outstanding ca_get requests issued, and channels created specifying a +nill connection callback function pointer, after the last call to ca_pend_io() +or CA context initialization whichever is later.

    + +

    Returns

    + +

    ECA_IODONE - All IO operations completed

    + +

    ECA_IOINPROGRESS - IO operations still in progress

    + +

    See Also

    + +

    ca_pend_io()

    + +

    ca_pend_event()

    +
    #include <cadef.h>
    +int ca_pend_event ( double TIMEOUT );
    +int ca_poll ();
    + +

    Description

    + +

    When ca_pend_event is invoked the send buffer is flushed and CA background +activity is processed for TIMEOUT seconds.

    + +

    When ca_poll is invoked the send buffer is flushed and any outstanding CA +background activity is processed.

    + +

    The ca_pend_event function will not return before the specified +time-out expires and all unfinished channel access labor has been processed, +and unlike ca_pend_io returning from the +function does not indicate anything about the status of pending IO +requests.

    + +

    Both ca_pend_event and ca_poll return ECA_TIMEOUT +when successful. This behavior probably isn't intuitive, but it is preserved to +insure backwards compatibility.

    + +

    See also Thread Safety and Preemptive Callback to User +Code.

    + +

    Arguments

    +
    +
    TIMEOUT
    +
    The duration to block in this routine in seconds. A timeout of zero + seconds blocks forever.
    +
    + +

    Returns

    + +

    ECA_TIMEOUT - The operation timed out

    + +

    ECA_EVDISALLOW - Function inappropriate for use within a call back +handler

    + +

    ca_flush_io()

    +
    #include <cadef.h>
    +int ca_flush_io();
    + +

    Description

    + +

    Flush outstanding IO requests to the server. This routine might be useful +to users who need to flush requests prior to performing client side labor in +parallel with labor performed in the server.

    + +

    Outstanding requests are also sent whenever the buffer which holds them +becomes full.

    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ca_signal()

    +
    #include <cadef.h>
    +int ca_signal ( long CA_STATUS, const char * CONTEXT_STRING ); 
    +void SEVCHK( CA_STATUS, CONTEXT_STRING );
    + +

    Description

    + +

    Provide the error message character string associated with the supplied +channel access error code and the supplied error context to diagnostics. If the +error code indicates an unsuccessful operation a stack dump is printed, if this +capability is available on the local operating system, and execution is +terminated.

    + +

    SEVCHK is a macro envelope around ca_signal which only calls ca_signal() if +the supplied error code indicates an unsuccessful operation. SEVCHK is the +recommended error handler for simple applications which do not wish to write +code testing the status returned from each channel access call.

    + +

    Examples

    +
    status = ca_context_create (...); 
    +SEVCHK ( status, "Unable to create a CA client context" );
    + +

    If the application only wishes to print the message associated with an error +code or test the severity of an error there are also functions provided for +this purpose.

    + +

    Arguments

    +
    +
    CA_STATUS
    +
    The status (error code) returned from a channel access function.
    +
    +
    +
    CONTEXT_STRING
    +
    A null terminated character string to supply as error context to + diagnostics.
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ca_add_exception_event()

    +
    #include <cadef.h> 
    +typedef void (*pCallback) ( struct exception_handler_args HANDLERARGS );
    +int ca_add_exception_event ( pCallback  USERFUNC, void *USERARG );
    + +

    Description

    + +

    Replace the currently installed CA context global exception handler call +back.

    + +

    When an error occurs in the server asynchronous to the clients thread then +information about this type of error is passed from the server to the client in +an exception message. When the client receives this exception message an +exception handler callback is called.The default exception handler prints a +diagnostic message on the client's standard out and terminates execution if the +error condition is severe.

    + +

    Note that certain fields in "struct exception_handler_args" are not +applicable in the context of some error messages. For instance, a failed get +will supply the address in the client task where the returned value was +requested to be written. For other failed operations the value of the addr +field should not be used.

    + +

    Arguments

    +
    +
    USERFUNC
    +
    Address of user callback function to be executed when an exceptions + occur. Passing a nil value causes the default exception handler to be + reinstalled. The following structure is passed by value to the user's + callback function. Currently, the op field can be one of + CA_OP_GET, CA_OP_PUT, CA_OP_CREATE_CHANNEL, CA_OP_ADD_EVENT, + CA_OP_CLEAR_EVENT, or CA_OP_OTHER. +
    struct exception_handler_args {
    +    void            *usr;   /* user argument supplied when installed */
    +    chanId          chid;   /* channel id (may be nill) */
    +    long            type;   /* type requested */
    +    long            count;  /* count requested */
    +    void            *addr;  /* user's address to write results of CA_OP_GET */
    +    long            stat;   /* channel access ECA_XXXX status code */
    +    long            op;     /* CA_OP_GET, CA_OP_PUT, ..., CA_OP_OTHER */
    +    const char      *ctx;   /* a character string containing context info */
    +    sonst char      *pFile; /* source file name (may be NULL) */
    +    unsigned        lineNo; /* source file line number (may be zero) */
    +};
    +
    +
    +
    +
    USERARG
    +
    pointer sized variable retained and passed back to user function + above
    +
    + +

    Example

    +
    void ca_exception_handler (
    +    struct exception_handler_args args)
    +{
    +    char buf[512];
    +    char *pName;
    +
    +    if ( args.chid ) {
    +        pName = ca_name ( args.chid );
    +    }
    +    else {
    +        pName = "?";
    +    }
    +    sprintf ( buf,
    +            "%s - with request chan=%s op=%d data type=%s count=%d",
    +            args.ctx, pName, args.op, dbr_type_to_text ( args.type ), args.count );
    +    ca_signal ( args.stat, buf );
    +}
    +ca_add_exception_event ( ca_exception_handler , 0 );
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    + +

    ca_add_fd_registration()

    +
    #include <cadef.h>
    +int ca_add_fd_registration ( void ( USERFUNC * ) ( void *USERARG, int FD, int OPENED ), void * USERARG )
    + +

    Description

    + +

    For use with the services provided by a file descriptor manager (IO +multiplexor) such as "fdmgr.c". A file descriptor manager is often needed when +two file descriptor IO intensive libraries such as the EPICS channel access +client library and the X window system client library must coexist in the same +UNIX process. This function allows an application code to be notified whenever +the CA client library places a new file descriptor into service and whenever +the CA client library removes a file descriptor from service. Specifying +USERFUNC=NULL disables file descriptor registration (this is the default).

    + +

    Arguments

    + +

    USERFUNC

    + +

    Pointer to a user supplied C function returning null with the above +arguments.

    + +

    USERARG

    + +

    User supplied pointer sized variable passed to the above function.

    + +

    FD

    + +

    A file descriptor.

    + +

    OPENED

    + +

    Boolean argument is true if the file descriptor was opened and false if the +file descriptor was closed.

    + +

    Example

    +
    int s;
    +static struct myStruct aStruct;
    +
    +void fdReg ( struct myStruct *pStruct, int fd, int opened )
    +{
    +    if ( opened ) printf ( "fd %d was opened\n", fd );
    +    else printf ( "fd %d was closed\n", fd );
    +}
    +s = ca_add_fd_registration ( fdReg, & aStruct );
    +SEVCHK ( s, NULL );
    + +

    Comments

    + +

    When using this function it is advisable to call it only once prior to +calling any other CA function, or once just after creating the CA context (if +you create the context explicitly). Use of this interface can improve latency +slightly in applications that use non preemptive callback mode at the expense +of some additional runtime overhead when compared to the alternative which is +just polling ca_pend_event periodically. It would probably not be appropriate +to use this function with preemptive callback mode. Starting with R3.14 this +function is implemented in a special backward compatibility mode. if +ca_add_fd_registration is called, a single pseudo UDP fd is +created which CA pokes whenever something significant happens. Xt and others +can watch this fd so that backwards compatibility is preserved, and so that +they will not need to use preemptive callback mode but they will nevertheless +get the lowest latency response to the arrival of CA messages.

    + +

    Returns

    + +

    "ECA_NORMAL - Normal successful completion

    + +

    + +

    ca_replace_printf_handler +()

    +
    #include <cadef.h>
    +typedef int caPrintfFunc ( const char *pFromat, va_list args );
    +int ca_replace_printf_handler ( caPrintfFunc *PFUNC );
    + +

    Description

    + +

    Replace the default handler for formatted diagnostic message output. The +default handler uses fprintf to send messages to 'stderr'.

    + +

    Arguments

    +
    +
    PFUNC
    +
    The address of a user supplied call back handler to be invoked when CA + prints diagnostic messages. Installing a nil pointer will cause the + default call back handler to be reinstalled.
    +
    + +

    Examples

    +
    int my_printf ( char *pformat, va_list args ) {
    +        int status;
    +        status = vfprintf( stderr, pformat, args);
    +        return status;
    +}
    +status = ca_replace_printf_handler ( my_printf );
    +SEVCHK ( status, "failed to install my printf handler" );
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ca_replace_access_rights_event()

    +
    #include <cadef.h>
    +typedef void ( *pCallBack )( struct access_rights_handler_args );
    +int ca_replace_access_rights_event ( chid CHAN, pCallBack PFUNC );
    + +

    Description

    + +

    Install or replace the access rights state change callback handler for the +specified channel.

    + +

    The callback handler is called in the following situations.

    +
      +
    • whenever CA connects the channel immediately before the channel's + connection handler is called
    • +
    • whenever CA disconnects the channel immediately after the channel's + disconnect call back is called
    • +
    • once immediately after installation if the channel is connected.
    • +
    • whenever the access rights state of a connected channel changes
    • +
    + +

    When a channel is created no access rights handler is installed.

    + +

    Arguments

    +
    +
    CHAN
    +
    The channel identifier.
    +
    +
    +
    PFUNC
    +
    Address of user supplied call back function. A nil pointer uninstalls + the current handler. The following arguments are passed by value + to the supplied callback handler. +
    typedef struct ca_access_rights {
    +    unsigned    read_access:1;
    +    unsigned    write_access:1;
    +} caar;
    +
    +/* arguments passed to user access rights handlers */
    +struct  access_rights_handler_args {
    +    chanId  chid;   /* channel id */
    +    caar    ar;     /* new access rights state */
    +};
    +
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ca_field_type()

    +
    #include <cadef.h>
    +chtype ca_field_type ( CHID );
    + +

    Description

    + +

    Return the native type in the server of the process variable.

    + +

    Arguments

    +
    +
    CHID
    +
    channel identifier
    +
    + +

    Returns

    +
    +
    TYPE
    +
    The data type code will be a member of the set of DBF_XXXX in + db_access.h. The constant TYPENOTCONN is returned if the channel is + disconnected.
    +
    + +

    ca_element_count()

    +
    #include <cadef.h>
    +unsigned ca_element_count ( CHID );
    + +

    Description

    + +

    Return the maximum array element count in the server for the specified IO +channel.

    + +

    Arguments

    +
    +
    CHID
    +
    channel identifier
    +
    + +

    Returns

    +
    +
    COUNT
    +
    The maximum array element count in the server. An element count of + zero is returned if the channel is disconnected.
    +
    + +

    ca_name()

    +
    #include <cadef.h>
    +char * ca_name ( CHID );
    + +

    Description

    + +

    Return the name provided when the supplied channel id was created.

    + +

    Arguments

    +
    +
    CHID
    +
    channel identifier
    +
    + +

    Returns

    +
    +
    PNAME
    +
    The channel name. The string returned is valid as long as the channel + specified exists.
    +
    + +

    ca_set_puser()

    +
    #include <cadef.h>
    +void ca_set_puser ( chid CHID, void *PUSER );
    + +

    Description

    + +

    Set a user private void pointer variable retained with each channel for use +at the users discretion.

    + +

    Arguments

    +
    +
    CHID
    +
    channel identifier
    +
    +
    +
    PUSER
    +
    user private void pointer
    +
    + +

    ca_puser()

    +
    #include <cadef.h>
    +void * ca_puser ( CHID );
    + +

    Description

    + +

    Return a user private void pointer variable retained with each channel for +use at the users discretion.

    + +

    Arguments

    +
    +
    CHID
    +
    channel identifier
    +
    + +

    Returns

    +
    +
    PUSER
    +
    user private pointer
    +
    + +

    ca_state()

    +
    #include <cadef.h>
    +enum channel_state {
    +    cs_never_conn, /* valid chid, server not found or unavailable */
    +    cs_prev_conn,  /* valid chid, previously connected to server */
    +    cs_conn,       /* valid chid, connected to server */
    +    cs_closed };   /* channel deleted by user */
    +enum channel_state ca_state ( CHID );
    + +

    Description

    + +

    Returns an enumerated type indicating the current state of the specified IO +channel.

    + +

    Arguments

    +
    +
    CHID
    +
    channel identifier
    +
    + +

    Returns

    +
    +
    STATE
    +
    the connection state
    +
    + +

    ca_message()

    +
    #include <cadef.h>
    +const char * ca_message ( STATUS );
    + +

    Description

    + +

    return a message character string corresponding to a user specified CA +status code.

    + +

    Arguments

    +
    +
    STATUS
    +
    a CA status code
    +
    + +

    Returns

    +
    +
    STRING
    +
    the corresponding error message string
    +
    + +

    ca_host_name()

    +
    #include <cadef.h>
    +char * ca_host_name ( CHID );
    + +

    Description

    + +

    Return a character string which contains the name of the host to which a +channel is currently connected.

    + +

    Arguments

    +
    +
    CHID
    +
    the channel identifier
    +
    + +

    Returns

    +
    +
    STRING
    +
    The process variable server's host name. If the channel is disconnected + the string "<disconnected>" is returned.
    +
    + +

    ca_read_access()

    +
    #include <cadef.h>
    +int ca_read_access ( CHID );
    + +

    Description

    + +

    Returns boolean true if the client currently has read access to the +specified channel and boolean false otherwise.

    + +

    Arguments

    +
    +
    CHID
    +
    the channel identifier
    +
    + +

    Returns

    +
    +
    STRING
    +
    boolean true if the client currently has read access to the specified + channel and boolean false otherwise
    +
    + +

    ca_write_access()

    +
    #include <cadef.h>
    +int ca_write_access ( CHID );
    + +

    Description

    + +

    Returns boolean true if the client currently has write access to the +specified channel and boolean false otherwise.

    + +

    Arguments

    +
    +
    CHID
    +
    the channel identifier
    +
    + +

    Returns

    +
    +
    STRING
    +
    boolean true if the client currently has write access to the specified + channel and boolean false otherwise
    +
    + +

    dbr_size[]

    +
    #include <db_access.h>
    +extern unsigned dbr_size[/* TYPE */];
    + +

    Description

    + +

    An array that returns the size in bytes for a DBR_XXXX type.

    + +

    Arguments

    +
    +
    TYPE
    +
    The data type code. A member of the set of DBF_XXXX in db_access.h.
    +
    + +

    Returns

    +
    +
    SIZE
    +
    the size in bytes of the specified type
    +
    + +

    dbr_size_n()

    +
    #include <db_access.h>
    +unsigned dbr_size_n ( TYPE, COUNT );
    + +

    Description

    + +

    Returns the size in bytes for a DBR_XXXX type with COUNT elements. If the +DBR type is a structure then the value field is the last field in the +structure. If COUNT is greater than one then COUNT-1 elements are appended to +the end of the structure so that they can be addressed as an array through a +pointer to the value field.

    + +

    Arguments

    +
    +
    TYPE
    +
    The data type
    +
    +
    +
    COUNT
    +
    The element count
    +
    + +

    Returns

    +
    +
    SIZE
    +
    the size in bytes of the specified type with the specified number of + elements
    +
    + +

    dbr_value_size[]

    +
    #include <db_access.h>
    +extern unsigned dbr_value_size[/* TYPE */];
    + +

    Description

    + +

    The array dbr_value_size[TYPE] returns the size in bytes for the value +stored in a DBR_XXXX type. If the type is a structure the size of the value +field is returned otherwise the size of the type is returned.

    + +

    Arguments

    +
    +
    TYPE
    +
    The data type code. A member of the set of DBF_XXXX in db_access.h.
    +
    + +

    Returns

    +
    +
    SIZE
    +
    the size in bytes of the value field if the type is a structure and + otherwise the size in bytes of the type
    +
    + +

    dbr_type_to_text()

    +
    #include <db_access.h>
    +const char * dbr_type_text ( chtype TYPE );
    + +

    Description

    + +

    Returns a constant null terminated string corresponding to the specified dbr +type.

    + +

    Arguments

    +
    +
    TYPE
    +
    The data type code. A member of the set of DBR_XXXX in db_access.h.
    +
    + +

    Returns

    +
    +
    STRING
    +
    +
    The const string corresponding to the DBR_XXX type.
    +
    + +

    ca_test_event()

    +
    #include <cadef.h>
    + +

    Description

    +
    void ca_test_event ( struct event_handler_args );
    + +

    A built-in subscription update call back handler for debugging purposes that +prints diagnostics to standard out.

    + +

    Examples

    +
    void ca_test_event (); 
    +status = ca_add_event ( type, chid, ca_test_event, NULL, NULL ); 
    +SEVCHK ( status, .... );
    + +

    See Also

    + +

    ca_add_event()

    + +

    ca_sg_create()

    +
    #include <cadef.h>
    +int ca_sg_create ( CA_SYNC_GID *PGID );
    + +

    Description

    + +

    Create a synchronous group and return an identifier for it.

    + +

    A synchronous group can be used to guarantee that a set of channel access +requests have completed. Once a synchronous group has been created then channel +access get and put requests may be issued within it using ca_sg_get() and +ca_sg_put() respectively. The routines ca_sg_block() and ca_sg_test() can be +used to block for and test for completion respectively. The routine +ca_sg_reset() is used to discard knowledge of old requests which have timed out +and in all likelihood will never be satisfied.

    + +

    Any number of asynchronous groups can have application requested operations +outstanding within them at any given time.

    + +

    Arguments

    +
    +
    PGID
    +
    Pointer to a user supplied CA_SYNC_GID.
    +
    + +

    Examples

    +
    CA_SYNC_GID gid; 
    +status = ca_sg_create ( &gid ); 
    +SEVCHK ( status, Sync group create failed );
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_ALLOCMEM - Failed, unable to allocate memory

    + +

    See Also

    + +

    ca_sg_delete()

    + +

    ca_sg_block()

    + +

    ca_sg_test()

    + +

    ca_sg_reset()

    + +

    ca_sg_array_put()

    + +

    ca_sg_array_get()

    + +

    ca_sg_delete()

    +
    #include <cadef.h>
    +int ca_sg_delete ( CA_SYNC_GID GID );
    + +

    Description

    + +

    Deletes a synchronous group.

    + +

    Arguments

    +
    +
    GID
    +
    Identifier of the synchronous group to be deleted.
    +
    + +

    Examples

    +
    CA_SYNC_GID gid; 
    +status = ca_sg_delete ( gid ); 
    +SEVCHK ( status, Sync group delete failed );
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADSYNCGRP - Invalid synchronous group

    + +

    See Also

    + +

    ca_sg_create()

    + +

    ca_sg_block()

    +
    #include <cadef.h>
    +int ca_sg_block ( CA_SYNC_GID GID, double timeout );
    + +

    Description

    + +

    Flushes the send buffer and then waits until outstanding requests complete +or the specified time out expires. At this time outstanding requests include +calls to ca_sg_array_get() and calls to ca_sg_array_put(). If ECA_TIMEOUT is +returned then failure must be assumed for all outstanding queries. Operations +can be reissued followed by another ca_sg_block(). This routine will only block +on outstanding queries issued after the last call to ca_sg_block(), +ca_sg_reset(), or ca_sg_create() whichever occurs later in time. If no queries +are outstanding then ca_sg_block() will return immediately without processing +any pending channel access activities.

    + +

    Values written into your program's variables by a channel access synchronous +group request should not be referenced by your program until ECA_NORMAL has +been received from ca_sg_block(). This routine will process pending channel +access background activity while it is waiting.

    + +

    Arguments

    +
    +
    GID
    +
    Identifier of the synchronous group.
    +
    + +

    Examples

    +
    CA_SYNC_GID gid;
    +status = ca_sg_block(gid);
    +SEVCHK(status, Sync group block failed);
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_TIMEOUT - The operation timed out

    + +

    ECA_EVDISALLOW - Function inappropriate for use within an event handler

    + +

    ECA_BADSYNCGRP - Invalid synchronous group

    + +

    See Also

    + +

    ca_sg_test()

    + +

    ca_sg_reset()

    + +

    ca_sg_test()

    +
    #include <cadef.h>
    +int ca_sg_test ( CA_SYNC_GID GID )
    + +

    Description

    + +

    Test to see if all requests made within a synchronous group have +completed.

    + +

    Arguments

    +
    +
    GID
    +
    Identifier of the synchronous group.
    +
    + +

    Description

    + +

    Test to see if all requests made within a synchronous group have +completed.

    + +

    Examples

    +
    CA_SYNC_GID gid;
    +status = ca_sg_test ( gid );
    + +

    Returns

    + +

    ECA_IODONE - IO operations completed

    + +

    ECA_IOINPROGRESS - Some IO operations still in progress

    + +

    ca_sg_reset()

    +
    #include <cadef.h>
    +int ca_sg_reset ( CA_SYNC_GID GID )
    + +

    Description

    + +

    Reset the number of outstanding requests within the specified synchronous +group to zero so that ca_sg_test() will return ECA_IODONE and ca_sg_block() +will not block unless additional subsequent requests are made.

    + +

    Arguments

    +
    +
    GID
    +
    Identifier of the synchronous group.
    +
    + +

    Examples

    +
    CA_SYNC_GID gid; 
    +status = ca_sg_reset(gid);
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADSYNCGRP - Invalid synchronous group

    + +

    ca_sg_array_put()

    +
    #include <cadef.h>
    +int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE, 
    +        unsigned long COUNT, chid CHID, void *PVALUE );
    + +

    Write a value, or array of values, to a channel and increment the +outstanding request count of a synchronous group. The ca_sg_array_put +functionality is implemented using ca_array_put_callback.

    + +

    All remote operation requests such as the above are accumulated (buffered) +and not forwarded to the server until one of ca_flush_io(), ca_pend_io(), +ca_pend_event(), or ca_sg_pend() are called. This allows several requests to be +efficiently sent in one message.

    + +

    If a connection is lost and then resumed outstanding puts are not +reissued.

    + +

    Arguments

    +
    +
    GID
    +
    synchronous group identifier
    +
    +
    +
    TYPE
    +
    The type of supplied value. Conversion will occur if it does not match + the native type. Specify one from the set of DBR_XXXX in db_access.h.
    +
    +
    +
    COUNT
    +
    element count to be written to the specified channel - must match the + array pointed to by PVALUE
    +
    +
    +
    CHID
    +
    channel identifier
    +
    +
    +
    PVALUE
    +
    A pointer to an application supplied buffer containing the value or + array of values returned
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADSYNCGRP - Invalid synchronous group

    + +

    ECA_BADCHID - Corrupted CHID

    + +

    ECA_BADTYPE - Invalid DBR_XXXX type

    + +

    ECA_BADCOUNT - Requested count larger than native element count

    + +

    ECA_STRTOBIG - Unusually large string supplied

    + +

    ECA_PUTFAIL - A local database put failed

    + +

    See Also

    + +

    ca_flush_io()

    + +

    ca_sg_array_get()

    +
    #include <cadef.h>
    +int ca_sg_array_get ( CA_SYNC_GID GID,
    +        chtype TYPE, unsigned long COUNT,
    +        chid CHID, void *PVALUE );
    + +

    Description

    + +

    Read a value from a channel and increment the outstanding request count of a +synchronous group. The ca_sg_array_get functionality is implemented using +ca_array_get_callback.

    + +

    The values written into your program's variables by ca_sg_get should not be +referenced by your program until ECA_NORMAL has been received from ca_sg_block, +or until ca_sg_test returns ECA_IODONE.

    + +

    All remote operation requests such as the above are accumulated (buffered) +and not forwarded to the server until one of ca_flush_io, ca_pend_io, +ca_pend_event, or ca_sg_pend are called. This allows several requests to be +efficiently sent in one message.

    + +

    If a connection is lost and then resumed outstanding gets are not +reissued.

    + +

    Arguments

    +
    +
    GID
    +
    Identifier of the synchronous group.
    +
    +
    +
    TYPE
    +
    External type of returned value. Conversion will occur if this does not + match native type. Specify one from the set of DBR_XXXX in + db_access.h
    +
    +
    +
    COUNT
    +
    Element count to be read from the specified channel. It must match the + array pointed to by PVALUE.
    +
    +
    +
    CHID
    +
    channel identifier
    +
    +
    +
    PVALUE
    +
    Pointer to application supplied buffer that is to contain the value or + array of values to be returned
    +
    + +

    Returns

    + +

    ECA_NORMAL - Normal successful completion

    + +

    ECA_BADSYNCGRP - Invalid synchronous group

    + +

    ECA_BADCHID - Corrupted CHID

    + +

    ECA_BADCOUNT - Requested count larger than native element count

    + +

    ECA_BADTYPE - Invalid DBR_XXXX type

    + +

    ECA_GETFAIL - A local database get failed

    + +

    See Also

    + +

    ca_pend_io()

    + +

    ca_flush_io()

    + +

    ca_get_callback()

    + +

    ca_client_status()

    +
    int ca_client_status ( unsigned level );
    +int ca_context_status ( struct ca_client_context *, 
    +       unsigned level );
    + +

    Description

    + +

    Prints information about the client context including, at higher interest +levels, status for each channel. Lacking a CA context pointer, +ca_client_status() prints information about the calling threads CA context.

    + +

    Arguments

    +
    +
    CONTEXT
    +
    A pointer to the CA context to join with.
    +
    LEVEL
    +
    The interest level. Increasing level produces increasing detail.
    +
    + +

    ca_current_context()

    +
    struct ca_client_context * ca_current_context ();
    + +

    Description

    + +

    Returns a pointer to the current thread's CA context. If none then nil is +returned.

    + +

    See Also

    + +

    ca_attach_context()

    + +

    ca_detach_context()

    + +

    ca_context_create()

    + +

    ca_context_destroy()

    + +

    ca_attach_context()

    +
    int ca_attach_context (struct ca_client_context *CONTEXT);
    + +

    Description

    + +

    The calling thread becomes a member of the specified CA context. If +ca_disable_preemptive_callback is specified when +ca_context_create() is called (or if ca_task_initialize() is called) then +additional threads are not allowed to join the CA context because +allowing other threads to join implies that CA callbacks will be called +preemptively from more than one thread.

    + +

    Arguments

    +
    +
    CONTEXT
    +
    A pointer to the CA context to join with.
    +
    + +

    Returns

    + +

    ECA_ISATTACHED - already attached to a CA context

    + +

    ECA_NOTTHREADED - the specified context is non-preemptive and therefore does +not allow other threads to join

    + +

    ECA_ISATTACHED - the current thread is already attached to a CA context

    + +

    See Also

    + +

    ca_current_context()

    + +

    ca_detach_context()

    + +

    ca_context_create()

    + +

    ca_context_destroy()

    + +

    ca_detach_context()

    +
    void ca_detach_context();
    + +

    Description

    + +

    Detach from any CA context currently attached to the calling thread. This +does not cleanup or shutdown any currently attached CA context (for +that use ca_context_destroy).

    + +

    See Also

    + +

    ca_current_context()

    + +

    ca_attach_context()

    + +

    ca_context_create()

    + +

    ca_context_destroy()

    + +

    ca_dump_dbr()

    +
    void ca_dump_dbr (chtype TYPE, unsigned COUNT, const void * PDBR);
    + +

    Description

    + +

    Dumps the specified dbr data type to standard out.

    + +

    Arguments

    +
    +
    TYPE
    +
    The data type (from the DBR_XXX set described in db_access.h).
    +
    COUNT
    +
    The array element count
    +
    PDBR
    +
    A pointer to data of the specified count and number.
    +
    +
    + +

    Return Codes

    +
    +
    ECA_NORMAL
    +
    Normal successful completion
    +
    ECA_ALLOCMEM
    +
    Unable to allocate additional dynamic memory
    +
    ECA_TOLARGE
    +
    The requested data transfer is greater than available memory or + EPICS_CA_MAX_ARRAY_BYTES
    +
    ECA_BADTYPE
    +
    The data type specified is invalid
    +
    ECA_BADSTR
    +
    Invalid string
    +
    ECA_BADCHID
    +
    Invalid channel identifier
    +
    ECA_BADCOUNT
    +
    Invalid element count requested
    +
    ECA_PUTFAIL
    +
    Channel write request failed
    +
    ECA_GETFAIL
    +
    Channel read request failed
    +
    ECA_ADDFAIL
    +
    unable to install subscription request
    +
    ECA_TIMEOUT
    +
    User specified timeout on IO operation expired
    +
    ECA_EVDISALLOW
    +
    function called was inappropriate for use within a callback + function
    +
    ECA_IODONE
    +
    IO operations have completed
    +
    ECA_IOINPROGRESS
    +
    IO operations are in progress
    +
    ECA_BADSYNCGRP
    +
    Invalid synchronous group identifier
    +
    ECA_NORDACCESS
    +
    Read access denied
    +
    ECA_NOWTACCESS
    +
    Write access denied
    +
    ECA_DISCONN
    +
    Virtual circuit disconnect"
    +
    ECA_DBLCHNL
    +
    Identical process variable name on multiple servers
    +
    ECA_EVDISALLOW
    +
    Request inappropriate within subscription (monitor) update callback
    +
    ECA_BADMONID
    +
    Bad event subscription (monitor) identifier
    +
    ECA_BADMASK
    +
    Invalid event selection mask
    +
    ECA_PUTCBINPROG
    +
    Put callback timed out
    +
    ECA_PUTCBINPROG
    +
    Put callback timed out
    +
    ECA_ANACHRONISM
    +
    Requested feature is no longer supported
    +
    ECA_NOSEARCHADDR
    +
    Empty PV search address list
    +
    ECA_NOCONVERT
    +
    No reasonable data conversion between client and server types
    +
    ECA_BADFUNCPTR
    +
    Invalid function pointer
    +
    ECA_ISATTACHED
    +
    Thread is already attached to a client context
    +
    ECA_UNAVAILINSERV
    +
    Not supported by attached service
    +
    ECA_CHANDESTROY
    +
    User destroyed channel
    +
    ECA_BADPRIORITY
    +
    Invalid channel priority
    +
    ECA_NOTTHREADED
    +
    Preemptive callback not enabled - additional threads may not join + context
    +
    ECA_16KARRAYCLIENT
    +
    Client's protocol revision does not support transfers exceeding 16k + bytes
    +
    + +

    + + diff --git a/src/ca/Makefile b/src/ca/Makefile new file mode 100644 index 000000000..db4186d16 --- /dev/null +++ b/src/ca/Makefile @@ -0,0 +1,106 @@ +#************************************************************************* +# 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 + +HTMLS += CAref.html + +# +# includes to install from this subproject +# +INC += cadef.h +INC += caerr.h +INC += caeventmask.h +INC += caProto.h +INC += db_access.h +INC += addrList.h +INC += cacIO.h +INC += caDiagnostics.h + +LIBSRCS += cac.cpp +LIBSRCS += cacChannel.cpp +LIBSRCS += cacChannelNotify.cpp +LIBSRCS += cacContextNotify.cpp +LIBSRCS += cacReadNotify.cpp +LIBSRCS += cacWriteNotify.cpp +LIBSRCS += cacStateNotify.cpp +LIBSRCS += access.cpp +LIBSRCS += iocinf.cpp +LIBSRCS += convert.cpp +LIBSRCS += test_event.cpp +LIBSRCS += repeater.cpp +LIBSRCS += searchTimer.cpp +LIBSRCS += disconnectGovernorTimer.cpp +LIBSRCS += repeaterSubscribeTimer.cpp +LIBSRCS += baseNMIU.cpp +LIBSRCS += nciu.cpp +LIBSRCS += netiiu.cpp +LIBSRCS += udpiiu.cpp +LIBSRCS += tcpiiu.cpp +LIBSRCS += noopiiu.cpp +LIBSRCS += netReadNotifyIO.cpp +LIBSRCS += netWriteNotifyIO.cpp +LIBSRCS += netSubscription.cpp +LIBSRCS += tcpSendWatchdog.cpp +LIBSRCS += tcpRecvWatchdog.cpp +LIBSRCS += bhe.cpp +LIBSRCS += ca_client_context.cpp +LIBSRCS += oldChannelNotify.cpp +LIBSRCS += oldSubscription.cpp +LIBSRCS += getCallback.cpp +LIBSRCS += getCopy.cpp +LIBSRCS += putCallback.cpp +LIBSRCS += syncgrp.cpp +LIBSRCS += CASG.cpp +LIBSRCS += syncGroupNotify.cpp +LIBSRCS += syncGroupReadNotify.cpp +LIBSRCS += syncGroupWriteNotify.cpp +LIBSRCS += localHostName.cpp +LIBSRCS += comQueRecv.cpp +LIBSRCS += comQueSend.cpp +LIBSRCS += comBuf.cpp +LIBSRCS += hostNameCache.cpp +LIBSRCS += msgForMultiplyDefinedPV.cpp +LIBSRCS_vxWorks += templateInstances.cpp + +LIBRARY=ca + +ca_RCS = ca.rc + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=ca +OBJLIB_SRCS = $(LIBSRCS) +endif + +ca_LIBS = Com + +ca_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 + +# libs needed for PROD and TESTPRODUCT +PROD_LIBS = ca Com +# needed when its an object library build +PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 + +PROD_HOST += caRepeater catime acctst caConnTest casw caEventRate +OBJS_IOC_vxWorks += catime acctst caConnTest casw caEventRate +caRepeater_SRCS = caRepeater.cpp +catime_SRCS = catimeMain.c catime.c +acctst_SRCS = acctstMain.c acctst.c +caEventRate_SRCS = caEventRateMain.cpp caEventRate.cpp +casw_SRCS = casw.cpp +caConnTest_SRCS = caConnTestMain.cpp caConnTest.cpp + +casw_SYS_LIBS_solaris = socket + +include $(TOP)/configure/RULES + diff --git a/src/ca/SearchDest.h b/src/ca/SearchDest.h new file mode 100755 index 000000000..c22be7c87 --- /dev/null +++ b/src/ca/SearchDest.h @@ -0,0 +1,39 @@ +/*************************************************************************\ + * 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. +\*************************************************************************/ + +#ifndef SearchDest_h +#define SearchDest_h + +#include +#include +#include +#include "caProto.h" + +class channelNode; +class epicsMutex; +template < class T > class epicsGuard; + +struct SearchDest : + public tsDLNode < SearchDest > { + virtual ~SearchDest () {}; + struct Callback { + virtual ~Callback () {}; + virtual void notify ( + const caHdr & msg, const void * pPayload, + const osiSockAddr & addr, const epicsTime & ) = 0; + virtual void show ( + epicsGuard < epicsMutex > &, unsigned level ) const = 0; + }; + virtual void searchRequest ( epicsGuard < epicsMutex > &, + const char * pbuf, size_t len ) = 0; + virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; +}; + +#endif // SearchDest_h diff --git a/src/ca/access.cpp b/src/ca/access.cpp new file mode 100644 index 000000000..85fe2b205 --- /dev/null +++ b/src/ca/access.cpp @@ -0,0 +1,1098 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeffrey O. Hill + * + */ + +#include +#include + +#include "dbDefs.h" +#include "epicsExit.h" + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + + +/* + * allocate error message string array + * here so I can use sizeof + */ +#define CA_ERROR_GLBLSOURCE + +/* + * allocate header version strings here + */ +#define CAC_VERSION_GLOBAL + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" +#include "cac.h" + +epicsThreadPrivateId caClientContextId; + +const char * ca_message_text [] += +{ +"Normal successful completion", +"Maximum simultaneous IOC connections exceeded", +"Unknown internet host", +"Unknown internet service", +"Unable to allocate a new socket", + +"Unable to connect to internet host or service", +"Unable to allocate additional dynamic memory", +"Unknown IO channel", +"Record field specified inappropriate for channel specified", +"The requested data transfer is greater than available memory or EPICS_CA_MAX_ARRAY_BYTES", + +"User specified timeout on IO operation expired", +"Sorry, that feature is planned but not supported at this time", +"The supplied string is unusually large", +"The request was ignored because the specified channel is disconnected", +"The data type specifed is invalid", + +"Remote Channel not found", +"Unable to locate all user specified channels", +"Channel Access Internal Failure", +"The requested local DB operation failed", +"Channel read request failed", + +"Channel write request failed", +"Channel subscription request failed", +"Invalid element count requested", +"Invalid string", +"Virtual circuit disconnect", + +"Identical process variable names on multiple servers", +"Request inappropriate within subscription (monitor) update callback", +"Database value get for that channel failed during channel search", +"Unable to initialize without the vxWorks VX_FP_TASK task option set", +"Event queue overflow has prevented first pass event after event add", + +"Bad event subscription (monitor) identifier", +"Remote channel has new network address", +"New or resumed network connection", +"Specified task isnt a member of a CA context", +"Attempt to use defunct CA feature failed", + +"The supplied string is empty", +"Unable to spawn the CA repeater thread- auto reconnect will fail", +"No channel id match for search reply- search reply ignored", +"Reseting dead connection- will try to reconnect", +"Server (IOC) has fallen behind or is not responding- still waiting", + +"No internet interface with broadcast available", +"Invalid event selection mask", +"IO operations have completed", +"IO operations are in progress", +"Invalid synchronous group identifier", + +"Put callback timed out", +"Read access denied", +"Write access denied", +"Requested feature is no longer supported", +"Empty PV search address list", + +"No reasonable data conversion between client and server types", +"Invalid channel identifier", +"Invalid function pointer", +"Thread is already attached to a client context", +"Not supported by attached service", + +"User destroyed channel", +"Invalid channel priority", +"Preemptive callback not enabled - additional threads may not join context", +"Client's protocol revision does not support transfers exceeding 16k bytes", +"Virtual circuit connection sequence aborted", + +"Virtual circuit unresponsive" +}; + +static epicsThreadOnceId caClientContextIdOnce = EPICS_THREAD_ONCE_INIT; + +extern "C" void ca_client_exit_handler (void *) +{ + if ( caClientContextId ) { + epicsThreadPrivateDelete ( caClientContextId ); + caClientContextId = 0; + } +} + +// runs once only for each process +extern "C" void ca_init_client_context ( void * ) +{ + caClientContextId = epicsThreadPrivateCreate (); + if ( caClientContextId ) { + epicsAtExit ( ca_client_exit_handler,0 ); + } +} + +/* + * fetchClientContext (); + */ +int fetchClientContext ( ca_client_context **ppcac ) +{ + epicsThreadOnce ( &caClientContextIdOnce, ca_init_client_context, 0 ); + if ( caClientContextId == 0 ) { + return ECA_ALLOCMEM; + } + + int status; + *ppcac = ( ca_client_context * ) epicsThreadPrivateGet ( caClientContextId ); + if ( *ppcac ) { + status = ECA_NORMAL; + } + else { + status = ca_task_initialize (); + if ( status == ECA_NORMAL ) { + *ppcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); + if ( ! *ppcac ) { + status = ECA_INTERNAL; + } + } + } + + return status; +} + +/* + * ca_task_initialize () + */ +// extern "C" +int epicsShareAPI ca_task_initialize ( void ) +{ + return ca_context_create ( ca_disable_preemptive_callback ); +} + +// extern "C" +int epicsShareAPI ca_context_create ( + ca_preemptive_callback_select premptiveCallbackSelect ) +{ + ca_client_context *pcac; + + try { + epicsThreadOnce ( & caClientContextIdOnce, ca_init_client_context, 0); + if ( caClientContextId == 0 ) { + return ECA_ALLOCMEM; + } + + pcac = ( ca_client_context * ) epicsThreadPrivateGet ( caClientContextId ); + if ( pcac ) { + if ( premptiveCallbackSelect == ca_enable_preemptive_callback && + ! pcac->preemptiveCallbakIsEnabled() ) { + return ECA_NOTTHREADED; + } + return ECA_NORMAL; + } + + pcac = new ca_client_context ( + premptiveCallbackSelect == ca_enable_preemptive_callback ); + if ( ! pcac ) { + return ECA_ALLOCMEM; + } + + epicsThreadPrivateSet ( caClientContextId, (void *) pcac ); + } + catch ( ... ) { + return ECA_ALLOCMEM; + } + return ECA_NORMAL; +} + +// +// ca_modify_host_name () +// +// defunct +// +// extern "C" +int epicsShareAPI ca_modify_host_name ( const char * ) +{ + return ECA_NORMAL; +} + +// +// ca_modify_user_name() +// +// defunct +// +// extern "C" +int epicsShareAPI ca_modify_user_name ( const char * ) +{ + return ECA_NORMAL; +} + +// +// ca_context_destroy () +// +// extern "C" +void epicsShareAPI ca_context_destroy () +{ + ca_client_context *pcac; + + if ( caClientContextId != NULL ) { + pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); + if ( pcac ) { + delete pcac; + epicsThreadPrivateSet ( caClientContextId, 0 ); + } + } +} + +/* + * ca_task_exit() + * + * releases all resources alloc to a channel access client + */ +// extern "C" +int epicsShareAPI ca_task_exit () +{ + ca_context_destroy (); + return ECA_NORMAL; +} + +/* + * + * CA_BUILD_AND_CONNECT + * + * backwards compatible entry point to ca_search_and_connect() + */ +// extern "C" +int epicsShareAPI ca_build_and_connect ( const char *name_str, chtype get_type, + arrayElementCount get_count, chid * chan, void *pvalue, + caCh *conn_func, void *puser ) +{ + if ( get_type != TYPENOTCONN && pvalue != 0 && get_count != 0 ) { + return ECA_ANACHRONISM; + } + + return ca_search_and_connect ( name_str, chan, conn_func, puser ); +} + +/* + * ca_search_and_connect() + */ +// extern "C" +int epicsShareAPI ca_search_and_connect ( + const char * name_str, chid * chanptr, + caCh * conn_func, void * puser ) +{ + return ca_create_channel ( name_str, conn_func, + puser, CA_PRIORITY_DEFAULT, chanptr ); +} + +// extern "C" +int epicsShareAPI ca_create_channel ( + const char * name_str, caCh * conn_func, void * puser, + capri priority, chid * chanptr ) +{ + ca_client_context * pcac; + int caStatus = fetchClientContext ( & pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + { + CAFDHANDLER * pFunc = 0; + void * pArg = 0; + { + epicsGuard < epicsMutex > + guard ( pcac->mutex ); + if ( pcac->fdRegFuncNeedsToBeCalled ) { + pFunc = pcac->fdRegFunc; + pArg = pcac->fdRegArg; + pcac->fdRegFuncNeedsToBeCalled = false; + } + } + if ( pFunc ) { + ( *pFunc ) ( pArg, pcac->sock, true ); + } + } + + try { + epicsGuard < epicsMutex > guard ( pcac->mutex ); + oldChannelNotify * pChanNotify = + new ( pcac->oldChannelNotifyFreeList ) + oldChannelNotify ( guard, *pcac, name_str, + conn_func, puser, priority ); + // make sure that their chan pointer is set prior to + // calling connection call backs + *chanptr = pChanNotify; + pChanNotify->initiateConnect ( guard ); + // no need to worry about a connect preempting here because + // the connect sequence will not start untill initiateConnect() + // is called + } + catch ( cacChannel::badString & ) { + return ECA_BADSTR; + } + catch ( std::bad_alloc & ) { + return ECA_ALLOCMEM; + } + catch ( cacChannel::badPriority & ) { + return ECA_BADPRIORITY; + } + catch ( cacChannel::unsupportedByService & ) { + return ECA_UNAVAILINSERV; + } + catch ( std :: exception & except ) { + pcac->printFormated ( + "ca_create_channel: " + "unexpected exception was \"%s\"", + except.what () ); + return ECA_INTERNAL; + } + catch ( ... ) { + return ECA_INTERNAL; + } + + return ECA_NORMAL; +} + +/* + * ca_clear_channel () + * + * a known defect here is that there will be a + * crash if they destroy the channel after destroying + * its context + */ +// extern "C" +int epicsShareAPI ca_clear_channel ( chid pChan ) +{ + ca_client_context & cac = pChan->getClientCtx (); + epicsGuard < epicsMutex > guard ( cac.mutex ); + try { + pChan->eliminateExcessiveSendBacklog ( guard ); + } + catch ( cacChannel::notConnected & ) { + // intentionally ignored + } + pChan->destructor ( guard ); + cac.oldChannelNotifyFreeList.release ( pChan ); + return ECA_NORMAL; +} + +/* + * Specify an event subroutine to be run for asynch exceptions + */ +// extern "C" +int epicsShareAPI ca_add_exception_event ( caExceptionHandler *pfunc, void *arg ) +{ + ca_client_context *pcac; + int caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + pcac->changeExceptionEvent ( pfunc, arg ); + + return ECA_NORMAL; +} + +/* + * ca_add_masked_array_event + */ +int epicsShareAPI ca_add_masked_array_event ( + chtype type, arrayElementCount count, chid pChan, + caEventCallBackFunc *pCallBack, void *pCallBackArg, + ca_real, ca_real, ca_real, + evid *monixptr, long mask ) +{ + return ca_create_subscription ( type, count, pChan, mask, + pCallBack, pCallBackArg, monixptr ); +} + +/* + * ca_clear_event () + */ +int epicsShareAPI ca_clear_event ( evid pMon ) +{ + return ca_clear_subscription ( pMon ); +} + +// extern "C" +chid epicsShareAPI ca_evid_to_chid ( evid pMon ) +{ + return & pMon->channel (); +} + +// extern "C" +int epicsShareAPI ca_pend ( ca_real timeout, int early ) // X aCC 361 +{ + if ( early ) { + return ca_pend_io ( timeout ); + } + else { + return ca_pend_event ( timeout ); + } +} + +/* + * ca_pend_event () + */ +// extern "C" +int epicsShareAPI ca_pend_event ( ca_real timeout ) +{ + ca_client_context *pcac; + int status = fetchClientContext ( &pcac ); + if ( status != ECA_NORMAL ) { + return status; + } + + try { + // preserve past odd ball behavior of waiting forever when + // the delay is zero + if ( timeout == 0.0 ) { + while ( true ) { + pcac->pendEvent ( 60.0 ); + } + } + return pcac->pendEvent ( timeout ); + } + catch ( ... ) { + return ECA_INTERNAL; + } +} + +/* + * ca_pend_io () + */ +// extern "C" +int epicsShareAPI ca_pend_io ( ca_real timeout ) +{ + ca_client_context *pcac; + int status = fetchClientContext ( &pcac ); + if ( status != ECA_NORMAL ) { + return status; + } + + try { + // preserve past odd ball behavior of waiting forever when + // the delay is zero + if ( timeout == 0.0 ) { + return pcac->pendIO ( DBL_MAX ); + } + + return pcac->pendIO ( timeout ); + } + catch ( ... ) { + return ECA_INTERNAL; + } +} + +/* + * ca_flush_io () + */ +int epicsShareAPI ca_flush_io () +{ + ca_client_context * pcac; + int caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + epicsGuard < epicsMutex > guard ( pcac->mutex ); + pcac->flush ( guard ); + + return ECA_NORMAL; +} + +/* + * CA_TEST_IO () + */ +int epicsShareAPI ca_test_io () // X aCC 361 +{ + ca_client_context *pcac; + int caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + if ( pcac->ioComplete () ) { + return ECA_IODONE; + } + else{ + return ECA_IOINPROGRESS; + } +} + +/* + * CA_SIGNAL() + */ +// extern "C" +void epicsShareAPI ca_signal ( long ca_status, const char *message ) +{ + ca_signal_with_file_and_lineno ( ca_status, message, NULL, 0 ); +} + +/* + * ca_message (long ca_status) + * + * - if it is an unknown error code then it possible + * that the error string generated below + * will be overwritten before (or while) the caller + * of this routine is calling this routine + * (if they call this routine again). + */ +// extern "C" +const char * epicsShareAPI ca_message ( long ca_status ) // X aCC 361 +{ + unsigned msgNo = CA_EXTRACT_MSG_NO ( ca_status ); + + if ( msgNo < NELEMENTS (ca_message_text) ) { + return ca_message_text[msgNo]; + } + else { + return "new CA message number known only by server - see caerr.h"; + } +} + +/* + * ca_signal_with_file_and_lineno() + */ +// extern "C" +void epicsShareAPI ca_signal_with_file_and_lineno ( long ca_status, + const char *message, const char *pfilenm, int lineno ) +{ + ca_signal_formated ( ca_status, pfilenm, lineno, message ); +} + +/* + * ca_signal_formated() + */ +// extern "C" +void epicsShareAPI ca_signal_formated ( long ca_status, const char *pfilenm, + int lineno, const char *pFormat, ... ) +{ + ca_client_context *pcac; + + if ( caClientContextId ) { + pcac = ( ca_client_context * ) epicsThreadPrivateGet ( caClientContextId ); + } + else { + pcac = 0; + } + + va_list theArgs; + va_start ( theArgs, pFormat ); + if ( pcac ) { + pcac->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs ); + } + else { + fprintf ( stderr, "CA exception in thread w/o CA ctx: status=%s file=%s line=%d: \n", + ca_message ( ca_status ), pfilenm, lineno ); + if ( pFormat ) { + vfprintf ( stderr, pFormat, theArgs ); + } + } + va_end ( theArgs ); +} + +/* + * CA_ADD_FD_REGISTRATION + * + * call their function with their argument whenever + * a new fd is added or removed + * (for a manager of the select system call under UNIX) + * + */ +// extern "C" +int epicsShareAPI ca_add_fd_registration ( CAFDHANDLER * func, void * arg ) +{ + ca_client_context *pcac; + int caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + pcac->registerForFileDescriptorCallBack ( func, arg ); + + return ECA_NORMAL; +} + +/* + * ca_version() + * function that returns the CA version string + */ +// extern "C" +const char * epicsShareAPI ca_version () +{ + return CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ); +} + +/* + * ca_replace_printf_handler () + */ +// extern "C" +int epicsShareAPI ca_replace_printf_handler ( caPrintfFunc *ca_printf_func ) +{ + ca_client_context *pcac; + int caStatus = fetchClientContext (&pcac); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + pcac->replaceErrLogHandler ( ca_printf_func ); + + return ECA_NORMAL; +} + +/* + * ca_get_ioc_connection_count() + * + * returns the number of IOC's that CA is connected to + * (for testing purposes only) + */ +// extern "C" +unsigned epicsShareAPI ca_get_ioc_connection_count () +{ + ca_client_context * pcac; + int caStatus = fetchClientContext ( & pcac ); + if ( caStatus != ECA_NORMAL ) { + return 0u; + } + + return pcac->circuitCount (); +} + +unsigned epicsShareAPI ca_beacon_anomaly_count () +{ + ca_client_context * pcac; + int caStatus = fetchClientContext ( & pcac ); + if ( caStatus != ECA_NORMAL ) { + return 0u; + } + + return pcac->beaconAnomaliesSinceProgramStart (); +} + +// extern "C" +int epicsShareAPI ca_channel_status ( epicsThreadId /* tid */ ) +{ + ::printf ("The R3.14 EPICS OS abstraction API does not allow peeking at thread private storage of another thread.\n"); + ::printf ("Please call \"ca_client_status ( unsigned level )\" from the subsystem specific diagnostic code.\n"); + return ECA_ANACHRONISM; +} + +// extern "C" +int epicsShareAPI ca_client_status ( unsigned level ) +{ + ca_client_context *pcac; + int caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + pcac->show ( level ); + return ECA_NORMAL; +} + +int epicsShareAPI ca_context_status ( ca_client_context * pcac, unsigned level ) +{ + pcac->show ( level ); + return ECA_NORMAL; +} + +/* + * ca_current_context () + * + * used when an auxillary thread needs to join a CA client context started + * by another thread + */ +// extern "C" +struct ca_client_context * epicsShareAPI ca_current_context () +{ + struct ca_client_context *pCtx; + if ( caClientContextId ) { + pCtx = ( struct ca_client_context * ) + epicsThreadPrivateGet ( caClientContextId ); + } + else { + pCtx = 0; + } + return pCtx; +} + +/* + * ca_attach_context () + * + * used when an auxillary thread needs to join a CA client context started + * by another thread + */ +// extern "C" +int epicsShareAPI ca_attach_context ( struct ca_client_context * pCtx ) +{ + ca_client_context *pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); + if ( pcac && pCtx != 0 ) { + return ECA_ISATTACHED; + } + if ( ! pCtx->preemptiveCallbakIsEnabled() ) { + return ECA_NOTTHREADED; + } + epicsThreadPrivateSet ( caClientContextId, pCtx ); + return ECA_NORMAL; +} + +void epicsShareAPI ca_detach_context () +{ + if ( caClientContextId ) { + epicsThreadPrivateSet ( caClientContextId, 0 ); + } +} + +int epicsShareAPI ca_preemtive_callback_is_enabled () +{ + ca_client_context *pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); + if ( ! pcac ) { + return 0; + } + return pcac->preemptiveCallbakIsEnabled (); +} + + +// extern "C" +void epicsShareAPI ca_self_test () +{ + ca_client_context *pcac = (ca_client_context *) epicsThreadPrivateGet ( caClientContextId ); + if ( ! pcac ) { + return; + } + pcac->selfTest (); +} + +// extern "C" +epicsShareDef const int epicsTypeToDBR_XXXX [lastEpicsType+1] = { + DBR_SHORT, /* forces conversion fronm uint8 to int16 */ + DBR_CHAR, + DBR_SHORT, + DBR_LONG, /* forces conversion fronm uint16 to int32 */ + DBR_ENUM, + DBR_LONG, + DBR_LONG, /* very large unsigned number will not map */ + DBR_FLOAT, + DBR_DOUBLE, + DBR_STRING, + DBR_STRING +}; + +// extern "C" +epicsShareDef const epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1] = { + epicsOldStringT, + epicsInt16T, + epicsFloat32T, + epicsEnum16T, + epicsUInt8T, + epicsInt32T, + epicsFloat64T, + + epicsOldStringT, + epicsInt16T, + epicsFloat32T, + epicsEnum16T, + epicsUInt8T, + epicsInt32T, + epicsFloat64T, + + epicsOldStringT, + epicsInt16T, + epicsFloat32T, + epicsEnum16T, + epicsUInt8T, + epicsInt32T, + epicsFloat64T, + + epicsOldStringT, + epicsInt16T, + epicsFloat32T, + epicsEnum16T, + epicsUInt8T, + epicsInt32T, + epicsFloat64T, + + epicsOldStringT, + epicsInt16T, + epicsFloat32T, + epicsEnum16T, + epicsUInt8T, + epicsInt32T, + epicsFloat64T, + + epicsUInt16T, + epicsUInt16T, + epicsOldStringT, + epicsOldStringT +}; + +// extern "C" +epicsShareDef const unsigned short dbr_size[LAST_BUFFER_TYPE+1] = { + sizeof(dbr_string_t), /* string max size */ + sizeof(dbr_short_t), /* short */ + sizeof(dbr_float_t), /* IEEE Float */ + sizeof(dbr_enum_t), /* item number */ + sizeof(dbr_char_t), /* character */ + + sizeof(dbr_long_t), /* long */ + sizeof(dbr_double_t), /* double */ + sizeof(struct dbr_sts_string), /* string field with status */ + sizeof(struct dbr_sts_short), /* short field with status */ + sizeof(struct dbr_sts_float), /* float field with status */ + + sizeof(struct dbr_sts_enum), /* item number with status */ + sizeof(struct dbr_sts_char), /* char field with status */ + sizeof(struct dbr_sts_long), /* long field with status */ + sizeof(struct dbr_sts_double), /* double field with time */ + sizeof(struct dbr_time_string), /* string field with time */ + + sizeof(struct dbr_time_short), /* short field with time */ + sizeof(struct dbr_time_float), /* float field with time */ + sizeof(struct dbr_time_enum), /* item number with time */ + sizeof(struct dbr_time_char), /* char field with time */ + sizeof(struct dbr_time_long), /* long field with time */ + + sizeof(struct dbr_time_double), /* double field with time */ + sizeof(struct dbr_sts_string), /* graphic string info */ + sizeof(struct dbr_gr_short), /* graphic short info */ + sizeof(struct dbr_gr_float), /* graphic float info */ + sizeof(struct dbr_gr_enum), /* graphic item info */ + + sizeof(struct dbr_gr_char), /* graphic char info */ + sizeof(struct dbr_gr_long), /* graphic long info */ + sizeof(struct dbr_gr_double), /* graphic double info */ + sizeof(struct dbr_sts_string), /* control string info */ + sizeof(struct dbr_ctrl_short), /* control short info */ + + sizeof(struct dbr_ctrl_float), /* control float info */ + sizeof(struct dbr_ctrl_enum), /* control item info */ + sizeof(struct dbr_ctrl_char), /* control char info */ + sizeof(struct dbr_ctrl_long), /* control long info */ + sizeof(struct dbr_ctrl_double), /* control double info */ + + sizeof(dbr_put_ackt_t), /* put ackt */ + sizeof(dbr_put_acks_t), /* put acks */ + sizeof(struct dbr_stsack_string),/* string field with status/ack*/ + sizeof(dbr_string_t), /* string max size */ +}; + +// extern "C" +epicsShareDef const unsigned short dbr_value_size[LAST_BUFFER_TYPE+1] = { + sizeof(dbr_string_t), /* string max size */ + sizeof(dbr_short_t), /* short */ + sizeof(dbr_float_t), /* IEEE Float */ + sizeof(dbr_enum_t), /* item number */ + sizeof(dbr_char_t), /* character */ + + sizeof(dbr_long_t), /* long */ + sizeof(dbr_double_t), /* double */ + sizeof(dbr_string_t), /* string max size */ + sizeof(dbr_short_t), /* short */ + sizeof(dbr_float_t), /* IEEE Float */ + + sizeof(dbr_enum_t), /* item number */ + sizeof(dbr_char_t), /* character */ + sizeof(dbr_long_t), /* long */ + sizeof(dbr_double_t), /* double */ + sizeof(dbr_string_t), /* string max size */ + + sizeof(dbr_short_t), /* short */ + sizeof(dbr_float_t), /* IEEE Float */ + sizeof(dbr_enum_t), /* item number */ + sizeof(dbr_char_t), /* character */ + sizeof(dbr_long_t), /* long */ + + sizeof(dbr_double_t), /* double */ + sizeof(dbr_string_t), /* string max size */ + sizeof(dbr_short_t), /* short */ + sizeof(dbr_float_t), /* IEEE Float */ + sizeof(dbr_enum_t), /* item number */ + + sizeof(dbr_char_t), /* character */ + sizeof(dbr_long_t), /* long */ + sizeof(dbr_double_t), /* double */ + sizeof(dbr_string_t), /* string max size */ + sizeof(dbr_short_t), /* short */ + + sizeof(dbr_float_t), /* IEEE Float */ + sizeof(dbr_enum_t), /* item number */ + sizeof(dbr_char_t), /* character */ + sizeof(dbr_long_t), /* long */ + sizeof(dbr_double_t), /* double */ + + sizeof(dbr_ushort_t), /* put_ackt */ + sizeof(dbr_ushort_t), /* put_acks */ + sizeof(dbr_string_t), /* string max size */ + sizeof(dbr_string_t), /* string max size */ +}; + +//extern "C" +epicsShareDef const enum dbr_value_class dbr_value_class[LAST_BUFFER_TYPE+1] = { + dbr_class_string, /* string max size */ + dbr_class_int, /* short */ + dbr_class_float, /* IEEE Float */ + dbr_class_int, /* item number */ + dbr_class_int, /* character */ + dbr_class_int, /* long */ + dbr_class_float, /* double */ + + dbr_class_string, /* string max size */ + dbr_class_int, /* short */ + dbr_class_float, /* IEEE Float */ + dbr_class_int, /* item number */ + dbr_class_int, /* character */ + dbr_class_int, /* long */ + dbr_class_float, /* double */ + + dbr_class_string, /* string max size */ + dbr_class_int, /* short */ + dbr_class_float, /* IEEE Float */ + dbr_class_int, /* item number */ + dbr_class_int, /* character */ + dbr_class_int, /* long */ + dbr_class_float, /* double */ + + dbr_class_string, /* string max size */ + dbr_class_int, /* short */ + dbr_class_float, /* IEEE Float */ + dbr_class_int, /* item number */ + dbr_class_int, /* character */ + dbr_class_int, /* long */ + dbr_class_float, /* double */ + + dbr_class_string, /* string max size */ + dbr_class_int, /* short */ + dbr_class_float, /* IEEE Float */ + dbr_class_int, /* item number */ + dbr_class_int, /* character */ + dbr_class_int, /* long */ + dbr_class_float, /* double */ + dbr_class_int, + dbr_class_int, + dbr_class_string, + dbr_class_string, /* string max size */ +}; + +// extern "C" +epicsShareDef const unsigned short dbr_value_offset[LAST_BUFFER_TYPE+1] = { + 0, /* string */ + 0, /* short */ + 0, /* IEEE Float */ + 0, /* item number */ + 0, /* character */ + 0, /* long */ + 0, /* IEEE double */ + (unsigned short) offsetof(dbr_sts_string,value[0]),/* string field with status */ + (unsigned short) offsetof(dbr_sts_short,value), /* short field with status */ + (unsigned short) offsetof(dbr_sts_float,value), /* float field with status */ + (unsigned short) offsetof(dbr_sts_enum,value), /* item number with status */ + (unsigned short) offsetof(dbr_sts_char,value), /* char field with status */ + (unsigned short) offsetof(dbr_sts_long,value), /* long field with status */ + (unsigned short) offsetof(dbr_sts_double,value), /* double field with time */ + (unsigned short) offsetof(dbr_time_string,value[0] ),/* string field with time */ + (unsigned short) offsetof(dbr_time_short,value), /* short field with time */ + (unsigned short) offsetof(dbr_time_float,value), /* float field with time */ + (unsigned short) offsetof(dbr_time_enum,value), /* item number with time */ + (unsigned short) offsetof(dbr_time_char,value), /* char field with time */ + (unsigned short) offsetof(dbr_time_long,value), /* long field with time */ + (unsigned short) offsetof(dbr_time_double,value), /* double field with time */ + (unsigned short) offsetof(dbr_sts_string,value[0]),/* graphic string info */ + (unsigned short) offsetof(dbr_gr_short,value), /* graphic short info */ + (unsigned short) offsetof(dbr_gr_float,value), /* graphic float info */ + (unsigned short) offsetof(dbr_gr_enum,value), /* graphic item info */ + (unsigned short) offsetof(dbr_gr_char,value), /* graphic char info */ + (unsigned short) offsetof(dbr_gr_long,value), /* graphic long info */ + (unsigned short) offsetof(dbr_gr_double,value), /* graphic double info */ + (unsigned short) offsetof(dbr_sts_string,value[0]),/* control string info */ + (unsigned short) offsetof(dbr_ctrl_short,value), /* control short info */ + (unsigned short) offsetof(dbr_ctrl_float,value), /* control float info */ + (unsigned short) offsetof(dbr_ctrl_enum,value), /* control item info */ + (unsigned short) offsetof(dbr_ctrl_char,value), /* control char info */ + (unsigned short) offsetof(dbr_ctrl_long,value), /* control long info */ + (unsigned short) offsetof(dbr_ctrl_double,value), /* control double info */ + 0, /* put ackt */ + 0, /* put acks */ + (unsigned short) offsetof(dbr_stsack_string,value[0]),/* string field with status */ + 0, /* string */ +}; + +// extern "C" +epicsShareDef const char *dbf_text[LAST_TYPE+3] = { + "TYPENOTCONN", + "DBF_STRING", + "DBF_SHORT", + "DBF_FLOAT", + "DBF_ENUM", + "DBF_CHAR", + "DBF_LONG", + "DBF_DOUBLE", + "DBF_NO_ACCESS" +}; + +// extern "C" +epicsShareDef const char *dbf_text_invalid = "DBF_invalid"; + +// extern "C" +epicsShareDef const short dbf_text_dim = (sizeof dbf_text)/(sizeof (char *)); + +// extern "C" +epicsShareDef const char *dbr_text[LAST_BUFFER_TYPE+1] = { + "DBR_STRING", + "DBR_SHORT", + "DBR_FLOAT", + "DBR_ENUM", + "DBR_CHAR", + "DBR_LONG", + "DBR_DOUBLE", + "DBR_STS_STRING", + "DBR_STS_SHORT", + "DBR_STS_FLOAT", + "DBR_STS_ENUM", + "DBR_STS_CHAR", + "DBR_STS_LONG", + "DBR_STS_DOUBLE", + "DBR_TIME_STRING", + "DBR_TIME_SHORT", + "DBR_TIME_FLOAT", + "DBR_TIME_ENUM", + "DBR_TIME_CHAR", + "DBR_TIME_LONG", + "DBR_TIME_DOUBLE", + "DBR_GR_STRING", + "DBR_GR_SHORT", + "DBR_GR_FLOAT", + "DBR_GR_ENUM", + "DBR_GR_CHAR", + "DBR_GR_LONG", + "DBR_GR_DOUBLE", + "DBR_CTRL_STRING", + "DBR_CTRL_SHORT", + "DBR_CTRL_FLOAT", + "DBR_CTRL_ENUM", + "DBR_CTRL_CHAR", + "DBR_CTRL_LONG", + "DBR_CTRL_DOUBLE", + "DBR_PUT_ACKT", + "DBR_PUT_ACKS", + "DBR_STSACK_STRING", + "DBR_CLASS_NAME" +}; + +// extern "C" +epicsShareDef const char *dbr_text_invalid = "DBR_invalid"; + +// extern "C" +epicsShareDef const short dbr_text_dim = (sizeof dbr_text) / (sizeof (char *)) + 1; diff --git a/src/ca/acctst.c b/src/ca/acctst.c new file mode 100644 index 000000000..9c4840996 --- /dev/null +++ b/src/ca/acctst.c @@ -0,0 +1,3193 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * CA regression test + */ + +/* + * ANSI + */ +#include +#include +#include +#include +#include + +/* + * EPICS + */ +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#include "epicsAssert.h" +#include "epicsTime.h" +#include "dbDefs.h" +#include "envDefs.h" +#include "caDiagnostics.h" +#include "cadef.h" +#include "fdmgr.h" +#include "epicsExit.h" + +typedef struct appChan { + char name[64]; + chid channel; + evid subscription; + unsigned char connected; + unsigned char accessRightsHandlerInstalled; + unsigned subscriptionUpdateCount; + unsigned accessUpdateCount; + unsigned connectionUpdateCount; + unsigned getCallbackCount; +} appChan; + +unsigned subscriptionUpdateCount; +unsigned accessUpdateCount; +unsigned connectionUpdateCount; +unsigned getCallbackCount; + +static epicsTimeStamp showProgressBeginTime; + +static const double timeoutToPendIO = 1e20; + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +void showProgressBegin ( const char *pTestName, unsigned interestLevel ) +{ + + if ( interestLevel > 0 ) { + if ( interestLevel > 1 ) { + printf ( "%s ", pTestName ); + epicsTimeGetCurrent ( & showProgressBeginTime ); + } + printf ( "{" ); + } + fflush ( stdout ); +} + +void showProgressEnd ( unsigned interestLevel ) +{ + if ( interestLevel > 0 ) { + printf ( "}" ); + if ( interestLevel > 1 ) { + epicsTimeStamp showProgressEndTime; + double delay; + epicsTimeGetCurrent ( & showProgressEndTime ); + delay = epicsTimeDiffInSeconds ( &showProgressEndTime, &showProgressBeginTime ); + printf ( " %f sec\n", delay ); + } + else { + fflush ( stdout ); + } + } +} + +void showProgress ( unsigned interestLevel ) +{ + if ( interestLevel > 0 ) { + printf ( "." ); + fflush ( stdout ); + } +} + +void nUpdatesTester ( struct event_handler_args args ) +{ + if ( args.status == ECA_NORMAL ) { + unsigned *pCtr = (unsigned *) args.usr; + ( *pCtr ) ++; + } + else { + printf ( "subscription update failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } +} + +void monitorSubscriptionFirstUpdateTest ( const char *pName, chid chan, unsigned interestLevel ) +{ + int status; + struct dbr_ctrl_double currentVal; + double delta; + unsigned eventCount = 0u; + unsigned waitCount = 0u; + evid id; + chid chan2; + + showProgressBegin ( "monitorSubscriptionFirstUpdateTest", interestLevel ); + + /* + * verify that the first event arrives (with evid) + * and channel connected + */ + status = ca_add_event ( DBR_FLOAT, + chan, nUpdatesTester, &eventCount, &id ); + SEVCHK ( status, 0 ); + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + while ( eventCount < 1 && waitCount++ < 100 ) { + printf ( "e" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + verify ( eventCount > 0 ); + + /* clear any knowledge of old gets */ + ca_pend_io ( 1e-5 ); + + /* verify that a ca_put() produces an update, but */ + /* this may fail if there is an unusual deadband */ + status = ca_get ( DBR_CTRL_DOUBLE, chan, ¤tVal ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, NULL ); + eventCount = 0u; + waitCount = 0u; + delta = ( currentVal.upper_ctrl_limit - currentVal.lower_ctrl_limit ) / 4.0; + if ( delta <= 0.0 ) { + delta = 100.0; + } + if ( currentVal.value + delta < currentVal.upper_ctrl_limit ) { + currentVal.value += delta; + } + else { + currentVal.value -= delta; + } + status = ca_put ( DBR_DOUBLE, chan, ¤tVal.value ); + SEVCHK ( status, NULL ); + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + while ( eventCount < 1 && waitCount++ < 100 ) { + printf ( "p" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + verify ( eventCount > 0 ); + + status = ca_clear_event ( id ); + SEVCHK (status, 0); + + /* + * verify that the first event arrives (w/o evid) + * and when channel initially disconnected + */ + eventCount = 0u; + waitCount = 0u; + status = ca_search ( pName, &chan2 ); + SEVCHK ( status, 0 ); + status = ca_add_event ( DBR_FLOAT, chan2, + nUpdatesTester, &eventCount, 0 ); + SEVCHK ( status, 0 ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK (status, 0); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( eventCount < 1 && waitCount++ < 100 ) { + printf ( "w" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + verify ( eventCount > 0 ); + + /* verify that a ca_put() produces an update, but */ + /* this may fail if there is an unusual deadband */ + status = ca_get ( DBR_CTRL_DOUBLE, chan2, ¤tVal ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, NULL ); + eventCount = 0u; + waitCount = 0u; + delta = ( currentVal.upper_ctrl_limit - currentVal.lower_ctrl_limit ) / 4.0; + if ( delta <= 0.0 ) { + delta = 100.0; + } + if ( currentVal.value + delta < currentVal.upper_ctrl_limit ) { + currentVal.value += delta; + } + else { + currentVal.value -= delta; + } + status = ca_put ( DBR_DOUBLE, chan2, ¤tVal.value ); + SEVCHK ( status, NULL ); + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( eventCount < 1 && waitCount++ < 100 ) { + printf ( "t" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + verify ( eventCount > 0 ); + + /* clean up */ + status = ca_clear_channel ( chan2 ); + SEVCHK ( status, 0 ); + + showProgressEnd ( interestLevel ); +} + +void ioTesterGet ( struct event_handler_args args ) +{ + if ( args.status == ECA_NORMAL ) { + unsigned *pCtr = (unsigned *) args.usr; + ( *pCtr ) ++; + } + else { + printf("get call back failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } +} + +void ioTesterEvent ( struct event_handler_args args ) +{ + if ( args.status == ECA_NORMAL ) { + int status; + status = ca_get_callback ( DBR_STS_STRING, args.chid, ioTesterGet, args.usr ); + SEVCHK ( status, 0 ); + } + else { + printf ( "subscription update failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } +} + +void verifyMonitorSubscriptionFlushIO ( chid chan, unsigned interestLevel ) +{ + int status; + unsigned eventCount = 0u; + unsigned waitCount = 0u; + evid id; + + showProgressBegin ( "verifyMonitorSubscriptionFlushIO", interestLevel ); + + /* + * verify that the first event arrives + */ + status = ca_add_event ( DBR_FLOAT, + chan, nUpdatesTester, &eventCount, &id ); + SEVCHK (status, 0); + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( eventCount < 1 && waitCount++ < 100 ) { + printf ( "-" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + verify ( eventCount > 0 ); + status = ca_clear_event ( id ); + SEVCHK (status, 0); + + showProgressEnd ( interestLevel ); +} + +void accessRightsStateChange ( struct access_rights_handler_args args ) +{ + appChan *pChan = (appChan *) ca_puser ( args.chid ); + + verify ( pChan->channel == args.chid ); + verify ( args.ar.read_access == ca_read_access ( args.chid ) ); + verify ( args.ar.write_access == ca_write_access ( args.chid ) ); + accessUpdateCount++; + pChan->accessUpdateCount++; +} + +void getCallbackStateChange ( struct event_handler_args args ) +{ + appChan *pChan = (appChan *) args.usr; + + verify ( pChan->channel == args.chid ); + verify ( pChan->connected ); + if ( args.status != ECA_NORMAL ) { + printf ( "getCallbackStateChange abnormal status was \"%s\"\n", + ca_message ( args.status ) ); + verify ( args.status == ECA_NORMAL ); + } + + getCallbackCount++; + pChan->getCallbackCount++; +} + +void connectionStateChange ( struct connection_handler_args args ) +{ + int status; + + appChan *pChan = (appChan *) ca_puser ( args.chid ); + + verify ( pChan->channel == args.chid ); + + if ( args.op == CA_OP_CONN_UP ) { + if ( pChan->accessRightsHandlerInstalled ) { + verify ( pChan->accessUpdateCount > 0u ); + } + verify ( ! pChan->connected ); + pChan->connected = 1; + status = ca_get_callback ( DBR_STS_STRING, args.chid, getCallbackStateChange, pChan ); + SEVCHK (status, 0); + } + else if ( args.op == CA_OP_CONN_DOWN ) { + verify ( pChan->connected ); + pChan->connected = 0u; + verify ( ! ca_read_access ( args.chid ) ); + verify ( ! ca_write_access ( args.chid ) ); + } + else { + verify ( 0 ); + } + pChan->connectionUpdateCount++; + connectionUpdateCount++; +} + +void subscriptionStateChange ( struct event_handler_args args ) +{ + struct dbr_sts_string * pdbrgs = ( struct dbr_sts_string * ) args.dbr; + appChan *pChan = (appChan *) args.usr; + + verify ( args.status == ECA_NORMAL ); + verify ( pChan->channel == args.chid ); + verify ( pChan->connected ); + verify ( args.type == DBR_STS_STRING ); + verify ( strlen ( pdbrgs->value ) <= MAX_STRING_SIZE ); + pChan->subscriptionUpdateCount++; + subscriptionUpdateCount++; +} + +void noopSubscriptionStateChange ( struct event_handler_args args ) +{ + if ( args.status != ECA_NORMAL ) { + printf ( "noopSubscriptionStateChange: subscription update failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } +} + +/* + * verifyConnectionHandlerConnect () + * + * 1) verify that connection handler runs during connect + * + * 2) verify that access rights handler runs during connect + * + * 3) verify that get call back runs from connection handler + * (and that they are not required to flush in the connection handler) + * + * 4) verify that first event callback arrives after connect + * + * 5) verify subscription can be cleared before channel is cleared + * + * 6) verify subscription can be cleared by clearing the channel + * + * 7) verify that a nill access rights handler can be installed + */ +void verifyConnectionHandlerConnect ( appChan *pChans, unsigned chanCount, + unsigned repetitionCount, unsigned interestLevel ) +{ + int status; + unsigned i, j; + + showProgressBegin ( "verifyConnectionHandlerConnect", interestLevel ); + + for ( i = 0; i < repetitionCount; i++ ) { + subscriptionUpdateCount = 0u; + accessUpdateCount = 0u; + connectionUpdateCount = 0u; + getCallbackCount = 0u; + + for ( j = 0u; j < chanCount; j++ ) { + + pChans[j].subscriptionUpdateCount = 0u; + pChans[j].accessUpdateCount = 0u; + pChans[j].accessRightsHandlerInstalled = 0; + pChans[j].connectionUpdateCount = 0u; + pChans[j].getCallbackCount = 0u; + pChans[j].connected = 0u; + + status = ca_search_and_connect ( pChans[j].name, + &pChans[j].channel, connectionStateChange, &pChans[j] ); + SEVCHK ( status, NULL ); + + status = ca_replace_access_rights_event ( + pChans[j].channel, accessRightsStateChange ); + SEVCHK ( status, NULL ); + pChans[j].accessRightsHandlerInstalled = 1; + + status = ca_add_event ( DBR_STS_STRING, pChans[j].channel, + subscriptionStateChange, &pChans[j], &pChans[j].subscription ); + SEVCHK ( status, NULL ); + + verify ( ca_test_io () == ECA_IODONE ); + } + + ca_flush_io (); + + showProgress ( interestLevel ); + + while ( connectionUpdateCount < chanCount || + getCallbackCount < chanCount ) { + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + + for ( j = 0u; j < chanCount; j++ ) { + verify ( pChans[j].getCallbackCount == 1u); + verify ( pChans[j].connectionUpdateCount > 0 ); + if ( pChans[j].connectionUpdateCount > 1u ) { + printf ("Unusual connection activity count = %u on channel %s?\n", + pChans[j].connectionUpdateCount, pChans[j].name ); + } + verify ( pChans[j].accessUpdateCount > 0 ); + if ( pChans[j].accessUpdateCount > 1u ) { + printf ("Unusual access rights activity count = %u on channel %s?\n", + pChans[j].connectionUpdateCount, pChans[j].name ); + } + } + + ca_self_test (); + + showProgress ( interestLevel ); + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_clear_event ( pChans[j].subscription ); + SEVCHK ( status, NULL ); + } + + ca_self_test (); + + showProgress ( interestLevel ); + + for ( j = 0u; j < chanCount; j++ ) { + status = ca_replace_access_rights_event ( + pChans[j].channel, 0 ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j++ ) { + status = ca_clear_channel ( pChans[j].channel ); + SEVCHK ( status, NULL ); + } + + ca_self_test (); + + showProgress ( interestLevel ); + + } + showProgressEnd ( interestLevel ); +} + +/* + * verifyBlockingConnect () + * + * 1) verify that we dont print a disconnect message when + * we delete the last channel + * + * 2) verify that we delete the connection to the IOC + * when the last channel is deleted. + * + * 3) verify channel connection state variables + * + * 4) verify ca_test_io () and ca_pend_io () work with + * channels w/o connection handlers + * + * 5) verify that the pending IO count is properly + * maintained when we are add/removing a connection + * handler + * + * 6) verify that the pending IO count goes to zero + * if the channel is deleted before it connects. + */ +void verifyBlockingConnect ( appChan *pChans, unsigned chanCount, + unsigned repetitionCount, unsigned interestLevel ) +{ + int status; + unsigned i, j; + unsigned connections; + unsigned backgroundConnCount = ca_get_ioc_connection_count (); + + showProgressBegin ( "verifyBlockingConnect", interestLevel ); + + i = 0; + while ( backgroundConnCount > 1u ) { + backgroundConnCount = ca_get_ioc_connection_count (); + verify ( i++ < 10 ); + printf ( "Z" ); + fflush ( stdout ); + epicsThreadSleep ( 1.0 ); + } + + for ( i = 0; i < repetitionCount; i++ ) { + + for ( j = 0u; j < chanCount; j++ ) { + pChans[j].subscriptionUpdateCount = 0u; + pChans[j].accessUpdateCount = 0u; + pChans[j].accessRightsHandlerInstalled = 0; + pChans[j].connectionUpdateCount = 0u; + pChans[j].getCallbackCount = 0u; + pChans[j].connected = 0u; + + status = ca_search_and_connect ( pChans[j].name, &pChans[j].channel, NULL, &pChans[j] ); + SEVCHK ( status, NULL ); + + if ( ca_state ( pChans[j].channel ) == cs_conn ) { + verify ( VALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) ); + } + else { + verify ( INVALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) ); + verify ( ca_test_io () == ECA_IOINPROGRESS ); + } + + status = ca_replace_access_rights_event ( + pChans[j].channel, accessRightsStateChange ); + SEVCHK ( status, NULL ); + pChans[j].accessRightsHandlerInstalled = 1; + } + + showProgress ( interestLevel ); + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, connectionStateChange ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, NULL ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, connectionStateChange ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j += 2 ) { + status = ca_change_connection_event ( pChans[j].channel, NULL ); + SEVCHK ( status, NULL ); + } + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, NULL ); + + ca_self_test (); + + showProgress ( interestLevel ); + + verify ( ca_test_io () == ECA_IODONE ); + + connections = ca_get_ioc_connection_count (); + verify ( connections == backgroundConnCount ); + + for ( j = 0u; j < chanCount; j++ ) { + verify ( VALID_DB_REQ ( ca_field_type ( pChans[j].channel ) ) ); + verify ( ca_state ( pChans[j].channel ) == cs_conn ); + SEVCHK ( ca_clear_channel ( pChans[j].channel ), NULL ); + } + + ca_self_test (); + + ca_flush_io (); + + showProgress ( interestLevel ); + + /* + * verify that connections to IOC's that are + * not in use are dropped + */ + if ( ca_get_ioc_connection_count () != backgroundConnCount ) { + epicsThreadSleep ( 0.1 ); + ca_poll (); + j=0; + while ( ca_get_ioc_connection_count () != backgroundConnCount ) { + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + verify ( ++j < 100 ); + } + } + showProgress ( interestLevel ); + } + + for ( j = 0u; j < chanCount; j++ ) { + status = ca_search ( pChans[j].name, &pChans[j].channel ); + SEVCHK ( status, NULL ); + } + + for ( j = 0u; j < chanCount; j++ ) { + status = ca_clear_channel ( pChans[j].channel ); + SEVCHK ( status, NULL ); + } + + verify ( ca_test_io () == ECA_IODONE ); + + /* + * verify ca_pend_io() does not see old search requests + * (that did not specify a connection handler) + */ + status = ca_search_and_connect ( pChans[0].name, &pChans[0].channel, NULL, NULL); + SEVCHK ( status, NULL ); + + if ( ca_state ( pChans[0].channel ) == cs_never_conn ) { + /* force an early timeout */ + status = ca_pend_io ( 1e-16 ); + if ( status == ECA_TIMEOUT ) { + /* + * we end up here if the channel isnt on the same host + */ + epicsThreadSleep ( 0.1 ); + ca_poll (); + if ( ca_state( pChans[0].channel ) != cs_conn ) { + while ( ca_state ( pChans[0].channel ) != cs_conn ) { + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + } + + status = ca_search_and_connect ( pChans[1].name, &pChans[1].channel, NULL, NULL ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( 1e-16 ); + if ( status != ECA_TIMEOUT ) { + verify ( ca_state ( pChans[1].channel ) == cs_conn ); + } + status = ca_clear_channel ( pChans[1].channel ); + SEVCHK ( status, NULL ); + } + else { + verify ( ca_state( pChans[0].channel ) == cs_conn ); + } + } + status = ca_clear_channel( pChans[0].channel ); + SEVCHK ( status, NULL ); + + ca_self_test (); + + showProgressEnd ( interestLevel ); +} + +/* + * 1) verify that use of NULL evid does not cause problems + * 2) verify clear before connect + */ +void verifyClear ( appChan *pChans, unsigned interestLevel ) +{ + int status; + + showProgressBegin ( "verifyClear", interestLevel ); + + /* + * verify channel clear before connect + */ + status = ca_search ( pChans[0].name, &pChans[0].channel ); + SEVCHK ( status, NULL ); + + status = ca_clear_channel ( pChans[0].channel ); + SEVCHK ( status, NULL ); + + /* + * verify subscription clear before connect + * and verify that NULL evid does not cause failure + */ + status = ca_search ( pChans[0].name, &pChans[0].channel ); + SEVCHK ( status, NULL ); + + SEVCHK ( status, NULL ); + status = ca_add_event ( DBR_GR_DOUBLE, + pChans[0].channel, noopSubscriptionStateChange, NULL, NULL ); + SEVCHK ( status, NULL ); + + status = ca_clear_channel ( pChans[0].channel ); + SEVCHK ( status, NULL ); + showProgressEnd ( interestLevel ); +} + +/* + * grEnumTest + */ +void grEnumTest ( chid chan, unsigned interestLevel ) +{ + struct dbr_gr_enum ge; + unsigned count; + int status; + unsigned i; + + showProgressBegin ( "grEnumTest", interestLevel ); + + ge.no_str = -1; + + status = ca_get (DBR_GR_ENUM, chan, &ge); + SEVCHK (status, "DBR_GR_ENUM ca_get()"); + + status = ca_pend_io (timeoutToPendIO); + verify (status == ECA_NORMAL); + + verify ( ge.no_str >= 0 && ge.no_str < NELEMENTS(ge.strs) ); + if ( ge.no_str > 0 ) { + printf ("Enum state str = {"); + count = (unsigned) ge.no_str; + for (i=0; i epsilon ) { + printf ( "float test failed val written %f\n", fval ); + printf ( "float test failed val read %f\n", fretval ); + verify (0); + } + fval += increment; + } +} + +/* + * doubleTest () + */ +void doubleTest ( chid chan, dbr_double_t beginValue, + dbr_double_t increment, dbr_double_t epsilon, + unsigned iterations) +{ + unsigned i; + dbr_double_t fval; + dbr_double_t fretval; + int status; + + fval = beginValue; + for ( i = 0; i < iterations; i++ ) { + fretval = DBL_MAX; + status = ca_put ( DBR_DOUBLE, chan, &fval ); + SEVCHK ( status, NULL ); + status = ca_get ( DBR_DOUBLE, chan, &fretval ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, NULL ); + if ( fabs ( fval - fretval ) > epsilon ) { + printf ( "double test failed val written %f\n", fval ); + printf ( "double test failed val read %f\n", fretval ); + verify ( 0 ); + } + fval += increment; + } +} + +/* + * Verify that we can write and then read back + * the same analog value + */ +void verifyAnalogIO ( chid chan, int dataType, double min, double max, + int minExp, int maxExp, double epsilon, unsigned interestLevel ) +{ + int i; + double incr; + double epsil; + double base; + unsigned iter; + + + if ( ! ca_write_access ( chan ) ) { + printf ("skipped analog test - no write access\n"); + return; + } + + if ( dbr_value_class[ca_field_type ( chan )] != dbr_class_float ) { + printf ("skipped analog test - not an analog type\n"); + return; + } + + showProgressBegin ( "verifyAnalogIO", interestLevel ); + + epsil = epsilon * 4.0; + base = min; + for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { + incr = ldexp ( 0.5, i ); + if ( fabs (incr) > max /10.0 ) { + iter = ( unsigned ) ( max / fabs (incr) ); + } + else { + iter = 10u; + } + if ( dataType == DBR_FLOAT ) { + floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, + (dbr_float_t) epsil, iter ); + } + else if (dataType == DBR_DOUBLE ) { + doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, + (dbr_double_t) epsil, iter ); + } + else { + verify ( 0 ); + } + } + base = max; + for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { + incr = - ldexp ( 0.5, i ); + if ( fabs (incr) > max / 10.0 ) { + iter = (unsigned) ( max / fabs (incr) ); + } + else { + iter = 10u; + } + if ( dataType == DBR_FLOAT ) { + floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, + (dbr_float_t) epsil, iter ); + } + else if (dataType == DBR_DOUBLE ) { + doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, + (dbr_double_t) epsil, iter ); + } + else { + verify ( 0 ); + } + } + base = - max; + for ( i = minExp; i <= maxExp; i += maxExp / 10 ) { + incr = ldexp ( 0.5, i ); + if ( fabs (incr) > max / 10.0 ) { + iter = (unsigned) ( max / fabs ( incr ) ); + } + else { + iter = 10l; + } + if ( dataType == DBR_FLOAT ) { + floatTest ( chan, (dbr_float_t) base, (dbr_float_t) incr, + (dbr_float_t) epsil, iter ); + } + else if (dataType == DBR_DOUBLE ) { + doubleTest ( chan, (dbr_double_t) base, (dbr_double_t) incr, + (dbr_double_t) epsil, iter ); + } + else { + verify ( 0 ); + } + } + showProgressEnd ( interestLevel ); +} + +/* + * Verify that we can write and then read back + * the same DBR_LONG value + */ +void verifyLongIO ( chid chan, unsigned interestLevel ) +{ + int status; + + dbr_long_t iter, rdbk, incr; + struct dbr_ctrl_long cl; + + if ( ca_write_access ( chan ) ) { + return; + } + + status = ca_get ( DBR_CTRL_LONG, chan, &cl ); + SEVCHK ( status, "control long fetch failed\n" ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "control long pend failed\n" ); + + incr = ( cl.upper_ctrl_limit - cl.lower_ctrl_limit ); + if ( incr >= 1 ) { + showProgressBegin ( "verifyLongIO", interestLevel ); + incr /= 1000; + if ( incr == 0 ) { + incr = 1; + } + for ( iter = cl.lower_ctrl_limit; + iter <= cl.upper_ctrl_limit; iter+=incr ) { + + ca_put ( DBR_LONG, chan, &iter ); + ca_get ( DBR_LONG, chan, &rdbk ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "get pend failed\n" ); + verify ( iter == rdbk ); + } + showProgressEnd ( interestLevel ); + } + else { + printf ( "strange limits configured for channel \"%s\"\n", ca_name (chan) ); + } +} + +/* + * Verify that we can write and then read back + * the same DBR_SHORT value + */ +void verifyShortIO ( chid chan, unsigned interestLevel ) +{ + int status; + + dbr_short_t iter, rdbk, incr; + struct dbr_ctrl_short cl; + + if ( ca_write_access ( chan ) ) { + return; + } + + status = ca_get ( DBR_CTRL_SHORT, chan, &cl ); + SEVCHK ( status, "control short fetch failed\n" ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "control short pend failed\n" ); + + incr = (dbr_short_t) ( cl.upper_ctrl_limit - cl.lower_ctrl_limit ); + if ( incr >= 1 ) { + showProgressBegin ( "verifyShortIO", interestLevel ); + + incr /= 1000; + if ( incr == 0 ) { + incr = 1; + } + for ( iter = (dbr_short_t) cl.lower_ctrl_limit; + iter <= (dbr_short_t) cl.upper_ctrl_limit; + iter = (dbr_short_t) (iter + incr) ) { + + ca_put ( DBR_SHORT, chan, &iter ); + ca_get ( DBR_SHORT, chan, &rdbk ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "get pend failed\n" ); + verify ( iter == rdbk ); + } + showProgressEnd ( interestLevel ); + } + else { + printf ( "Strange limits configured for channel \"%s\"\n", ca_name (chan) ); + } +} + +void verifyHighThroughputRead ( chid chan, unsigned interestLevel ) +{ + int status; + unsigned i; + + /* + * verify we dont jam up on many uninterrupted + * solicitations + */ + if ( ca_read_access (chan) ) { + dbr_float_t temp; + showProgressBegin ( "verifyHighThroughputRead", interestLevel ); + for ( i=0; i<10000; i++ ) { + status = ca_get ( DBR_FLOAT, chan, &temp ); + SEVCHK ( status ,NULL ); + } + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, NULL ); + showProgressEnd ( interestLevel ); + } + else { + printf ( "Skipped highthroughput read test - no read access\n" ); + } +} + +void verifyHighThroughputWrite ( chid chan, unsigned interestLevel ) +{ + int status; + unsigned i; + + if (ca_write_access ( chan ) ) { + showProgressBegin ( "verifyHighThroughputWrite", interestLevel ); + for ( i=0; i<10000; i++ ) { + dbr_double_t fval = 3.3; + status = ca_put ( DBR_DOUBLE, chan, &fval ); + SEVCHK ( status, NULL ); + } + SEVCHK ( ca_pend_io (timeoutToPendIO), NULL ); + showProgressEnd ( interestLevel ); + } + else{ + printf("Skipped multiple put test - no write access\n"); + } +} + +/* + * verify we dont jam up on many uninterrupted + * get callback requests + */ +void verifyHighThroughputReadCallback ( chid chan, unsigned interestLevel ) +{ + unsigned i; + int status; + + if ( ca_read_access ( chan ) ) { + unsigned count = 0u; + showProgressBegin ( "verifyHighThroughputReadCallback", interestLevel ); + for ( i=0; i<10000; i++ ) { + status = ca_array_get_callback ( + DBR_FLOAT, 1, chan, nUpdatesTester, &count ); + SEVCHK ( status, NULL ); + } + SEVCHK ( ca_flush_io (), NULL ); + while ( count < 10000u ) { + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + showProgressEnd ( interestLevel ); + } + else { + printf ( "Skipped multiple get cb test - no read access\n" ); + } +} + +/* + * verify we dont jam up on many uninterrupted + * put callback request + */ +void verifyHighThroughputWriteCallback ( chid chan, unsigned interestLevel ) +{ + unsigned i; + int status; + + if ( ca_write_access (chan) && ca_v42_ok (chan) ) { + unsigned count = 0u; + dbr_double_t dval; + showProgressBegin ( "verifyHighThroughputWriteCallback", interestLevel ); + for ( i=0; i<10000; i++ ) { + dval = i + 1; + status = ca_array_put_callback ( + DBR_DOUBLE, 1, chan, &dval, + nUpdatesTester, &count ); + SEVCHK ( status, NULL ); + } + SEVCHK ( ca_flush_io (), NULL ); + dval = 0.0; + status = ca_get ( DBR_DOUBLE, chan, &dval ); + SEVCHK ( status, + "verifyHighThroughputWriteCallback, verification get" ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, + "verifyHighThroughputWriteCallback, verification get pend" ); + verify ( dval == i ); + showProgressEnd ( interestLevel ); + } + else { + printf ( "Skipped multiple put cb test - no write access\n" ); + } +} + +void verifyBadString ( chid chan, unsigned interestLevel ) +{ + int status; + + /* + * verify that we detect that a large string has been written + */ + if ( ca_write_access (chan) ) { + dbr_string_t stimStr; + dbr_string_t respStr; + showProgressBegin ( "verifyBadString", interestLevel ); + memset (stimStr, 'a', sizeof (stimStr) ); + status = ca_array_put ( DBR_STRING, 1u, chan, stimStr ); + verify ( status != ECA_NORMAL ); + sprintf ( stimStr, "%u", 8u ); + status = ca_array_put ( DBR_STRING, 1u, chan, stimStr ); + verify ( status == ECA_NORMAL ); + status = ca_array_get ( DBR_STRING, 1u, chan, respStr ); + verify ( status == ECA_NORMAL ); + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + if ( strcmp ( stimStr, respStr ) ) { + printf ( + "Test fails if stim \"%s\" isnt roughly equiv to resp \"%s\"\n", + stimStr, respStr); + } + showProgressEnd ( interestLevel ); + } + else { + printf ( "Skipped bad string test - no write access\n" ); + } +} + +/* + * multiple_sg_requests() + */ +void multiple_sg_requests ( chid chix, CA_SYNC_GID gid ) +{ + int status; + unsigned i; + static dbr_float_t fvalput = 3.3F; + static dbr_float_t fvalget; + + for ( i=0; i < 1000; i++ ) { + if ( ca_write_access (chix) ){ + status = ca_sg_array_put ( gid, DBR_FLOAT, 1, + chix, &fvalput); + SEVCHK ( status, NULL ); + } + + if ( ca_read_access (chix) ) { + status = ca_sg_array_get ( gid, DBR_FLOAT, 1, + chix, &fvalget); + SEVCHK ( status, NULL ); + } + } +} + +/* + * test_sync_groups() + */ +void test_sync_groups ( chid chan, unsigned interestLevel ) +{ + int status; + CA_SYNC_GID gid1=0; + CA_SYNC_GID gid2=0; + + if ( ! ca_v42_ok ( chan ) ) { + printf ( "skipping sync group test - server is on wrong version\n" ); + } + + showProgressBegin ( "test_sync_groups", interestLevel ); + + status = ca_sg_create ( &gid1 ); + SEVCHK ( status, NULL ); + + multiple_sg_requests ( chan, gid1 ); + status = ca_sg_reset ( gid1 ); + SEVCHK ( status, NULL ); + + status = ca_sg_create ( &gid2 ); + SEVCHK ( status, NULL ); + + multiple_sg_requests ( chan, gid2 ); + multiple_sg_requests ( chan, gid1 ); + status = ca_sg_test ( gid2 ); + SEVCHK ( status, "SYNC GRP2" ); + status = ca_sg_test ( gid1 ); + SEVCHK ( status, "SYNC GRP1" ); + status = ca_sg_block ( gid1, 500.0 ); + SEVCHK ( status, "SYNC GRP1" ); + status = ca_sg_block ( gid2, 500.0 ); + SEVCHK ( status, "SYNC GRP2" ); + + status = ca_sg_delete ( gid2 ); + SEVCHK (status, NULL); + status = ca_sg_create ( &gid2 ); + SEVCHK (status, NULL); + + multiple_sg_requests ( chan, gid1 ); + multiple_sg_requests ( chan, gid2 ); + status = ca_sg_block ( gid1, 500.0 ); + SEVCHK ( status, "SYNC GRP1" ); + status = ca_sg_block ( gid2, 500.0 ); + SEVCHK ( status, "SYNC GRP2" ); + status = ca_sg_delete ( gid1 ); + SEVCHK ( status, NULL ); + status = ca_sg_delete ( gid2 ); + SEVCHK ( status, NULL ); + + showProgressEnd ( interestLevel ); +} + +/* + * multiSubscriptionDeleteTest + * + * 1) verify we can add many monitors at once + * 2) verify that under heavy load the last monitor + * returned is the last modification sent + * 3) attempt to delete monitors while many monitors + * are running + */ +void multiSubscriptionDeleteTest ( chid chan, unsigned interestLevel ) +{ + unsigned count = 0u; + evid mid[1000]; + dbr_float_t temp, getResp; + unsigned i; + + showProgressBegin ( "multiSubscriptionDeleteTest", interestLevel ); + + for ( i=0; i < NELEMENTS (mid); i++ ) { + SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, noopSubscriptionStateChange, + &count, &mid[i]) , NULL ); + } + + /* + * force all of the monitors subscription requests to + * complete + * + * NOTE: this hopefully demonstrates that when the + * server is very busy with monitors the client + * is still able to punch through with a request. + */ + SEVCHK ( ca_get ( DBR_FLOAT,chan,&getResp ), NULL ); + SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); + + showProgress ( interestLevel ); + + /* + * attempt to generate heavy event traffic before initiating + * the monitor delete + */ + if ( ca_write_access (chan) ) { + for ( i=0; i < NELEMENTS (mid); i++ ) { + temp = (float) i; + SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL); + } + } + + showProgress ( interestLevel ); + + /* + * without pausing begin deleting the event subscriptions + * while the queue is full + * + * continue attempting to generate heavy event traffic + * while deleting subscriptions with the hope that we will + * deleting an event at the instant that its callback is + * occurring + */ + for ( i=0; i < NELEMENTS (mid); i++ ) { + if ( ca_write_access (chan) ) { + temp = (float) i; + SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL); + } + SEVCHK ( ca_clear_event ( mid[i]), NULL ); + } + + showProgress ( interestLevel ); + + /* + * force all of the clear event requests to complete + */ + SEVCHK ( ca_get (DBR_FLOAT,chan,&temp), NULL ); + SEVCHK ( ca_pend_io (timeoutToPendIO), NULL ); + + showProgressEnd ( interestLevel ); +} + + +/* + * singleSubscriptionDeleteTest + * + * verify that we dont fail when we repeatedly create + * and delete only one subscription with a high level of + * traffic on it + */ +void singleSubscriptionDeleteTest ( chid chan, unsigned interestLevel ) +{ + unsigned count = 0u; + evid sid; + dbr_float_t temp, getResp; + unsigned i; + + showProgressBegin ( "singleSubscriptionDeleteTest", interestLevel ); + + for ( i=0; i < 1000; i++ ){ + count = 0u; + SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, noopSubscriptionStateChange, + &count, &sid) , NULL ); + + if ( i % 100 == 0 ) { + showProgress ( interestLevel ); + } + + /* + * force the subscription request to complete + */ + SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp ), NULL ); + SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); + + /* + * attempt to generate heavy event traffic before initiating + * the monitor delete + * + * try to interrupt the recv thread while it is processing + * incoming subscription updates + */ + if ( ca_write_access (chan) ) { + unsigned j = 0; + while ( j < i ) { + temp = (float) j++; + SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), + "singleSubscriptionDeleteTest - one of multiple puts" ); + } + ca_flush_io (); + } + + SEVCHK ( ca_clear_event ( sid ), NULL ); + } + + showProgressEnd ( interestLevel ); +} + +/* + * channelClearWithEventTrafficTest + * + * verify that we can delete a channel that has subcriptions + * attached with heavy update traffic + */ +void channelClearWithEventTrafficTest ( const char *pName, unsigned interestLevel ) +{ + unsigned count = 0u; + evid sid; + dbr_float_t temp, getResp; + unsigned i; + + showProgressBegin ( "channelClearWithEventTrafficTest", interestLevel ); + + for ( i=0; i < 1000; i++ ) { + chid chan; + + int status = ca_create_channel ( pName, 0, 0, + CA_PRIORITY_DEFAULT, &chan ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "channelClearWithEventTrafficTest: channel connect failed" ); + verify ( status == ECA_NORMAL ); + + count = 0u; + SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, noopSubscriptionStateChange, + &count, &sid ) , NULL ); + + /* + * force the subscription request to complete + */ + SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp ), NULL ); + SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); + + /* + * attempt to generate heavy event traffic before initiating + * the channel delete + * + * try to interrupt the recv thread while it is processing + * incoming subscription updates + */ + if ( ca_write_access (chan) ) { + unsigned j = 0; + while ( j < i ) { + temp = (float) j++; + SEVCHK ( ca_put (DBR_FLOAT, chan, &temp), NULL); + } + ca_flush_io (); + epicsThreadSleep ( 0.001 ); + } + + SEVCHK ( ca_clear_channel ( chan ), NULL ); + } + + showProgressEnd ( interestLevel ); +} + + + +evid globalEventID; + +void selfDeleteEvent ( struct event_handler_args args ) +{ + int status; + status = ca_clear_event ( globalEventID ); + verify ( status == ECA_NORMAL ); +} + +void eventClearTest ( chid chan ) +{ + int status; + evid monix1, monix2, monix3; + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix1 ); + SEVCHK ( status, NULL ); + + status = ca_clear_event ( monix1 ); + SEVCHK ( status, NULL ); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix1 ); + SEVCHK ( status, NULL ); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix2); + SEVCHK (status, NULL); + + status = ca_clear_event ( monix2 ); + SEVCHK ( status, NULL); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix2); + SEVCHK ( status, NULL ); + + status = ca_add_event ( DBR_FLOAT, chan, noopSubscriptionStateChange, + NULL, &monix3); + SEVCHK ( status, NULL ); + + status = ca_clear_event ( monix2 ); + SEVCHK ( status, NULL); + + status = ca_clear_event ( monix1 ); + SEVCHK ( status, NULL); + + status = ca_clear_event ( monix3 ); + SEVCHK ( status, NULL); + + status = ca_add_event ( DBR_FLOAT, chan, selfDeleteEvent, + 0, &globalEventID ); + SEVCHK ( status, NULL ); +} + +unsigned acctstExceptionCount = 0u; +void acctstExceptionNotify ( struct exception_handler_args args ) +{ + acctstExceptionCount++; +} + +static unsigned arrayEventExceptionNotifyComplete = 0; +void arrayEventExceptionNotify ( struct event_handler_args args ) +{ + if ( args.status == ECA_NORMAL ) { + printf ( + "arrayEventExceptionNotify: expected " + "exception but didnt receive one against \"%s\" \n", + ca_name ( args.chid ) ); + } + else { + arrayEventExceptionNotifyComplete = 1; + } +} + +void exceptionTest ( chid chan, unsigned interestLevel ) +{ + int status; + + showProgressBegin ( "exceptionTest", interestLevel ); + + /* + * force a get exception to occur + */ + { + dbr_put_ackt_t *pRS; + + acctstExceptionCount = 0u; + status = ca_add_exception_event ( acctstExceptionNotify, 0 ); + SEVCHK ( status, "exception notify install failed" ); + + pRS = malloc ( ca_element_count (chan) * sizeof (*pRS) ); + verify ( pRS ); + status = ca_array_get ( DBR_PUT_ACKT, + ca_element_count (chan), chan, pRS ); + SEVCHK ( status, "array read request failed" ); + ca_pend_io ( 1e-5 ); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( acctstExceptionCount < 1u ) { + printf ( "G" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + status = ca_add_exception_event ( 0, 0 ); + SEVCHK ( status, "exception notify install failed" ); + free ( pRS ); + } + + /* + * force a get call back exception to occur + */ + { + arrayEventExceptionNotifyComplete = 0u; + status = ca_array_get_callback ( DBR_PUT_ACKT, + ca_element_count (chan), chan, arrayEventExceptionNotify, 0 ); + if ( status != ECA_NORMAL ) { + verify ( status == ECA_BADTYPE || status == ECA_GETFAIL ); + arrayEventExceptionNotifyComplete = 1; + } + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( ! arrayEventExceptionNotifyComplete ) { + printf ( "GCB" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + } + + /* + * force a subscription exception to occur + */ + { + evid id; + + arrayEventExceptionNotifyComplete = 0u; + status = ca_add_array_event ( DBR_PUT_ACKT, ca_element_count ( chan ), + chan, arrayEventExceptionNotify, 0, 0.0, 0.0, 0.0, &id ); + if ( status != ECA_NORMAL ) { + verify ( status == ECA_BADTYPE || status == ECA_GETFAIL ); + arrayEventExceptionNotifyComplete = 1; + } + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( ! arrayEventExceptionNotifyComplete ) { + printf ( "S" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + status = ca_clear_event ( id ); + SEVCHK ( status, "subscription clear failed" ); + } + + /* + * force a put exception to occur + */ + { + dbr_string_t * pWS; + unsigned i; + + acctstExceptionCount = 0u; + status = ca_add_exception_event ( acctstExceptionNotify, 0 ); + SEVCHK ( status, "exception notify install failed" ); + + pWS = malloc ( ca_element_count (chan) * MAX_STRING_SIZE ); + verify ( pWS ); + for ( i = 0; i < ca_element_count (chan); i++ ) { + strcpy ( pWS[i], "@#$%" ); + } + status = ca_array_put ( DBR_STRING, + ca_element_count (chan), chan, pWS ); + if ( status != ECA_NORMAL ) { + verify ( status == ECA_BADTYPE || status == ECA_PUTFAIL ); + acctstExceptionCount++; /* local PV case */ + } + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( acctstExceptionCount < 1u ) { + printf ( "P" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + status = ca_add_exception_event ( 0, 0 ); + SEVCHK ( status, "exception notify install failed" ); + free ( pWS ); + } + + /* + * force a put callback exception to occur + */ + { + dbr_string_t *pWS; + unsigned i; + + pWS = malloc ( ca_element_count (chan) * MAX_STRING_SIZE ); + verify ( pWS ); + for ( i = 0; i < ca_element_count (chan); i++ ) { + strcpy ( pWS[i], "@#$%" ); + } + arrayEventExceptionNotifyComplete = 0u; + status = ca_array_put_callback ( DBR_STRING, + ca_element_count (chan), chan, pWS, + arrayEventExceptionNotify, 0); + if ( status != ECA_NORMAL ) { + arrayEventExceptionNotifyComplete = 1; + } + ca_flush_io (); + epicsThreadSleep ( 0.1 ); + ca_poll (); + while ( ! arrayEventExceptionNotifyComplete ) { + printf ( "PCB" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + free ( pWS ); + } + + showProgressEnd ( interestLevel ); +} + +/* + * array test + * + * verify that we can at least write and read back the same array + * if multiple elements are present + */ +static unsigned arrayReadNotifyComplete = 0; +static unsigned arrayWriteNotifyComplete = 0; +void arrayReadNotify ( struct event_handler_args args ) +{ + dbr_double_t *pWF = ( dbr_double_t * ) ( args.usr ); + dbr_double_t *pRF = ( dbr_double_t * ) ( args.dbr ); + int i; + for ( i = 0; i < args.count; i++ ) { + verify ( pWF[i] == pRF[i] ); + } + arrayReadNotifyComplete = 1; +} +void arrayWriteNotify ( struct event_handler_args args ) +{ + if ( args.status == ECA_NORMAL ) { + arrayWriteNotifyComplete = 1; + } + else { + printf ( "arrayWriteNotify: update failed for \"%s\" because \"%s\"", + ca_name ( args.chid ), ca_message ( args.status ) ); + } +} + +void arrayTest ( chid chan, unsigned maxArrayBytes, unsigned interestLevel ) +{ + dbr_double_t *pRF, *pWF; + unsigned i; + int status; + evid id; + + if ( ! ca_write_access ( chan ) ) { + printf ( "skipping array test - no write access\n" ); + return; + } + + showProgressBegin ( "arrayTest", interestLevel ); + + pRF = (dbr_double_t *) calloc ( ca_element_count (chan), sizeof (*pRF) ); + verify ( pRF != NULL ); + + pWF = (dbr_double_t *) calloc ( ca_element_count (chan), sizeof (*pWF) ); + verify ( pWF != NULL ); + + /* + * write some random numbers into the array + */ + for ( i = 0; i < ca_element_count (chan); i++ ) { + pWF[i] = rand (); + pRF[i] = - pWF[i]; + } + status = ca_array_put ( DBR_DOUBLE, ca_element_count ( chan ), + chan, pWF ); + SEVCHK ( status, "array write request failed" ); + + /* + * read back the array + */ + status = ca_array_get ( DBR_DOUBLE, ca_element_count (chan), + chan, pRF ); + SEVCHK ( status, "array read request failed" ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "array read failed" ); + + /* + * verify read response matches values written + */ + for ( i = 0; i < ca_element_count ( chan ); i++ ) { + if ( pWF[i] != pRF[i] ) { + printf ( "i=%u, pWF[i]=%f, pRF[i]=%f\n", + i, pWF[i], pRF[i]); + } + verify ( pWF[i] == pRF[i] ); + } + + /* + * read back the array as strings + */ + { + char *pRS; + unsigned size = ca_element_count (chan) * MAX_STRING_SIZE; + + if ( size <= maxArrayBytes ) { + + pRS = malloc ( ca_element_count (chan) * MAX_STRING_SIZE ); + verify ( pRS ); + status = ca_array_get ( DBR_STRING, + ca_element_count (chan), chan, pRS ); + SEVCHK ( status, "array read request failed" ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "array read failed" ); + free ( pRS ); + } + else { + printf ( "skipping the fetch array in string data type test - does not fit\n" ); + } + } + + /* + * write some random numbers into the array + */ + for ( i = 0; i < ca_element_count (chan); i++ ) { + pWF[i] = rand (); + pRF[i] = - pWF[i]; + } + status = ca_array_put_callback ( DBR_DOUBLE, ca_element_count (chan), + chan, pWF, arrayWriteNotify, 0 ); + SEVCHK ( status, "array write notify request failed" ); + status = ca_array_get_callback ( DBR_DOUBLE, ca_element_count (chan), + chan, arrayReadNotify, pWF ); + SEVCHK ( status, "array read notify request failed" ); + ca_flush_io (); + while ( ! arrayWriteNotifyComplete || ! arrayReadNotifyComplete ) { + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + + /* + * write some random numbers into the array + */ + for ( i = 0; i < ca_element_count (chan); i++ ) { + pWF[i] = rand (); + pRF[i] = - pWF[i]; + } + arrayReadNotifyComplete = 0; + status = ca_array_put ( DBR_DOUBLE, ca_element_count ( chan ), + chan, pWF ); + SEVCHK ( status, "array write notify request failed" ); + status = ca_add_array_event ( DBR_DOUBLE, ca_element_count ( chan ), + chan, arrayReadNotify, pWF, 0.0, 0.0, 0.0, &id ); + SEVCHK ( status, "array subscription request failed" ); + ca_flush_io (); + while ( ! arrayReadNotifyComplete ) { + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + status = ca_clear_event ( id ); + SEVCHK ( status, "clear event request failed" ); + + /* + * a get request should fail or fill with zeros + * when the array size is too large + */ + { + acctstExceptionCount = 0u; + status = ca_add_exception_event ( acctstExceptionNotify, 0 ); + SEVCHK ( status, "exception notify install failed" ); + status = ca_array_get ( DBR_DOUBLE, + ca_element_count (chan)+1, chan, pRF ); + if ( status == ECA_NORMAL ) { + ca_poll (); + while ( acctstExceptionCount < 1u ) { + printf ( "N" ); + fflush ( stdout ); + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + } + } + else { + verify ( status == ECA_BADCOUNT ); + } + status = ca_add_exception_event ( 0, 0 ); + SEVCHK ( status, "exception notify install failed" ); + } + + free ( pRF ); + free ( pWF ); + + showProgressEnd ( interestLevel ); +} + +/* + * verify that unequal send/recv buffer sizes work + * (a bug related to this test was detected in early R3.14) + * + * this test must be run when no other channels are connected + */ +void unequalServerBufferSizeTest ( const char * pName, unsigned interestLevel ) +{ + dbr_double_t *pRF, *pWF; + unsigned connections; + chid newChan; + int status; + + showProgressBegin ( "unequalServerBufferSizeTest", interestLevel ); + + /* this test must be run when no channels are connected */ + connections = ca_get_ioc_connection_count (); + verify ( connections == 0u ); + + status = ca_create_channel ( pName, 0, 0, 0, & newChan ); + verify ( status == ECA_NORMAL ); + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + + showProgress ( interestLevel ); + + if ( ! ca_write_access ( newChan ) ) { + printf ( "skipping unequal buffer size test - no write access\n" ); + status = ca_clear_channel ( newChan ); + verify ( status == ECA_NORMAL ); + return; + } + + pRF = (dbr_double_t *) calloc ( ca_element_count (newChan), sizeof (*pRF) ); + verify ( pRF != NULL ); + + pWF = (dbr_double_t *) calloc ( ca_element_count (newChan), sizeof (*pWF) ); + verify ( pWF != NULL ); + + status = ca_array_get ( DBR_DOUBLE, ca_element_count ( newChan ), + newChan, pRF ); + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + status = ca_clear_channel ( newChan ); + verify ( status == ECA_NORMAL ); + + showProgress ( interestLevel ); + + status = ca_create_channel ( pName, 0, 0, 0, &newChan ); + verify ( status == ECA_NORMAL ); + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + + showProgress ( interestLevel ); + + status = ca_array_put ( DBR_DOUBLE, ca_element_count ( newChan ), + newChan, pWF ); + verify ( status == ECA_NORMAL ); + status = ca_array_get ( DBR_DOUBLE, 1, + newChan, pRF ); + verify ( status == ECA_NORMAL ); + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + status = ca_clear_channel ( newChan ); + verify ( status == ECA_NORMAL ); + + free ( pRF ); + free ( pWF ); + + showProgressEnd ( interestLevel ); +} + +/* + * pend_event_delay_test() + */ +void pend_event_delay_test ( dbr_double_t request ) +{ + int status; + epicsTimeStamp end_time; + epicsTimeStamp start_time; + dbr_double_t delay; + dbr_double_t accuracy; + + epicsTimeGetCurrent ( &start_time ); + status = ca_pend_event ( request ); + if (status != ECA_TIMEOUT) { + SEVCHK(status, NULL); + } + epicsTimeGetCurrent(&end_time); + delay = epicsTimeDiffInSeconds ( &end_time, &start_time ); + accuracy = 100.0*(delay-request)/request; + printf ( "CA pend event delay = %f sec results in error = %f %%\n", + request, accuracy ); + verify ( fabs(accuracy) < 10.0 ); +} + +void caTaskExitTest ( unsigned interestLevel ) +{ + int status; + + showProgressBegin ( "caTaskExitTest", interestLevel ); + + status = ca_task_exit (); + SEVCHK ( status, NULL ); + + showProgressEnd ( interestLevel ); +} + +void verifyDataTypeMacros (void) +{ + int type; + + type = dbf_type_to_DBR ( DBF_SHORT ); + verify ( type == DBR_SHORT ); + type = dbf_type_to_DBR_STS ( DBF_SHORT ); + verify ( type == DBR_STS_SHORT ); + type = dbf_type_to_DBR_GR ( DBF_SHORT ); + verify ( type == DBR_GR_SHORT ); + type = dbf_type_to_DBR_CTRL ( DBF_SHORT ); + verify ( type == DBR_CTRL_SHORT ); + type = dbf_type_to_DBR_TIME ( DBF_SHORT ); + verify ( type == DBR_TIME_SHORT ); + verify ( strcmp ( dbr_type_to_text( DBR_SHORT ), "DBR_SHORT" ) == 0 ); + verify ( strcmp ( dbf_type_to_text( DBF_SHORT ), "DBF_SHORT" ) == 0 ); + verify ( dbr_type_is_SHORT ( DBR_SHORT ) ); + verify ( dbr_type_is_valid ( DBR_SHORT ) ); + verify ( dbf_type_is_valid ( DBF_SHORT ) ); + { + int dataType = -1; + dbf_text_to_type ( "DBF_SHORT", dataType ); + verify ( dataType == DBF_SHORT ); + dbr_text_to_type ( "DBR_CLASS_NAME", dataType ); + verify ( dataType == DBR_CLASS_NAME ); + } +} + +typedef struct { + evid id; + dbr_float_t lastValue; + unsigned count; +} eventTest; + +/* + * updateTestEvent () + */ +void updateTestEvent ( struct event_handler_args args ) +{ + eventTest *pET = (eventTest *) args.usr; + struct dbr_gr_float *pGF = (struct dbr_gr_float *) args.dbr; + pET->lastValue = pGF->value; + pET->count++; +} + +dbr_float_t monitorUpdateTestPattern ( unsigned iter ) +{ + return ( (float) iter ) * 10.12345f + 10.7f; +} + +void callbackClearsChannel ( struct event_handler_args args ) +{ + int status; + status = ca_clear_channel ( args.chid ); + SEVCHK ( status, "clearChannelInXxxxCallbackTest clear channel" ); +} + +void clearChannelInGetCallbackTest ( const char *pName, unsigned level ) +{ + unsigned i; + chid chan; + int status; + + showProgressBegin ( "clearChannelInGetCallbackTest", level ); + + for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { + ca_pend_event ( 0.1 ); + verify ( i < 100 ); + } + + status = ca_create_channel ( pName, 0, 0, 0, & chan ); + SEVCHK ( status, "clearChannelInGetCallbackTest create channel" ); + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "clearChannelInGetCallbackTest connect channel" ); + + status = ca_get_callback ( DBR_DOUBLE, chan, callbackClearsChannel, 0 ); + SEVCHK ( status, "clearChannelInGetCallbackTest get callback" ); + + for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { + ca_pend_event ( 0.1 ); + verify ( i < 100 ); + } + + showProgressEnd ( level ); +} + +void clearChannelInPutCallbackTest ( const char *pName, unsigned level ) +{ + unsigned i; + const dbr_double_t value = 1.1; + chid chan; + int status; + + showProgressBegin ( "clearChannelInPutCallbackTest", level ); + + for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { + ca_pend_event ( 0.1 ); + verify ( i < 100 ); + } + + status = ca_create_channel ( pName, 0, 0, 0, & chan ); + SEVCHK ( status, "clearChannelInPutCallbackTest create channel" ); + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "clearChannelInPutCallbackTest connect channel" ); + + status = ca_put_callback ( DBR_DOUBLE, chan, & value, + callbackClearsChannel, 0 ); + SEVCHK ( status, "clearChannelInPutCallbackTest get callback" ); + + for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { + ca_pend_event ( 0.1 ); + verify ( i < 100 ); + } + + showProgressEnd ( level ); +} + +void clearChannelInSubscrCallbackTest ( const char *pName, unsigned level ) +{ + unsigned i; + chid chan; + int status; + + showProgressBegin ( "clearChannelInSubscrCallbackTest", level ); + + for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { + ca_pend_event ( 0.1 ); + verify ( i < 100 ); + } + + status = ca_create_channel ( pName, 0, 0, 0, & chan ); + SEVCHK ( status, "clearChannelInSubscrCallbackTest create channel" ); + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "clearChannelInSubscrCallbackTest connect channel" ); + + status = ca_create_subscription ( DBR_DOUBLE, 1, chan, + DBE_VALUE, callbackClearsChannel, 0, 0 ); + SEVCHK ( status, "clearChannelInSubscrCallbackTest subscribe" ); + + for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { + ca_pend_event ( 0.1 ); + verify ( i < 100 ); + } + + showProgressEnd ( level ); +} + +void monitorAddConnectionCallback ( struct connection_handler_args args ) +{ + if ( args.op == CA_OP_CONN_UP ) { + unsigned * pEventCount = ( unsigned * ) ca_puser ( args.chid ); + int status; + verify ( *pEventCount == 0u ); + (*pEventCount)++; + status = ca_create_subscription ( DBR_DOUBLE, 1, + args.chid, DBE_VALUE, nUpdatesTester, ca_puser ( args.chid ), 0 ); + SEVCHK ( status, "monitorAddConnectionCallback create subscription" ); + } + else { + fprintf ( stderr, "disconnect during monitorAddConnectionCallbackTest?\n" ); + } +} + +/* + * monitorAddConnectionCallbackTest + * 1) subscription add from within connection callback needs to work + * 2) check for problems where subscription is installed twice if + * its installed within the connection callback handler + */ +void monitorAddConnectionCallbackTest ( const char *pName, unsigned interestLevel ) +{ + unsigned i; + chid chan; + int status; + unsigned eventCount = 0u; + unsigned getCallbackCount = 0u; + + showProgressBegin ( "monitorAddConnectionCallbackTest", interestLevel ); + + for ( i = 0; ca_get_ioc_connection_count () > 0 ; i++ ) { + ca_pend_event ( 0.1 ); + verify ( i < 100 ); + } + + status = ca_create_channel ( pName, + monitorAddConnectionCallback, &eventCount, 0, & chan ); + SEVCHK ( status, "monitorAddConnectionCallbackTest create channel" ); + + while ( eventCount < 2 ) { + ca_pend_event ( 0.1 ); + } + verify ( eventCount >= 2u ); + + status = ca_get_callback ( DBR_DOUBLE, chan, nUpdatesTester, &getCallbackCount ); + SEVCHK ( status, "monitorAddConnectionCallback get callback" ); + while ( getCallbackCount == 0 ) { + ca_pend_event ( 0.1 ); + } + verify ( eventCount >= 2u ); + verify ( getCallbackCount == 1u ); + + status = ca_clear_channel ( chan ); + SEVCHK ( status, "monitorAddConnectionCallbackTest clear channel" ); + + status = ca_flush_io (); + SEVCHK ( status, "monitorAddConnectionCallbackTest flush" ); + + showProgressEnd ( interestLevel ); +} + + +/* + * monitorUpdateTest + * + * 1) verify we can add many monitors at once + * 2) verify that under heavy load the last monitor + * returned is the last modification sent + */ +void monitorUpdateTest ( chid chan, unsigned interestLevel ) +{ + eventTest test[100]; + dbr_float_t temp, getResp; + unsigned i, j; + unsigned flowCtrlCount = 0u; + unsigned prevPassCount; + unsigned tries; + + if ( ! ca_write_access ( chan ) ) { + printf ("skipped monitorUpdateTest test - no write access\n"); + return; + } + + if ( dbr_value_class[ca_field_type ( chan )] != dbr_class_float ) { + printf ("skipped monitorUpdateTest test - not an analog type\n"); + return; + } + + showProgressBegin ( "monitorUpdateTest", interestLevel ); + + /* + * set channel to known value + */ + temp = 1; + SEVCHK ( ca_put ( DBR_FLOAT, chan, &temp ), NULL ); + + for ( i = 0; i < NELEMENTS(test); i++ ) { + test[i].count = 0; + test[i].lastValue = -1.0f; + SEVCHK(ca_add_event(DBR_GR_FLOAT, chan, updateTestEvent, + &test[i], &test[i].id),NULL); + } + + /* + * force all of the monitors subscription requests to + * complete + * + * NOTE: this hopefully demonstrates that when the + * server is very busy with monitors the client + * is still able to punch through with a request. + */ + SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp) ,NULL ); + SEVCHK ( ca_pend_io ( timeoutToPendIO ) ,NULL ); + + showProgress ( interestLevel ); + + /* + * pass the test only if we get the first monitor update + */ + tries = 0; + while ( 1 ) { + unsigned nFailed = 0u; + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + for ( i = 0; i < NELEMENTS ( test ); i++ ) { + if ( test[i].count > 0 ) { + if ( test[i].lastValue != temp ) { + nFailed++; + } + } + else { + nFailed++; + } + } + if ( nFailed == 0u ) { + break; + } + printf ( "-" ); + fflush ( stdout ); + verify ( tries++ < 50 ); + } + + showProgress ( interestLevel ); + + /* + * attempt to uncover problems where the last event isnt sent + * and hopefully get into a flow control situation + */ + prevPassCount = 0u; + for ( i = 0; i < 10; i++ ) { + for ( j = 0; j < NELEMENTS(test); j++ ) { + SEVCHK ( ca_clear_event ( test[j].id ), NULL ); + test[j].count = 0; + test[j].lastValue = -1.0f; + SEVCHK ( ca_add_event ( DBR_GR_FLOAT, chan, updateTestEvent, + &test[j], &test[j].id ) , NULL ); + } + + for ( j = 0; j <= i; j++ ) { + temp = monitorUpdateTestPattern ( j ); + SEVCHK ( ca_put ( DBR_FLOAT, chan, &temp ), NULL ); + } + + /* + * wait for the above to complete + */ + getResp = -1; + SEVCHK ( ca_get ( DBR_FLOAT, chan, &getResp ), NULL ); + SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); + + if ( getResp != temp ) { + printf ( "getResp=%f, temp=%f\n", getResp, temp ); + verify ( getResp == temp ); + } + + /* + * wait for all of the monitors to have correct values + */ + tries = 0; + while (1) { + unsigned passCount = 0; + unsigned tmpFlowCtrlCount = 0u; + epicsThreadSleep ( 0.1 ); + ca_poll (); /* emulate typical GUI */ + for ( j = 0; j < NELEMENTS ( test ); j++ ) { + /* + * we shouldnt see old monitors because + * we resubscribed + */ + verify ( test[j].count <= i + 2 ); + if ( test[j].lastValue == temp ) { + if ( test[j].count < i + 1 ) { + tmpFlowCtrlCount++; + } + passCount++; + } + } + if ( passCount == NELEMENTS ( test ) ) { + flowCtrlCount += tmpFlowCtrlCount; + break; + } + if ( passCount == prevPassCount ) { + verify ( tries++ < 500 ); + if ( tries % 50 == 0 ) { + for ( j = 0; j <= i; j++ ) { + dbr_float_t pat = monitorUpdateTestPattern ( j ); + if ( pat == test[0].lastValue ) { + break; + } + } + if ( j <= i) { + printf ( "-(%d)", i-j ); + } + else { + printf ( "." ); + } + fflush ( stdout ); + } + } + prevPassCount = passCount; + } + } + + showProgress ( interestLevel ); + + /* + * delete the event subscriptions + */ + for ( i = 0; i < NELEMENTS ( test ); i++ ) { + SEVCHK ( ca_clear_event ( test[i].id ), NULL ); + } + + /* + * force all of the clear event requests to + * complete + */ + SEVCHK ( ca_get ( DBR_FLOAT, chan, &temp ), NULL ); + SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); + + /* printf ( "flow control bypassed %u events\n", flowCtrlCount ); */ + + showProgressEnd ( interestLevel ); +} + +void verifyReasonableBeaconPeriod ( chid chan, unsigned interestLevel ) +{ + if ( ca_get_ioc_connection_count () > 0 ) { + double beaconPeriod; + double watchDogDelay; + unsigned i; + + showProgressBegin ( "verifyReasonableBeaconPeriod", interestLevel ); + + + printf ( "Beacon anomalies detected since program start %u\n", + ca_beacon_anomaly_count () ); + + beaconPeriod = ca_beacon_period ( chan ); + printf ( "Estimated beacon period for channel %s = %g sec.\n", + ca_name ( chan ), beaconPeriod ); + + watchDogDelay = ca_receive_watchdog_delay ( chan ); + verify ( watchDogDelay >= 0.0 ); + + printf ( "busy: receive watchdog for \"%s\" expires in %g sec.\n", + ca_name ( chan ), watchDogDelay ); + + /* + * let one default connection timeout go by w/o receive activity + * so we can see if beacons reset the watchdog + */ + for ( i = 0u; i < 15u; i++ ) { + ca_pend_event ( 2.0 ); + showProgress ( interestLevel ); + } + if ( interestLevel > 0 ) { + printf ( "\n" ); + } + + watchDogDelay = ca_receive_watchdog_delay ( chan ); + verify ( watchDogDelay >= 0.0 ); + + printf ( "inactive: receive watchdog for \"%s\" expires in %g sec.\n", + ca_name ( chan ), watchDogDelay ); + + showProgressEnd ( interestLevel ); + } +} + +void verifyOldPend ( unsigned interestLevel ) +{ + int status; + + showProgressBegin ( "verifyOldPend", interestLevel ); + + /* + * verify that the old ca_pend() is in the symbol table + */ + status = ca_pend ( 100000.0, 1 ); + verify ( status == ECA_NORMAL ); + status = ca_pend ( 1e-12, 0 ); + verify ( status == ECA_TIMEOUT ); + + showProgressEnd ( interestLevel ); +} + +void verifyTimeStamps ( chid chan, unsigned interestLevel ) +{ + struct dbr_time_double first; + struct dbr_time_double last; + epicsTimeStamp localTime; + char buf[128]; + size_t length; + double diff; + int status; + + showProgressBegin ( "verifyTimeStamps", interestLevel ); + + status = epicsTimeGetCurrent ( & localTime ); + verify ( status >= 0 ); + + status = ca_get ( DBR_TIME_DOUBLE, chan, & first ); + SEVCHK ( status, "fetch of dbr time double failed\n" ); + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + + status = ca_get ( DBR_TIME_DOUBLE, chan, & last ); + SEVCHK ( status, "fetch of dbr time double failed\n" ); + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + + length = epicsTimeToStrftime ( buf, sizeof ( buf ), + "%a %b %d %Y %H:%M:%S.%f", & first.stamp ); + verify ( length ); + printf ("Processing time of channel \"%s\" was \"%s\"\n", + ca_name ( chan ), buf ); + + diff = epicsTimeDiffInSeconds ( & last.stamp, & first.stamp ); + printf ("Time difference between two successive reads was %g sec\n", + diff ); + + diff = epicsTimeDiffInSeconds ( & first.stamp, & localTime ); + printf ("Time difference between client and server %g sec\n", + diff ); + + showProgressEnd ( interestLevel ); +} + +/* + * attempts to verify from the client side that + * channel priorities work correctly + */ +void verifyChannelPriorities ( const char *pName, unsigned interestLevel ) +{ + static const unsigned nPrio = 30; + unsigned i; + + showProgressBegin ( "verifyChannelPriorities", interestLevel ); + + for ( i = 0u; i < nPrio; i++ ) { + int status; + double value; + chid chan0, chan1; + unsigned priority0, priority1; + + priority0 = ( i * ( CA_PRIORITY_MAX - CA_PRIORITY_MIN ) ) / nPrio; + priority0 += CA_PRIORITY_MIN; + if ( priority0 > CA_PRIORITY_MAX ) { + priority0 = CA_PRIORITY_MAX; + } + + priority1 = ( (i+1) * ( CA_PRIORITY_MAX - CA_PRIORITY_MIN ) ) / nPrio; + priority1 += CA_PRIORITY_MIN; + if ( priority1 > CA_PRIORITY_MAX ) { + priority1 = CA_PRIORITY_MAX; + } + + status = ca_create_channel ( pName, 0, 0, + priority0, &chan0 ); + SEVCHK ( status, "prioritized channel create failed" ); + + status = ca_create_channel ( pName, 0, 0, + priority1, &chan1 ); + SEVCHK ( status, "prioritized channel create failed" ); + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "prioritized channel connect failed" ); + verify ( status == ECA_NORMAL ); + + value = i; + status = ca_put ( DBR_DOUBLE, chan0, &value ); + SEVCHK ( status, "prioritized channel put failed" ); + status = ca_put ( DBR_DOUBLE, chan1, &value ); + SEVCHK ( status, "prioritized channel put failed" ); + + status = ca_flush_io (); + SEVCHK ( status, "prioritized channel flush failed" ); + + status = ca_get ( DBR_DOUBLE, chan0, &value ); + SEVCHK ( status, "prioritized channel get failed" ); + status = ca_get ( DBR_DOUBLE, chan1, &value ); + SEVCHK ( status, "prioritized channel get failed" ); + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "prioritized channel pend io failed" ); + + status = ca_clear_channel ( chan0 ); + SEVCHK ( status, "prioritized channel clear failed" ); + status = ca_clear_channel ( chan1 ); + SEVCHK ( status, "prioritized channel clear failed" ); + } + + showProgressEnd ( interestLevel ); +} + +void verifyTearDownWhenChannelConnected ( const char * pName, + enum ca_preemptive_callback_select select, + unsigned interestLevel ) +{ + static const unsigned chanCount = 20; + static const unsigned loopCount = 100; + + chid * const pChans = (chid * const) calloc ( chanCount, sizeof ( *pChans ) ); + double * const pValues = (double * const) calloc ( chanCount, sizeof ( *pValues ) ); + unsigned i, j; + + verify ( pChans && pValues ); + + showProgressBegin ( "verifyTearDownWhenChannelConnected", interestLevel ); + + for ( i = 0u; i < loopCount; i++ ) { + int status; + ca_context_create ( select ); + + for ( j = 0; j < chanCount; j++ ) { + status = ca_create_channel ( pName, 0, 0, 0, & pChans[j] ); + SEVCHK ( status, "immediate tear down channel create failed" ); + } + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "immediate tear down channel connect failed" ); + verify ( status == ECA_NORMAL ); + + for ( j = 0; j < chanCount; j++ ) { + status = ca_get ( DBR_DOUBLE, pChans[j], &pValues[j] ); + SEVCHK ( status, "immediate tear down channel get failed" ); + } + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "immediate tear down get pend io failed" ); + + ca_context_destroy (); + } + + ca_context_create ( select ); + + free ( pChans ); + free ( pValues ); + + showProgressEnd ( interestLevel ); +} + + +void verifyImmediateTearDown ( const char * pName, + enum ca_preemptive_callback_select select, + unsigned interestLevel ) +{ + int i; + + showProgressBegin ( "verifyImmediateTearDown", interestLevel ); + + for ( i = 0u; i < 100; i++ ) { + chid chan; + int status; + dbr_long_t value = i % 8; + ca_context_create ( select ); + ca_task_initialize (); + status = ca_create_channel ( pName, 0, 0, 0, & chan ); + SEVCHK ( status, "immediate tear down channel create failed" ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "immediate tear down channel connect failed" ); + verify ( status == ECA_NORMAL ); + /* + * verify that puts pending when we call ca_task_exit() + * get flushed out + */ + if ( i > 0 ) { + dbr_long_t currentValue = value; + status = ca_get ( DBR_LONG, chan, & currentValue ); + SEVCHK ( status, "immediate tear down channel get failed" ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "immediate tear down channel get failed" ); + if ( currentValue != ( (i - 1) % 8 ) ) { + printf ( "currentValue = %i, i = %i\n", currentValue, i ); + verify ( currentValue == ( (i - 1) % 8 ) ); + } + } + status = ca_put ( DBR_LONG, chan, & value ); + SEVCHK ( status, "immediate tear down channel put failed" ); + status = ca_clear_channel ( chan ); + SEVCHK ( status, "immediate tear down channel clear failed" ); + ca_context_destroy (); + /* epicsThreadSleep ( 1e-15 ); */ + if ( i % 10 == 0 ) { + showProgress ( interestLevel ); + } + } + + ca_context_create ( select ); + + showProgressEnd ( interestLevel ); +} + +/* + * keeping these tests together detects a bug + */ +void eventClearAndMultipleMonitorTest ( chid chan, unsigned interestLevel ) +{ + eventClearTest ( chan ); + monitorUpdateTest ( chan, interestLevel ); +} + +void fdcb ( void * parg ) +{ + ca_poll (); +} + +void fdRegCB ( void * parg, int fd, int opened ) +{ + int status; + + fdctx * mgrCtx = ( fdctx * ) parg; + if ( opened ) { + status = fdmgr_add_callback ( + mgrCtx, fd, fdi_read, fdcb, 0 ); + verify ( status >= 0 ); + } + else { + status = fdmgr_clear_callback ( + mgrCtx, fd, fdi_read ); + verify ( status >= 0 ); + } +} + +void fdManagerVerify ( const char * pName, unsigned interestLevel ) +{ + int status; + fdctx * mgrCtx; + struct timeval tmo; + chid newChan; + evid subscription; + unsigned eventCount = 0u; + epicsTimeStamp begin, end; + + mgrCtx = fdmgr_init (); + verify ( mgrCtx ); + + showProgressBegin ( "fdManagerVerify", interestLevel ); + + status = ca_add_fd_registration ( fdRegCB, mgrCtx ); + verify ( status == ECA_NORMAL ); + + status = ca_create_channel ( pName, 0, 0, 0, & newChan ); + verify ( status == ECA_NORMAL ); + + while ( ca_state ( newChan ) != cs_conn ) { + tmo.tv_sec = 6000; + tmo.tv_usec = 0; + status = fdmgr_pend_event ( mgrCtx, & tmo ); + verify ( status >= 0 ); + } + + showProgress ( interestLevel ); + + status = ca_add_event ( DBR_FLOAT, newChan, + nUpdatesTester, & eventCount, & subscription ); + verify ( status == ECA_NORMAL ); + + status = ca_flush_io (); + verify ( status == ECA_NORMAL ); + + while ( eventCount < 1 ) { + tmo.tv_sec = 6000; + tmo.tv_usec = 0; + status = fdmgr_pend_event ( mgrCtx, & tmo ); + verify ( status >= 0 ); + } + + showProgress ( interestLevel ); + + status = ca_clear_event ( subscription ); + verify ( status == ECA_NORMAL ); + + status = ca_flush_io (); + verify ( status == ECA_NORMAL ); + + /* look for infinite loop in fd manager schedualing */ + epicsTimeGetCurrent ( & begin ); + eventCount = 0u; + while ( 1 ) { + double delay; + tmo.tv_sec = 1; + tmo.tv_usec = 0; + status = fdmgr_pend_event ( mgrCtx, & tmo ); + verify ( status >= 0 ); + epicsTimeGetCurrent ( & end ); + delay = epicsTimeDiffInSeconds ( & end, & begin ); + if ( delay >= 1.0 ) { + break; + } + verify ( eventCount++ < 100 ); + } + + showProgress ( interestLevel ); + + status = ca_clear_channel ( newChan ); + verify ( status == ECA_NORMAL ); + + status = ca_add_fd_registration ( 0, 0 ); + verify ( status == ECA_NORMAL ); + + status = fdmgr_delete ( mgrCtx ); + verify ( status >= 0 ); + + showProgressEnd ( interestLevel ); +} + +void verifyConnectWithDisconnectedChannels ( + const char *pName, unsigned interestLevel ) +{ + int status; + chid bogusChan[300]; + chid validChan; + unsigned i; + + showProgressBegin ( "verifyConnectWithDisconnectedChannels", interestLevel ); + + for ( i= 0u; i < NELEMENTS ( bogusChan ); i++ ) { + char buf[256]; + sprintf ( buf, "aChannelThatShouldNeverNeverNeverExist%u", i ); + status = ca_create_channel ( buf, 0, 0, 0, & bogusChan[i] ); + verify ( status == ECA_NORMAL ); + } + + status = ca_pend_io ( 0.001 ); + verify ( status == ECA_TIMEOUT ); + + /* wait a long time for the search interval to increase */ + for ( i= 0u; i < 10; i++ ) { + epicsThreadSleep ( 1.0 ); + showProgress ( interestLevel ); + } + + status = ca_create_channel ( pName, 0, 0, 0, & validChan ); + verify ( status == ECA_NORMAL ); + + /* + * we should be able to connect to a valid + * channel within a reasonable delay even + * though there is one permanently + * diasconnected channel + */ + status = ca_pend_io ( timeoutToPendIO ); + verify ( status == ECA_NORMAL ); + + status = ca_clear_channel ( validChan ); + verify ( status == ECA_NORMAL ); + + for ( i= 0u; i < NELEMENTS ( bogusChan ); i++ ) { + status = ca_clear_channel ( bogusChan[i] ); + verify ( status == ECA_NORMAL ); + } + + showProgressEnd ( interestLevel ); +} + +void verifyClearChannelOnDisconnectCallback ( + struct connection_handler_args args ) +{ + int * pDisconnectFlag = ca_puser ( args.chid ); + if ( args.op == CA_OP_CONN_DOWN ) { + ca_clear_channel ( args.chid ); + *pDisconnectFlag = 1; + } +} + +void noopExceptionCallback ( struct exception_handler_args args ) +{ +} + +void verifyDisconnect ( + const char * pName, unsigned interestLevel ) +{ + int disconnectFlag = 0; + unsigned count = 0; + chid chan0; + chid chan1; + int status; + + status = ca_create_channel ( + pName, verifyClearChannelOnDisconnectCallback, + & disconnectFlag, 0, & chan0 ); + SEVCHK ( status, NULL ); + + fprintf ( stdout, "Waiting for test channel to connect." ); + fflush ( stdout ); + do { + ca_pend_event ( 0.1 ); + if ( count++%50 == 0 ) { + fprintf ( stdout, "." ); + fflush ( stdout ); + } + } while ( ca_state ( chan0 ) != cs_conn ); + fprintf ( stdout, "confirmed.\n" ); + + /* + * if its a local channel and will never disconnect + * then skip the portions of this test that cant be + * completed. + */ + if ( ca_get_ioc_connection_count () == 0 ) { + status = ca_clear_channel ( chan0 ); + SEVCHK ( status, NULL ); + return; + } + + status = ca_add_exception_event ( noopExceptionCallback, 0 ); + SEVCHK ( status, NULL ); + + fprintf ( stdout, "Please force test channel to disconnect." ); + fflush ( stdout ); + do { + ca_pend_event ( 0.1 );; + if ( count++%50 == 0 ) { + fprintf ( stdout, "." ); + fflush ( stdout ); + } + } while ( ! disconnectFlag ); + fprintf ( stdout, "confirmed.\n" ); + /* channel cleared by disconnect handler */ + + status = ca_create_channel ( + pName, 0, 0, 0, & chan1 ); + SEVCHK ( status, NULL ); + + fprintf ( stdout, "Waiting for test channel to connect." ); + fflush ( stdout ); + while ( ca_state ( chan1 ) != cs_conn ) { + ca_pend_event ( 5.0 ); + fprintf ( stdout, "." ); + fflush ( stdout ); + } + status = ca_clear_channel ( chan1 ); + SEVCHK ( status, NULL ); + fprintf ( stdout, "confirmed.\n" ); + + status = ca_add_exception_event ( 0, 0 ); + SEVCHK ( status, NULL ); +} + +void verifyName ( + const char * pName, unsigned interestLevel ) +{ + chid chan; + int status = ca_create_channel ( + pName, 0, 0, 0, & chan ); + SEVCHK ( status, NULL ); + if ( strcmp ( pName, ca_name ( chan ) ) != 0 ) { + printf ( "Canonical name for channel was \"%s\"\n", ca_name ( chan ) ); + } + status = ca_clear_channel ( chan ); + SEVCHK ( status, NULL ); +} + +void verifyContextRundownFlush ( const char * pName, unsigned interestLevel ) +{ + unsigned i; + + showProgressBegin ( "verifyContextRundownFlush", interestLevel ); + + for ( i=0u; i < 1000; i++ ) { + const dbr_double_t stim = i; + + { + chid chan; + int status; + status = ca_context_create ( ca_disable_preemptive_callback ); + SEVCHK ( status, "context create failed" ); + + status = ca_create_channel ( pName, 0, 0, 0, & chan ); + SEVCHK ( status, NULL ); + + status = ca_pend_io( timeoutToPendIO ); + SEVCHK ( status, "channel connect failed" ); + + status = ca_put ( DBR_DOUBLE, chan, & stim ); + SEVCHK ( status, "channel put failed" ); + + status = ca_clear_channel ( chan ); + SEVCHK ( status, NULL ); + + ca_context_destroy (); + } + + { + chid chan; + int status; + dbr_double_t resp; + status = ca_context_create ( ca_disable_preemptive_callback ); + SEVCHK ( status, "context create failed" ); + + status = ca_create_channel ( pName, 0, 0, 0, & chan ); + SEVCHK ( status, NULL ); + + status = ca_pend_io( timeoutToPendIO ); + SEVCHK ( status, "channel connect failed" ); + + status = ca_get ( DBR_DOUBLE, chan, & resp ); + SEVCHK ( status, "channel get failed" ); + + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, "get, pend io failed" ); + + verify ( stim == resp ); + + status = ca_clear_channel ( chan ); + SEVCHK ( status, NULL ); + + ca_context_destroy (); + } + + if ( i % 100 == 0 ) { + showProgress ( interestLevel ); + } + } + + showProgressEnd ( interestLevel ); +} + +void verifyContextRundownChanStillExist ( + const char * pName, unsigned interestLevel ) +{ + chid chan[10000]; + int status; + unsigned i; + + showProgressBegin ( "verifyContextRundownChanStillExist", interestLevel ); + + status = ca_context_create ( ca_disable_preemptive_callback ); + SEVCHK ( status, "context create failed" ); + + for ( i = 0; i < NELEMENTS ( chan ); i++ ) { + status = ca_create_channel ( pName, 0, 0, 0, & chan[i] ); + SEVCHK ( status, NULL ); + } + + status = ca_pend_io( timeoutToPendIO ); + SEVCHK ( status, "channel connect failed" ); + + ca_context_destroy (); + + showProgressEnd ( interestLevel ); +} + +int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount, + unsigned repetitionCount, enum ca_preemptive_callback_select select ) +{ + chid chan; + int status; + unsigned i; + appChan *pChans; + unsigned connections; + unsigned maxArrayBytes = 10000000; + + printf ( "CA Client V%s, channel name \"%s\", timeout %g\n", + ca_version (), pName, timeoutToPendIO ); + if ( select == ca_enable_preemptive_callback ) { + printf ( "Preemptive call back is enabled.\n" ); + } + + { + char tmpString[32]; + sprintf ( tmpString, "%u", maxArrayBytes ); + epicsEnvSet ( "EPICS_CA_MAX_ARRAY_BYTES", tmpString ); + } + + status = ca_context_create ( select ); + SEVCHK ( status, NULL ); + + verifyDisconnect ( pName, interestLevel ); + verifyImmediateTearDown ( pName, select, interestLevel ); + verifyTearDownWhenChannelConnected ( pName, select, interestLevel ); + + verifyDataTypeMacros (); + + connections = ca_get_ioc_connection_count (); + verify ( connections == 0u ); + unequalServerBufferSizeTest ( pName, interestLevel ); + clearChannelInGetCallbackTest ( pName, interestLevel ); + clearChannelInPutCallbackTest ( pName, interestLevel ); + clearChannelInSubscrCallbackTest ( pName, interestLevel ); + monitorAddConnectionCallbackTest ( pName, interestLevel ); + + showProgressBegin ( "connecting to test channel", interestLevel ); + status = ca_search ( pName, & chan ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( timeoutToPendIO ); + SEVCHK ( status, NULL ); + showProgressEnd ( interestLevel ); + + printf ( "native type was %s, native count was %lu\n", + dbf_type_to_text ( ca_field_type ( chan ) ), + ca_element_count ( chan ) ); + + connections = ca_get_ioc_connection_count (); + verify ( connections == 1u || connections == 0u ); + if ( connections == 0u ) { + printf ( "testing with a local channel\n" ); + } + + verifyName ( pName, interestLevel ); + verifyConnectWithDisconnectedChannels ( pName, interestLevel ); + grEnumTest ( chan, interestLevel ); + test_sync_groups ( chan, interestLevel ); + verifyChannelPriorities ( pName, interestLevel ); + verifyTimeStamps ( chan, interestLevel ); + verifyOldPend ( interestLevel ); + exceptionTest ( chan, interestLevel ); + arrayTest ( chan, maxArrayBytes, interestLevel ); + verifyMonitorSubscriptionFlushIO ( chan, interestLevel ); + monitorSubscriptionFirstUpdateTest ( pName, chan, interestLevel ); + ctrlDoubleTest ( chan, interestLevel ); + verifyBlockInPendIO ( chan, interestLevel ); + verifyAnalogIO ( chan, DBR_FLOAT, FLT_MIN, FLT_MAX, + FLT_MIN_EXP, FLT_MAX_EXP, FLT_EPSILON, interestLevel ); + verifyAnalogIO ( chan, DBR_DOUBLE, DBL_MIN, DBL_MAX, + DBL_MIN_EXP, DBL_MAX_EXP, DBL_EPSILON, interestLevel ); + verifyLongIO ( chan, interestLevel ); + verifyShortIO ( chan, interestLevel ); + multiSubscriptionDeleteTest ( chan, interestLevel ); + singleSubscriptionDeleteTest ( chan, interestLevel ); + channelClearWithEventTrafficTest ( pName, interestLevel ); + eventClearAndMultipleMonitorTest ( chan, interestLevel ); + verifyHighThroughputRead ( chan, interestLevel ); + verifyHighThroughputWrite ( chan, interestLevel ); + verifyHighThroughputReadCallback ( chan, interestLevel ); + verifyHighThroughputWriteCallback ( chan, interestLevel ); + verifyBadString ( chan, interestLevel ); + if ( select != ca_enable_preemptive_callback ) { + fdManagerVerify ( pName, interestLevel ); + } + + /* + * CA pend event delay accuracy test + * (CA asssumes that search requests can be sent + * at least every 25 mS on all supported os) + */ + printf ( "\n" ); + pend_event_delay_test ( 1.0 ); + pend_event_delay_test ( 0.1 ); + pend_event_delay_test ( 0.25 ); + + /* ca_channel_status ( 0 ); */ + ca_client_status ( 0 ); + /* ca_client_status ( 6u ); info about each channel */ + + pChans = calloc ( channelCount, sizeof ( *pChans ) ); + verify ( pChans ); + + for ( i = 0; i < channelCount; i++ ) { + strncpy ( pChans[ i ].name, pName, sizeof ( pChans[ i ].name ) ); + pChans[ i ].name[ sizeof ( pChans[i].name ) - 1 ] = '\0'; + } + + verifyConnectionHandlerConnect ( pChans, channelCount, repetitionCount, interestLevel ); + verifyBlockingConnect ( pChans, channelCount, repetitionCount, interestLevel ); + verifyClear ( pChans, interestLevel ); + + verifyReasonableBeaconPeriod ( chan, interestLevel ); + + /* + * Verify that we can do IO with the new types for ALH + */ +#if 0 + if ( ca_read_access (chan) && ca_write_access (chan) ) { + { + dbr_put_ackt_t acktIn = 1u; + dbr_put_acks_t acksIn = 1u; + struct dbr_stsack_string stsackOut; + + SEVCHK ( ca_put ( DBR_PUT_ACKT, chan, &acktIn ), NULL ); + SEVCHK ( ca_put ( DBR_PUT_ACKS, chan, &acksIn ), NULL ); + SEVCHK ( ca_get ( DBR_STSACK_STRING, chan, &stsackOut ), NULL ); + SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL ); + } +#endif + + /* test that ca_task_exit () works when there is still one channel remaining */ + /* status = ca_clear_channel ( chan ); */ + /* SEVCHK ( status, NULL ); */ + + caTaskExitTest ( interestLevel ); + + verifyContextRundownFlush ( pName, interestLevel ); + verifyContextRundownChanStillExist ( pName, interestLevel ); + + free ( pChans ); + + printf ( "\nTest Complete\n" ); + + epicsExit ( EXIT_SUCCESS ); + + return 0; +} + + diff --git a/src/ca/acctstMain.c b/src/ca/acctstMain.c new file mode 100644 index 000000000..4c700a30b --- /dev/null +++ b/src/ca/acctstMain.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* 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 +#include + +#include "cadef.h" +#include "caDiagnostics.h" + +int main ( int argc, char **argv ) +{ + unsigned progressLoggingLevel; + unsigned channelCount; + unsigned repetitionCount; + enum ca_preemptive_callback_select preempt; + int aBoolean; + + + if ( argc < 2 || argc > 6 ) { + printf ("usage: %s [progress logging level] [channel count] " + "[repetition count] [enable preemptive callback]\n", + argv[0] ); + return 1; + } + + if ( argc >= 3 ) { + progressLoggingLevel = atoi ( argv[2] ); + } + else { + progressLoggingLevel = 0; + } + + if ( argc >= 4 ) { + channelCount = atoi ( argv[3] ); + } + else { + channelCount = 20000; + } + + if ( argc >= 5 ) { + repetitionCount = atoi ( argv[4] ); + } + else { + repetitionCount = 1; + } + + if ( argc >= 6 ) { + aBoolean = atoi ( argv[5] ); + } + else { + aBoolean = 0; + } + if ( aBoolean ) { + preempt = ca_enable_preemptive_callback; + } + else { + preempt = ca_disable_preemptive_callback; + } + + acctst ( argv[1], progressLoggingLevel, channelCount, repetitionCount, preempt ); + + return 0; +} diff --git a/src/ca/addrList.h b/src/ca/addrList.h new file mode 100644 index 000000000..6e50fb3f6 --- /dev/null +++ b/src/ca/addrList.h @@ -0,0 +1,40 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef addrListh +#define addrListh + +#include "shareLib.h" +#include "envDefs.h" +#include "osiSock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI configureChannelAccessAddressList + ( struct ELLLIST *pList, SOCKET sock, unsigned short port ); + +epicsShareFunc void epicsShareAPI addAddrToChannelAccessAddressList + ( struct ELLLIST *pList, const ENV_PARAM *pEnv, + unsigned short port, int ignoreNonDefaultPort ); + +epicsShareFunc void epicsShareAPI printChannelAccessAddressList + ( const struct ELLLIST *pList ); + +epicsShareFunc void epicsShareAPI removeDuplicateAddresses + ( struct ELLLIST *pDestList, ELLLIST *pSrcList, int silent); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef addrListh */ + diff --git a/src/ca/autoPtrDestroy.h b/src/ca/autoPtrDestroy.h new file mode 100644 index 000000000..ef7360736 --- /dev/null +++ b/src/ca/autoPtrDestroy.h @@ -0,0 +1,94 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef autoPtrDestroyh +#define autoPtrDestroyh + +template < class T > +class autoPtrDestroy { +public: + autoPtrDestroy ( T * ); + ~autoPtrDestroy (); + T & operator * () const; + T * operator -> () const; + autoPtrDestroy & operator = ( T * ); + T * get () const; + T * release (); +private: + T * p; + // not implemented + autoPtrDestroy & operator = ( const autoPtrDestroy & ); + autoPtrDestroy ( const autoPtrDestroy & ); +}; + +template < class T > +inline autoPtrDestroy::autoPtrDestroy ( T *pIn ) : + p ( pIn ) {} + +template < class T > +inline autoPtrDestroy::~autoPtrDestroy () +{ + if ( this->p ) { + this->p->destroy (); + } +} + +template < class T > +inline T & autoPtrDestroy::operator * () const +{ + return * this->p; +} + +template < class T > +inline T * autoPtrDestroy::operator -> () const +{ + return this->p; +} + +template < class T > +inline autoPtrDestroy & autoPtrDestroy::operator = ( T * pIn ) +{ + if ( this->p ) { + this->p->destroy (); + } + this->p = pIn; + return *this; +} + +template < class T > +inline T * autoPtrDestroy::get () const +{ + return this->p; +} + +template < class T > +inline T * autoPtrDestroy::release () +{ + T *pTmp = this->p; + this->p = 0; + return pTmp; +} + +#endif // #ifdef autoPtrDestroyh diff --git a/src/ca/autoPtrFreeList.h b/src/ca/autoPtrFreeList.h new file mode 100644 index 000000000..7dc73609a --- /dev/null +++ b/src/ca/autoPtrFreeList.h @@ -0,0 +1,104 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef autoPtrFreeListh +#define autoPtrFreeListh + +#ifdef epicsExportSharedSymbols +# define autoPtrFreeListh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "tsFreeList.h" +#include "compilerDependencies.h" + +#ifdef autoPtrFreeListh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +#endif + +template < class T, unsigned N = 0x400, class MUTEX = epicsMutex > +class autoPtrFreeList { +public: + autoPtrFreeList ( tsFreeList < T, N, MUTEX > &, T * ); + ~autoPtrFreeList (); + T & operator * () const; + T * operator -> () const; + T * get () const; + T * release (); +private: + T * p; + tsFreeList < T, N, MUTEX > & freeList; + // not implemented + autoPtrFreeList & operator = ( const autoPtrFreeList & ); + autoPtrFreeList ( const autoPtrFreeList < T, N, MUTEX > & ); +}; + +template < class T, unsigned N, class MUTEX > +inline autoPtrFreeList < T, N, MUTEX >::autoPtrFreeList ( + tsFreeList < T, N, MUTEX > & freeListIn, T * pIn ) : + p ( pIn ), freeList ( freeListIn ) {} + +template < class T, unsigned N, class MUTEX > +inline autoPtrFreeList < T, N, MUTEX >::~autoPtrFreeList () +{ + if ( this->p ) { + this->p->~T(); + // its probably a good idea to require that the class has placement delete + // by calling it during cleanup if the compiler supports it +# if defined ( CXX_PLACEMENT_DELETE ) + T::operator delete ( this->p, this->freeList ); +# else + this->freeList.release ( this->p ); +# endif + } +} + +template < class T, unsigned N, class MUTEX > +inline T & autoPtrFreeList < T, N, MUTEX >::operator * () const +{ + return * this->p; +} + +template < class T, unsigned N, class MUTEX > +inline T * autoPtrFreeList < T, N, MUTEX >::operator -> () const +{ + return this->p; +} + +template < class T, unsigned N, class MUTEX > +inline T * autoPtrFreeList < T, N, MUTEX >::get () const +{ + return this->p; +} + +template < class T, unsigned N, class MUTEX > +inline T * autoPtrFreeList < T, N, MUTEX >::release () +{ + T *pTmp = this->p; + this->p = 0; + return pTmp; +} + +#endif // #ifdef autoPtrFreeListh diff --git a/src/ca/autoPtrRecycle.h b/src/ca/autoPtrRecycle.h new file mode 100644 index 000000000..173e148b8 --- /dev/null +++ b/src/ca/autoPtrRecycle.h @@ -0,0 +1,92 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef autoPtrRecycleh +#define autoPtrRecycleh + +template < class T > +class autoPtrRecycle { +public: + autoPtrRecycle ( + epicsGuard < epicsMutex > &, chronIntIdResTable < baseNMIU > &, + cacRecycle &, T * ); + ~autoPtrRecycle (); + T & operator * () const; + T * operator -> () const; + T * get () const; + T * release (); +private: + T * p; + cacRecycle & r; + chronIntIdResTable < baseNMIU > & ioTable; + epicsGuard < epicsMutex > & guard; + // not implemented + autoPtrRecycle ( const autoPtrRecycle & ); + autoPtrRecycle & operator = ( const autoPtrRecycle & ); +}; + +template < class T > +inline autoPtrRecycle::autoPtrRecycle ( + epicsGuard < epicsMutex > & guardIn, chronIntIdResTable < baseNMIU > & tbl, + cacRecycle & rIn, T * pIn ) : + p ( pIn ), r ( rIn ), ioTable ( tbl ), guard ( guardIn ) {} + +template < class T > +inline autoPtrRecycle::~autoPtrRecycle () +{ + if ( this->p ) { + baseNMIU *pb = this->p; + this->ioTable.remove ( *pb ); + pb->destroy ( this->guard, this->r ); + } +} + +template < class T > +inline T & autoPtrRecycle::operator * () const +{ + return * this->p; +} + +template < class T > +inline T * autoPtrRecycle::operator -> () const +{ + return this->p; +} + +template < class T > +inline T * autoPtrRecycle::get () const +{ + return this->p; +} + +template < class T > +inline T * autoPtrRecycle::release () +{ + T *pTmp = this->p; + this->p = 0; + return pTmp; +} + +#endif // #ifdef autoPtrRecycleh diff --git a/src/ca/baseNMIU.cpp b/src/ca/baseNMIU.cpp new file mode 100644 index 000000000..20f153b0a --- /dev/null +++ b/src/ca/baseNMIU.cpp @@ -0,0 +1,39 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, the Regents of the University of California. + * + * Author: Jeff Hill + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "iocinf.h" +#include "nciu.h" +#include "netIO.h" + +baseNMIU::~baseNMIU () +{ +} + +void baseNMIU::forceSubscriptionUpdate ( + epicsGuard < epicsMutex > &, nciu & ) +{ +} + + + + diff --git a/src/ca/bhe.cpp b/src/ca/bhe.cpp new file mode 100644 index 000000000..8ede482b4 --- /dev/null +++ b/src/ca/bhe.cpp @@ -0,0 +1,365 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include +#include +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "virtualCircuit.h" +#include "bhe.h" + +/* + * set average to -1.0 so that when the next beacon + * occurs we can distinguish between: + * o new server + * o existing server's beacon we are seeing + * for the first time shortly after program + * start up + * + * if creating this in response to a search reply + * and not in response to a beacon then + * we set the beacon time stamp to + * zero (so we can correctly compute the period + * between the 1st and 2nd beacons) + */ +bhe::bhe ( epicsMutex & mutexIn, const epicsTime & initialTimeStamp, + unsigned initialBeaconNumber, const inetAddrID & addr ) : + inetAddrID ( addr ), timeStamp ( initialTimeStamp ), averagePeriod ( - DBL_MAX ), + mutex ( mutexIn ), pIIU ( 0 ), lastBeaconNumber ( initialBeaconNumber ) +{ +# ifdef DEBUG + { + char name[64]; + addr.name ( name, sizeof ( name ) ); + ::printf ( "created beacon entry for %s\n", name ); + } +# endif +} + +bhe::~bhe () +{ +} + +void bhe::beaconAnomalyNotify ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->pIIU ) { + this->pIIU->beaconAnomalyNotify ( guard ); + } +} + +#ifdef DEBUG +void bhe::logBeacon ( const char * pDiagnostic, + const double & currentPeriod, + const epicsTime & currentTime ) +{ + if ( this->pIIU ) { + char name[64]; + this->name ( name, sizeof ( name ) ); + char date[64]; + currentTime.strftime ( date, sizeof ( date ), + "%a %b %d %Y %H:%M:%S.%f"); + ::printf ( "%s cp=%g ap=%g %s %s\n", + pDiagnostic, currentPeriod, + this->averagePeriod, name, date ); + } +} +#else +inline void bhe::logBeacon ( const char * /* pDiagnostic */, + const double & /* currentPeriod */, + const epicsTime & /* currentTime */ ) +{ +} +#endif + +#ifdef DEBUG +void bhe::logBeaconDiscard ( unsigned beaconAdvance, + const epicsTime & currentTime ) +{ + if ( this->pIIU ) { + char name[64]; + this->name ( name, sizeof ( name ) ); + char date[64]; + currentTime.strftime ( date, sizeof ( date ), + "%a %b %d %Y %H:%M:%S.%f"); + ::printf ( "bb %u %s %s\n", + beaconAdvance, name, date ); + } +} +#else +void bhe::logBeaconDiscard ( unsigned /* beaconAdvance */, + const epicsTime & /* currentTime */ ) +{ +} +#endif + +/* + * update beacon period + * + * updates beacon period, and looks for beacon anomalies + */ +bool bhe::updatePeriod ( + epicsGuard < epicsMutex > & guard, const epicsTime & programBeginTime, + const epicsTime & currentTime, ca_uint32_t beaconNumber, + unsigned protocolRevision ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + // + // this block is enetered if the beacon was created as a side effect of + // creating a connection and so we dont yet know the first beacon time + // and sequence number + // + if ( this->timeStamp == epicsTime () ) { + if ( CA_V410 ( protocolRevision ) ) { + this->lastBeaconNumber = beaconNumber; + } + + this->beaconAnomalyNotify ( guard ); + + /* + * this is the 1st beacon seen - the beacon time stamp + * was not initialized during BHE create because + * a TCP/IP connection created the beacon. + * (nothing to do but set the beacon time stamp and return) + */ + this->timeStamp = currentTime; + + logBeacon ( "fb", - DBL_MAX, currentTime ); + + return false; + } + + // 1) detect beacon duplications due to redundant routes + // 2) detect lost beacons due to input queue overrun or damage + if ( CA_V410 ( protocolRevision ) ) { + unsigned beaconSeqAdvance; + if ( beaconNumber >= this->lastBeaconNumber ) { + beaconSeqAdvance = beaconNumber - this->lastBeaconNumber; + } + else { + beaconSeqAdvance = ( ca_uint32_max - this->lastBeaconNumber ) + beaconNumber; + } + this->lastBeaconNumber = beaconNumber; + + // throw out sequence numbers just prior to, or the same as, the last one received + // (this situation is probably caused by a temporary duplicate route ) + if ( beaconSeqAdvance == 0 || beaconSeqAdvance > ca_uint32_max - 256 ) { + logBeaconDiscard ( beaconSeqAdvance, currentTime ); + return false; + } + + // throw out sequence numbers that jump forward by only a few numbers + // (this situation is probably caused by a duplicate route + // or a beacon due to input queue overun) + if ( beaconSeqAdvance > 1 && beaconSeqAdvance < 4 ) { + logBeaconDiscard ( beaconSeqAdvance, currentTime ); + return false; + } + } + + // compute the beacon period (if we have seen at least two beacons) + bool netChange = false; + double currentPeriod = currentTime - this->timeStamp; + + if ( this->averagePeriod < 0.0 ) { + double totalRunningTime; + + this->beaconAnomalyNotify ( guard ); + + /* + * this is the 2nd beacon seen. We cant tell about + * the change in period at this point so we just + * initialize the average period and return. + */ + this->averagePeriod = currentPeriod; + + logBeacon ( "fp", currentPeriod, currentTime ); + + + /* + * ignore beacons seen for the first time shortly after + * init, but do not ignore beacons arriving with a short + * period because the IOC was rebooted soon after the + * client starts up. + */ + totalRunningTime = this->timeStamp - programBeginTime; + if ( currentPeriod <= totalRunningTime ) { + netChange = true; + } + } + else { + + /* + * Is this an IOC seen because of a restored + * network segment? + * + * It may be possible to get false triggers here + * if the client is busy, but this does not cause + * problems because the echo response will tell us + * that the server is available + */ + if ( currentPeriod >= this->averagePeriod * 1.25 ) { + + /* + * trigger on any missing beacon + * if connected to this server + */ + this->beaconAnomalyNotify ( guard ); + + if ( currentPeriod >= this->averagePeriod * 3.25 ) { + /* + * trigger on any 3 contiguous missing beacons + * if not connected to this server + */ + netChange = true; + } + logBeacon ( "bah", currentPeriod, currentTime ); + } + + /* + * Is this an IOC seen because of an IOC reboot + * (beacon come at a higher rate just after the + * IOC reboots). Lower tolarance here because we + * dont have to worry about lost beacons. + * + * It may be possible to get false triggers here + * if the client is busy, but this does not cause + * problems because the echo response will tell us + * that the server is available + */ + else if ( currentPeriod <= this->averagePeriod * 0.80 ) { + this->beaconAnomalyNotify ( guard ); + netChange = true; + logBeacon ( "bal", currentPeriod, currentTime ); + } + else if ( this->pIIU ) { + // update state of health for active virtual circuits + // if the beacon looks ok + this->pIIU->beaconArrivalNotify ( guard ); + logBeacon ( "vb", currentPeriod, currentTime ); + } + + // update a running average period + this->averagePeriod = currentPeriod * 0.125 + + this->averagePeriod * 0.875; + } + + this->timeStamp = currentTime; + + return netChange; +} + +void bhe::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->show ( guard, level ); +} + +void bhe::show ( epicsGuard < epicsMutex > &, unsigned level ) const +{ + char host [64]; + this->name ( host, sizeof ( host ) ); + if ( this->averagePeriod == -DBL_MAX ) { + ::printf ( "CA beacon hash entry for %s \n", + host ); + } + else { + ::printf ( "CA beacon hash entry for %s with period estimate %f\n", + host, this->averagePeriod ); + } + if ( level > 0u ) { + char date[64]; + this->timeStamp.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); + ::printf ( "\tbeacon number %u, on %s\n", + this->lastBeaconNumber, date ); + } +} + +double bhe::period ( epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->averagePeriod; +} + +epicsTime bhe::updateTime ( epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->timeStamp; +} + +void bhe::registerIIU ( + epicsGuard < epicsMutex > & guard, tcpiiu & iiu ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->pIIU = & iiu; +} + +void bhe::unregisterIIU ( + epicsGuard < epicsMutex > & guard, tcpiiu & iiu ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->pIIU == & iiu ) { + this->pIIU = 0; + this->timeStamp = epicsTime(); + this->averagePeriod = - DBL_MAX; + logBeacon ( "ui", this->averagePeriod, epicsTime::getCurrent () ); + } +} + +void * bhe::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void bhe::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void * bheFreeStore::allocate ( size_t size ) +{ + return freeList.allocate ( size ); +} + +void bheFreeStore::release ( void * pCadaver ) +{ + freeList.release ( pCadaver ); +} + +bheMemoryManager::~bheMemoryManager () {} + + diff --git a/src/ca/bhe.h b/src/ca/bhe.h new file mode 100644 index 000000000..1660e485b --- /dev/null +++ b/src/ca/bhe.h @@ -0,0 +1,123 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#ifndef bheh +#define bheh + +#ifdef epicsExportSharedSymbols +# define bhehEpicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "tsDLList.h" +#include "tsFreeList.h" +#include "epicsTime.h" +#include "compilerDependencies.h" + +#ifdef bhehEpicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "inetAddrID.h" +#include "caProto.h" + +class tcpiiu; +class bheMemoryManager; + +// using a pure abstract wrapper class around the free list avoids +// Tornado 2.0.1 GNU compiler bugs +class epicsShareClass bheMemoryManager { +public: + virtual ~bheMemoryManager (); + virtual void * allocate ( size_t ) = 0; + virtual void release ( void * ) = 0; +}; + +class bhe : public tsSLNode < bhe >, public inetAddrID { +public: + epicsShareFunc bhe ( + epicsMutex &, const epicsTime & initialTimeStamp, + unsigned initialBeaconNumber, const inetAddrID & addr ); + epicsShareFunc ~bhe (); + epicsShareFunc bool updatePeriod ( + epicsGuard < epicsMutex > &, + const epicsTime & programBeginTime, + const epicsTime & currentTime, ca_uint32_t beaconNumber, + unsigned protocolRevision ); + epicsShareFunc double period ( epicsGuard < epicsMutex > & ) const; + epicsShareFunc epicsTime updateTime ( epicsGuard < epicsMutex > & ) const; + epicsShareFunc void show ( unsigned level ) const; + epicsShareFunc void show ( epicsGuard < epicsMutex > &, unsigned /* level */ ) const; + epicsShareFunc void registerIIU ( epicsGuard < epicsMutex > &, tcpiiu & ); + epicsShareFunc void unregisterIIU ( epicsGuard < epicsMutex > &, tcpiiu & ); + epicsShareFunc void * operator new ( size_t size, bheMemoryManager & ); +#ifdef CXX_PLACEMENT_DELETE + epicsShareFunc void operator delete ( void *, bheMemoryManager & ); +#endif +private: + epicsTime timeStamp; + double averagePeriod; + epicsMutex & mutex; + tcpiiu * pIIU; + ca_uint32_t lastBeaconNumber; + void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); + void logBeacon ( const char * pDiagnostic, + const double & currentPeriod, + const epicsTime & currentTime ); + void logBeaconDiscard ( unsigned beaconAdvance, + const epicsTime & currentTime ); + bhe ( const bhe & ); + bhe & operator = ( const bhe & ); + void * operator new ( size_t size ); + epicsShareFunc void operator delete ( void * ); +}; + +// using a wrapper class around the free list avoids +// Tornado 2.0.1 GNU compiler bugs +class bheFreeStore : public bheMemoryManager { +public: + bheFreeStore () {} + void * allocate ( size_t ); + void release ( void * ); +private: + tsFreeList < bhe, 0x100 > freeList; + bheFreeStore ( const bheFreeStore & ); + bheFreeStore & operator = ( const bheFreeStore & ); +}; + +inline void * bhe::operator new ( size_t size, + bheMemoryManager & mgr ) +{ + return mgr.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void bhe::operator delete ( void * pCadaver, + bheMemoryManager & mgr ) +{ + mgr.release ( pCadaver ); +} +#endif + +#endif // ifdef bheh + + diff --git a/src/ca/ca.rc b/src/ca/ca.rc new file mode 100755 index 000000000..cf25cdf24 --- /dev/null +++ b/src/ca/ca.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Channel Access Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Channel Access Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "ca\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "ca.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/ca/caConnTest.cpp b/src/ca/caConnTest.cpp new file mode 100644 index 000000000..21077398a --- /dev/null +++ b/src/ca/caConnTest.cpp @@ -0,0 +1,109 @@ +/*************************************************************************\ +* 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 +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "cadef.h" +#include "epicsTime.h" + +static unsigned channelCount = 0u; +static unsigned connCount = 0u; +static bool subsequentConnect = false; + +epicsTime begin; + +extern "C" void caConnTestConnHandler ( struct connection_handler_args args ) +{ + if ( args.op == CA_OP_CONN_UP ) { + if ( connCount == 0u ) { + if ( subsequentConnect ) { + printf ("the first channel connected\n"); + begin = epicsTime::getCurrent (); + } + } + connCount++; + // printf ( "." ); + // fflush ( stdout ); + if ( connCount == channelCount ) { + epicsTime current = epicsTime::getCurrent (); + double delay = current - begin; + printf ( "all channels connected after %f sec ( %f sec per channel)\n", + delay, delay / channelCount ); + } + } + else if ( args.op == CA_OP_CONN_DOWN ) { + if ( connCount == channelCount ) { + printf ( "channels are disconnected\n" ); + subsequentConnect = true; + } + connCount--; + if ( connCount == 0u ) { + printf ( "all channels are disconnected\n" ); + } + } + else { + assert ( 0 ); + } +} + +void caConnTest ( const char *pNameIn, unsigned channelCountIn, double delayIn ) +{ + unsigned iteration = 0u; + int status; + unsigned i; + chid *pChans; + + channelCount = channelCountIn; + + pChans = new chid [channelCount]; + + while ( 1 ) { + connCount = 0u; + subsequentConnect = false; + begin = epicsTime::getCurrent (); + + printf ( "initializing CA client library\n" ); + + status = ca_task_initialize(); + SEVCHK ( status, "CA init failed" ); + + printf ( "creating channels\n" ); + + for ( i = 0u; i < channelCount; i++ ) { + status = ca_search_and_connect ( pNameIn, + &pChans[i], caConnTestConnHandler, 0 ); + SEVCHK ( status, "CA search problems" ); + } + + printf ( "all channels were created\n" ); + + ca_pend_event ( delayIn ); + + if ( iteration & 1 ) { + for ( i = 0u; i < channelCount; i++ ) { + status = ca_clear_channel ( pChans[i] ); + SEVCHK ( status, "ca_clear_channel() problems" ); + } + printf ( "all channels were destroyed\n" ); + } + + printf ( "shutting down CA client library\n" ); + + status = ca_task_exit (); + SEVCHK ( status, "task exit problems" ); + + iteration++; + } + + //delete [] pChans; +} diff --git a/src/ca/caConnTestMain.cpp b/src/ca/caConnTestMain.cpp new file mode 100644 index 000000000..f3985801a --- /dev/null +++ b/src/ca/caConnTestMain.cpp @@ -0,0 +1,45 @@ +/*************************************************************************\ +* 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 +#include + +#include "caDiagnostics.h" + +int main ( int argc, char **argv ) +{ + double delay = 60.0 * 5.0; + unsigned count = 2000; + + if ( argc < 2 || argc > 4 ) { + printf ( "usage: %s < channel name > [ < count > ] [ < delay sec > ]\n", argv[0] ); + return -1; + } + + if ( argc >= 3 ) { + int nConverted = sscanf ( argv[2], "%u", &count ); + if ( nConverted != 1 ) { + printf ( "conversion failed, changing channel count arg \"%s\" to %u\n", + argv[1], count ); + } + } + + if ( argc >= 4 ) { + int nConverted = epicsScanDouble( argv[3], &delay ); + if ( nConverted != 1 ) { + printf ( "conversion failed, changing delay arg \"%s\" to %f\n", + argv[2], delay ); + } + } + + caConnTest ( argv[1], count, delay ); + + return 0; +} diff --git a/src/ca/caDiagnostics.h b/src/ca/caDiagnostics.h new file mode 100644 index 000000000..90221e1ed --- /dev/null +++ b/src/ca/caDiagnostics.h @@ -0,0 +1,38 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef caDiagnosticsh +#define caDiagnosticsh + +#include "cadef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum appendNumberFlag {appendNumber, dontAppendNumber}; +int catime ( const char *channelName, unsigned channelCount, enum appendNumberFlag appNF ); + +int acctst ( const char *pname, unsigned logggingInterestLevel, + unsigned channelCount, unsigned repetitionCount, + enum ca_preemptive_callback_select select ); + +#define CATIME_OK 0 +#define CATIME_ERROR -1 + +#ifdef __cplusplus +} +#endif + +void caConnTest ( const char *pNameIn, unsigned channelCountIn, double delayIn ); + +#endif /* caDiagnosticsh */ + + diff --git a/src/ca/caEventRate.cpp b/src/ca/caEventRate.cpp new file mode 100644 index 000000000..8beb16333 --- /dev/null +++ b/src/ca/caEventRate.cpp @@ -0,0 +1,139 @@ +/*************************************************************************\ +* 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 +#include +#include + +#include "cadef.h" +#include "dbDefs.h" +#include "epicsTime.h" +#include "errlog.h" + +/* + * event_handler() + */ +extern "C" void eventCallBack ( struct event_handler_args args ) +{ + unsigned *pCount = static_cast < unsigned * > ( args.usr ); + (*pCount)++; +} + +/* + * caEventRate () + */ +void caEventRate ( const char *pName, unsigned count ) +{ + static const double initialSamplePeriod = 1.0; + static const double maxSamplePeriod = 60.0 * 5.0; + unsigned eventCount = 0u; + + chid * pChidTable = new chid [ count ]; + + { + printf ( "Connecting to CA Channel \"%s\" %u times.", + pName, count ); + fflush ( stdout ); + + epicsTime begin = epicsTime::getCurrent (); + for ( unsigned i = 0u; i < count; i++ ) { + int status = ca_search ( pName, & pChidTable[i] ); + SEVCHK ( status, NULL ); + } + + int status = ca_pend_io ( 10000.0 ); + if ( status != ECA_NORMAL ) { + fprintf ( stderr, " not found.\n" ); + return; + } + epicsTime end = epicsTime::getCurrent (); + + printf ( " done(%f sec).\n", end - begin ); + } + + { + printf ( "Subscribing %u times.", count ); + fflush ( stdout ); + + epicsTime begin = epicsTime::getCurrent (); + for ( unsigned i = 0u; i < count; i++ ) { + int addEventStatus = ca_add_event ( DBR_FLOAT, + pChidTable[i], eventCallBack, &eventCount, NULL); + SEVCHK ( addEventStatus, __FILE__ ); + } + + int status = ca_flush_io (); + SEVCHK ( status, __FILE__ ); + + epicsTime end = epicsTime::getCurrent (); + + printf ( " done(%f sec).\n", end - begin ); + } + + { + printf ( "Waiting for initial value events." ); + fflush ( stdout ); + + // let the first one go by + epicsTime begin = epicsTime::getCurrent (); + while ( eventCount < count ) { + int status = ca_pend_event ( 0.01 ); + if ( status != ECA_TIMEOUT ) { + SEVCHK ( status, NULL ); + } + } + epicsTime end = epicsTime::getCurrent (); + + printf ( " done(%f sec).\n", end - begin ); + } + + double samplePeriod = initialSamplePeriod; + double X = 0.0; + double XX = 0.0; + unsigned N = 0u; + while ( true ) { + unsigned nEvents, lastEventCount, curEventCount; + + epicsTime beginPend = epicsTime::getCurrent (); + lastEventCount = eventCount; + int status = ca_pend_event ( samplePeriod ); + curEventCount = eventCount; + epicsTime endPend = epicsTime::getCurrent (); + if ( status != ECA_TIMEOUT ) { + SEVCHK ( status, NULL ); + } + + if ( curEventCount >= lastEventCount ) { + nEvents = curEventCount - lastEventCount; + } + else { + nEvents = ( UINT_MAX - lastEventCount ) + curEventCount + 1u; + } + + N++; + + double period = endPend - beginPend; + double Hz = nEvents / period; + + X += Hz; + XX += Hz * Hz; + + double mean = X / N; + double stdDev = sqrt ( XX / N - mean * mean ); + + printf ( "CA Event Rate (Hz): current %g mean %g std dev %g\n", + Hz, mean, stdDev ); + + if ( samplePeriod < maxSamplePeriod ) { + samplePeriod += samplePeriod; + } + } +} + diff --git a/src/ca/caEventRateMain.cpp b/src/ca/caEventRateMain.cpp new file mode 100644 index 000000000..725e66102 --- /dev/null +++ b/src/ca/caEventRateMain.cpp @@ -0,0 +1,38 @@ +/*************************************************************************\ +* 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 +#include + +void caEventRate ( const char *pName, unsigned count ); + +int main ( int argc, char **argv ) +{ + if ( argc < 2 || argc > 3 ) { + fprintf ( stderr, "usage: %s < PV name > [subscription count]\n", argv[0] ); + return 0; + } + + unsigned count; + if ( argc == 3 ) { + int status = sscanf ( argv[2], " %u ", & count ); + if ( status != 1 ) { + fprintf ( stderr, "expected unsigned integer 2nd argument\n" ); + return 0; + } + } + else { + count = 1; + } + + caEventRate ( argv[1], count ); + + return 0; +} diff --git a/src/ca/caProto.h b/src/ca/caProto.h new file mode 100644 index 000000000..f61f52e08 --- /dev/null +++ b/src/ca/caProto.h @@ -0,0 +1,215 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef __CAPROTO__ +#define __CAPROTO__ + +#define capStrOf(A) #A +#define capStrOfX(A) capStrOf ( A ) + +/* + * CA protocol revision + * TCP/UDP port number (bumped each major protocol change) + */ +#define CA_MAJOR_PROTOCOL_REVISION 4 +#define CA_VERSION_STRING( MINOR_REVISION ) \ +( capStrOfX ( CA_MAJOR_PROTOCOL_REVISION ) "." capStrOfX ( MINOR_REVISION ) ) +#define CA_UKN_MINOR_VERSION 0u /* unknown minor version */ +#if CA_MAJOR_PROTOCOL_REVISION == 4u +# define CA_V41(MINOR) ((MINOR)>=1u) +# define CA_V42(MINOR) ((MINOR)>=2u) +# define CA_V43(MINOR) ((MINOR)>=3u) +# define CA_V44(MINOR) ((MINOR)>=4u) +# define CA_V45(MINOR) ((MINOR)>=5u) +# define CA_V46(MINOR) ((MINOR)>=6u) +# define CA_V47(MINOR) ((MINOR)>=7u) +# define CA_V48(MINOR) ((MINOR)>=8u) +# define CA_V49(MINOR) ((MINOR)>=9u) /* large arrays, dispatch priorities */ +# define CA_V410(MINOR) ((MINOR)>=10u) /* beacon counter */ +# define CA_V411(MINOR) ((MINOR)>=11u) /* sequence numbers in UDP version command */ +# define CA_V412(MINOR) ((MINOR)>=12u) /* TCP-based search requests */ +# define CA_V413(MINOR) ((MINOR)>=13u) /* Allow zero length in requests. */ +#elif CA_MAJOR_PROTOCOL_REVISION > 4u +# define CA_V41(MINOR) ( 1u ) +# define CA_V42(MINOR) ( 1u ) +# define CA_V43(MINOR) ( 1u ) +# define CA_V44(MINOR) ( 1u ) +# define CA_V45(MINOR) ( 1u ) +# define CA_V46(MINOR) ( 1u ) +# define CA_V47(MINOR) ( 1u ) +# define CA_V48(MINOR) ( 1u ) +# define CA_V49(MINOR) ( 1u ) +# define CA_V410(MINOR) ( 1u ) +# define CA_V411(MINOR) ( 1u ) +# define CA_V412(MINOR) ( 1u ) +# define CA_V413(MINOR) ( 1u ) +#else +# define CA_V41(MINOR) ( 0u ) +# define CA_V42(MINOR) ( 0u ) +# define CA_V43(MINOR) ( 0u ) +# define CA_V44(MINOR) ( 0u ) +# define CA_V45(MINOR) ( 0u ) +# define CA_V46(MINOR) ( 0u ) +# define CA_V47(MINOR) ( 0u ) +# define CA_V48(MINOR) ( 0u ) +# define CA_V49(MINOR) ( 0u ) +# define CA_V410(MINOR) ( 0u ) +# define CA_V411(MINOR) ( 0u ) +# define CA_V412(MINOR) ( 0u ) +# define CA_V413(MINOR) ( 0u ) +#endif + +/* + * These port numbers are only used if the CA repeater and + * CA server port numbers cant be obtained from the EPICS + * environment variables "EPICS_CA_REPEATER_PORT" and + * "EPICS_CA_SERVER_PORT" + */ +#define CA_PORT_BASE IPPORT_USERRESERVED + 56U +#define CA_SERVER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u) +#define CA_REPEATER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u+1u) + +/* + * 1500 (max of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP) + * (the MTU of Ethernet is currently independent of its speed varient) + */ +#define ETHERNET_MAX_UDP ( 1500u - 20u - 8u ) +#define MAX_UDP_RECV ( 0xffff + 16u ) /* allow large frames to be received in the future */ +#define MAX_UDP_SEND 1024u /* original MAX_UDP */ +#define MAX_TCP ( 1024 * 16u ) /* so waveforms fit */ +#define MAX_MSG_SIZE ( MAX_TCP ) /* the larger of tcp and udp max */ + +#define CA_PROTO_PRIORITY_MIN 0u +#define CA_PROTO_PRIORITY_MAX 99u + +/* + * architecture independent types + * + * (so far this works on all archs we have ported to) + */ +typedef unsigned char ca_uint8_t; +typedef unsigned short ca_uint16_t; +typedef unsigned int ca_uint32_t; +typedef float ca_float32_t; +typedef ca_uint32_t caResId; + +#define ca_uint32_max 0xffffffff + + /* values for m_cmmd */ +#define CA_PROTO_VERSION 0u /* set minor version and priority (used to be NOOP cmd) */ +#define CA_PROTO_EVENT_ADD 1u /* add an event */ +#define CA_PROTO_EVENT_CANCEL 2u /* cancel an event */ +#define CA_PROTO_READ 3u /* read and return a channel value*/ +#define CA_PROTO_WRITE 4u /* write a channel value */ +#define CA_PROTO_SNAPSHOT 5u /* snapshot of the system */ +#define CA_PROTO_SEARCH 6u /* IOC channel search */ +#define CA_PROTO_BUILD 7u /* build - obsolete */ +#define CA_PROTO_EVENTS_OFF 8u /* flow control */ +#define CA_PROTO_EVENTS_ON 9u /* flow control */ +#define CA_PROTO_READ_SYNC 10u /* purge old reads */ +#define CA_PROTO_ERROR 11u /* an operation failed */ +#define CA_PROTO_CLEAR_CHANNEL 12u /* free chan resources */ +#define CA_PROTO_RSRV_IS_UP 13u /* CA server has joined the net */ +#define CA_PROTO_NOT_FOUND 14u /* channel not found */ +#define CA_PROTO_READ_NOTIFY 15u /* add a one shot event */ +#define CA_PROTO_READ_BUILD 16u /* read and build - obsolete */ +#define REPEATER_CONFIRM 17u /* registration confirmation */ +#define CA_PROTO_CREATE_CHAN 18u /* client creates channel in server */ +#define CA_PROTO_WRITE_NOTIFY 19u /* notify after write chan value */ +#define CA_PROTO_CLIENT_NAME 20u /* CA V4.1 identify client */ +#define CA_PROTO_HOST_NAME 21u /* CA V4.1 identify client */ +#define CA_PROTO_ACCESS_RIGHTS 22u /* CA V4.2 asynch access rights chg */ +#define CA_PROTO_ECHO 23u /* CA V4.3 connection verify */ +#define REPEATER_REGISTER 24u /* register for repeater fan out */ +#define CA_PROTO_SIGNAL 25u /* knock the server out of select */ +#define CA_PROTO_CREATE_CH_FAIL 26u /* unable to create chan resource in server */ +#define CA_PROTO_SERVER_DISCONN 27u /* server deletes PV (or channel) */ + +#define CA_PROTO_LAST_CMMD CA_PROTO_SERVER_DISCONN + +/* + * for use with search and not_found (if search fails and + * its not a broadcast tell the client to look elesewhere) + */ +#define DOREPLY 10u +#define DONTREPLY 5u + +/* + * for use with the m_dataType field in UDP messages emitted by servers + */ +#define sequenceNoIsValid 1 + +/* size of object in bytes rounded up to nearest oct word */ +#define OCT_ROUND(A) (((A)+7)/8) +#define OCT_SIZEOF(A) (OCT_ROUND(sizeof(A))) + +/* size of object in bytes rounded up to nearest long word */ +#define QUAD_ROUND(A) ((A)+3)/4) +#define QUAD_SIZEOF(A) (QUAD_ROUND(sizeof(A))) + +/* size of object in bytes rounded up to nearest short word */ +#define BI_ROUND(A) (((A)+1)/2) +#define BI_SIZEOF(A) (BI_ROUND(sizeof(A))) + +/* + * For communicating access rights to the clients + * + * (placed in m_available hdr field of CA_PROTO_ACCESS_RIGHTS cmmd + */ +#define CA_PROTO_ACCESS_RIGHT_READ (1u<<0u) +#define CA_PROTO_ACCESS_RIGHT_WRITE (1u<<1u) + +/* + * All structures passed in the protocol must have individual + * fields aligned on natural boundaries. + * + * NOTE: all structures declared in this file must have a + * byte count which is evenly divisible by 8 matching + * the largest atomic data type in db_access.h. + */ +#define CA_MESSAGE_ALIGN(A) (OCT_ROUND(A)<<3u) + +/* + * the common part of each message sent/recv by the + * CA server. + */ +typedef struct ca_hdr { + ca_uint16_t m_cmmd; /* operation to be performed */ + ca_uint16_t m_postsize; /* size of payload */ + ca_uint16_t m_dataType; /* operation data type */ + ca_uint16_t m_count; /* operation data count */ + ca_uint32_t m_cid; /* channel identifier */ + ca_uint32_t m_available; /* protocol stub dependent */ +} caHdr; + +/* + * for monitor (event) message extension + */ +struct mon_info { + ca_float32_t m_lval; /* low delta */ + ca_float32_t m_hval; /* high delta */ + ca_float32_t m_toval; /* period btween samples */ + ca_uint16_t m_mask; /* event select mask */ + ca_uint16_t m_pad; /* extend to 32 bits */ +}; + +/* + * PV names greater than this length assumed to be invalid + */ +#define unreasonablePVNameSize 500u + +#endif /* __CAPROTO__ */ + diff --git a/src/ca/caRepeater.cpp b/src/ca/caRepeater.cpp new file mode 100644 index 000000000..9879af2b4 --- /dev/null +++ b/src/ca/caRepeater.cpp @@ -0,0 +1,42 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * CA UDP repeater standalone executable + * + * Author: Jeff Hill + * Date: 3-27-90 + * + * PURPOSE: + * Broadcasts fan out over the LAN, but old IP kernels do not allow + * two processes on the same machine to get the same broadcast + * (and modern IP kernels do not allow two processes on the same machine + * to receive the same unicast). + * + * This code fans out UDP messages sent to the CA repeater port + * to all CA client processes that have subscribed. + * + * NOTES: + * + * see repeater.c + * + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "epicsAssert.h" +#include "udpiiu.h" + +int main() +{ + ca_repeater (); + return ( 0 ); +} + diff --git a/src/ca/caServerID.h b/src/ca/caServerID.h new file mode 100644 index 000000000..08bfdd5df --- /dev/null +++ b/src/ca/caServerID.h @@ -0,0 +1,88 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#ifndef caServerIDh +#define caServerIDh + +#include "osiSock.h" +#include "resourceLib.h" +#include "caProto.h" + +class caServerID { +public: + caServerID ( const struct sockaddr_in & addrIn, unsigned priority ); + bool operator == ( const caServerID & ) const; + resTableIndex hash () const; + osiSockAddr address () const; + unsigned priority () const; +private: + struct sockaddr_in addr; + ca_uint8_t pri; +}; + +inline caServerID::caServerID ( + const struct sockaddr_in & addrIn, unsigned priorityIn ) : + addr ( addrIn ), pri ( static_cast ( priorityIn ) ) +{ + assert ( priorityIn <= 0xff ); +} + +inline bool caServerID::operator == ( const caServerID & rhs ) const +{ + if ( this->addr.sin_addr.s_addr == rhs.addr.sin_addr.s_addr && + this->addr.sin_port == rhs.addr.sin_port && + this->pri == rhs.pri ) { + return true; + } + return false; +} + +inline resTableIndex caServerID::hash () const +{ + // start with a very small server table to speed + // up the flush traverse for the frequent case - + // a small numbers of servers + const unsigned caServerMinIndexBitWidth = 2u; + const unsigned caServerMaxIndexBitWidth = 32u; + + unsigned index; + index = this->addr.sin_addr.s_addr; + index ^= this->addr.sin_port; + index ^= this->addr.sin_port >> 8u; + index ^= this->pri; + return integerHash ( caServerMinIndexBitWidth, + caServerMaxIndexBitWidth, index ); +} + +inline osiSockAddr caServerID::address () const +{ + osiSockAddr tmp; + tmp.ia = this->addr; + return tmp; +} + +inline unsigned caServerID::priority () const +{ + return this->pri; +} + +#endif // ifdef caServerID + + diff --git a/src/ca/ca_client_context.cpp b/src/ca/ca_client_context.cpp new file mode 100644 index 000000000..f0d71f5b3 --- /dev/null +++ b/src/ca/ca_client_context.cpp @@ -0,0 +1,791 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifdef _MSC_VER +# pragma warning(disable:4355) +#endif + +#include +#include // vxWorks 6.0 requires this include +#include + +#include "epicsExit.h" +#include "errlog.h" +#include "locationException.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" +#include "cac.h" + +epicsShareDef epicsThreadPrivateId caClientCallbackThreadId; + +static epicsThreadOnceId cacOnce = EPICS_THREAD_ONCE_INIT; + +const unsigned ca_client_context :: flushBlockThreshold = 0x58000; + +extern "C" void cacExitHandler ( void *) +{ + epicsThreadPrivateDelete ( caClientCallbackThreadId ); + caClientCallbackThreadId = 0; + delete ca_client_context::pDefaultServiceInstallMutex; +} + +// runs once only for each process +extern "C" void cacOnceFunc ( void * ) +{ + caClientCallbackThreadId = epicsThreadPrivateCreate (); + assert ( caClientCallbackThreadId ); + ca_client_context::pDefaultServiceInstallMutex = new epicsMutex; + epicsAtExit ( cacExitHandler,0 ); +} + +extern epicsThreadPrivateId caClientContextId; + +cacService * ca_client_context::pDefaultService = 0; +epicsMutex * ca_client_context::pDefaultServiceInstallMutex; + +ca_client_context::ca_client_context ( bool enablePreemptiveCallback ) : + createdByThread ( epicsThreadGetIdSelf () ), + ca_exception_func ( 0 ), ca_exception_arg ( 0 ), + pVPrintfFunc ( errlogVprintf ), fdRegFunc ( 0 ), fdRegArg ( 0 ), + pndRecvCnt ( 0u ), ioSeqNo ( 0u ), callbackThreadsPending ( 0u ), + localPort ( 0 ), fdRegFuncNeedsToBeCalled ( false ), + noWakeupSincePend ( true ) +{ + static const unsigned short PORT_ANY = 0u; + + if ( ! osiSockAttach () ) { + throwWithLocation ( noSocket () ); + } + + epicsThreadOnce ( & cacOnce, cacOnceFunc, 0 ); + { + epicsGuard < epicsMutex > guard ( *ca_client_context::pDefaultServiceInstallMutex ); + if ( ca_client_context::pDefaultService ) { + this->pServiceContext.reset ( + & ca_client_context::pDefaultService->contextCreate ( + this->mutex, this->cbMutex, *this ) ); + } + else { + this->pServiceContext.reset ( new cac ( this->mutex, this->cbMutex, *this ) ); + } + } + + this->sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( this->sock == INVALID_SOCKET ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + this->printFormated ( + "ca_client_context: unable to create " + "datagram socket because = \"%s\"\n", + sockErrBuf ); + throwWithLocation ( noSocket () ); + } + + { + osiSockIoctl_t yes = true; + int status = socket_ioctl ( this->sock, // X aCC 392 + FIONBIO, & yes); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy ( this->sock ); + this->printFormated ( + "%s: non blocking IO set fail because \"%s\"\n", + __FILE__, sockErrBuf ); + throwWithLocation ( noSocket () ); + } + } + + // force a bind to an unconstrained address so we can obtain + // the local port number below + { + osiSockAddr addr; + memset ( (char *)&addr, 0 , sizeof ( addr ) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); + addr.ia.sin_port = htons ( PORT_ANY ); // X aCC 818 + int status = bind (this->sock, &addr.sa, sizeof (addr) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy (this->sock); + this->printFormated ( + "CAC: unable to bind to an unconstrained " + "address because = \"%s\"\n", + sockErrBuf ); + throwWithLocation ( noSocket () ); + } + } + + { + osiSockAddr tmpAddr; + osiSocklen_t saddr_length = sizeof ( tmpAddr ); + int status = getsockname ( this->sock, & tmpAddr.sa, & saddr_length ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy ( this->sock ); + this->printFormated ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf ); + throwWithLocation ( noSocket () ); + } + if ( tmpAddr.sa.sa_family != AF_INET) { + epicsSocketDestroy ( this->sock ); + this->printFormated ( "CAC: UDP socket was not inet addr family\n" ); + throwWithLocation ( noSocket () ); + } + this->localPort = htons ( tmpAddr.ia.sin_port ); + } + + epics_auto_ptr < epicsGuard < epicsMutex > > pCBGuard; + if ( ! enablePreemptiveCallback ) { + pCBGuard.reset ( new epicsGuard < epicsMutex > ( this->cbMutex ) ); + } + + // multiple steps ensure exception safety + this->pCallbackGuard = pCBGuard; +} + +ca_client_context::~ca_client_context () +{ + if ( this->fdRegFunc ) { + ( *this->fdRegFunc ) + ( this->fdRegArg, this->sock, false ); + } + epicsSocketDestroy ( this->sock ); + + osiSockRelease (); + + // force a logical shutdown order + // so that the cac class does not hang its + // receive threads during their shutdown sequence + // and so that classes using this classes mutex + // are destroyed before the mutex is destroyed + if ( this->pCallbackGuard.get() ) { + epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); + this->pServiceContext.reset ( 0 ); + } + else { + this->pServiceContext.reset ( 0 ); + } +} + +void ca_client_context::destroyGetCopy ( + epicsGuard < epicsMutex > & guard, getCopy & gc ) +{ + guard.assertIdenticalMutex ( this->mutex ); + gc.~getCopy (); + this->getCopyFreeList.release ( & gc ); +} + +void ca_client_context::destroyGetCallback ( + epicsGuard < epicsMutex > & guard, getCallback & gcb ) +{ + guard.assertIdenticalMutex ( this->mutex ); + gcb.~getCallback (); + this->getCallbackFreeList.release ( & gcb ); +} + +void ca_client_context::destroyPutCallback ( + epicsGuard < epicsMutex > & guard, putCallback & pcb ) +{ + guard.assertIdenticalMutex ( this->mutex ); + pcb.~putCallback (); + this->putCallbackFreeList.release ( & pcb ); +} + +void ca_client_context::destroySubscription ( + epicsGuard < epicsMutex > & guard, oldSubscription & os ) +{ + guard.assertIdenticalMutex ( this->mutex ); + os.~oldSubscription (); + this->subscriptionFreeList.release ( & os ); +} + +void ca_client_context::changeExceptionEvent ( + caExceptionHandler * pfunc, void * arg ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->ca_exception_func = pfunc; + this->ca_exception_arg = arg; +// should block here until releated callback in progress completes +} + +void ca_client_context::replaceErrLogHandler ( + caPrintfFunc * ca_printf_func ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( ca_printf_func ) { + this->pVPrintfFunc = ca_printf_func; + } + else { + this->pVPrintfFunc = epicsVprintf; + } +// should block here until releated callback in progress completes +} + +void ca_client_context::registerForFileDescriptorCallBack ( + CAFDHANDLER *pFunc, void *pArg ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->fdRegFunc = pFunc; + this->fdRegArg = pArg; + this->fdRegFuncNeedsToBeCalled = true; + if ( pFunc ) { + // the receive thread might already be blocking + // w/o having sent the wakeup message + this->_sendWakeupMsg (); + } +// should block here until releated callback in progress completes +} + +int ca_client_context :: printFormated ( + const char *pformat, ... ) const +{ + va_list theArgs; + int status; + + va_start ( theArgs, pformat ); + + status = this->ca_client_context :: varArgsPrintFormated ( pformat, theArgs ); + + va_end ( theArgs ); + + return status; +} + +int ca_client_context :: varArgsPrintFormated ( + const char *pformat, va_list args ) const // X aCC 361 +{ + caPrintfFunc * pFunc; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + pFunc = this->pVPrintfFunc; + } + if ( pFunc ) { + return ( *pFunc ) ( pformat, args ); + } + else { + return :: vfprintf ( stderr, pformat, args ); + } +} + +void ca_client_context::exception ( + epicsGuard < epicsMutex > & guard, int stat, const char * pCtx, + const char * pFile, unsigned lineNo ) +{ + struct exception_handler_args args; + caExceptionHandler * pFunc = this->ca_exception_func; + void * pArg = this->ca_exception_arg; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + // NOOP if they disable exceptions + if ( pFunc ) { + args.chid = NULL; + args.type = TYPENOTCONN; + args.count = 0; + args.addr = NULL; + args.stat = stat; + args.op = CA_OP_OTHER; + args.ctx = pCtx; + args.pFile = pFile; + args.lineNo = lineNo; + args.usr = pArg; + ( *pFunc ) ( args ); + } + else { + this->signal ( stat, pFile, lineNo, pCtx ); + } + } +} + +void ca_client_context::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, + unsigned type, arrayElementCount count, unsigned op ) +{ + struct exception_handler_args args; + caExceptionHandler * pFunc = this->ca_exception_func; + void * pArg = this->ca_exception_arg; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + // NOOP if they disable exceptions + if ( pFunc ) { + args.chid = &chan; + args.type = type; + args.count = count; + args.addr = NULL; + args.stat = status; + args.op = op; + args.ctx = pContext; + args.pFile = pFileName; + args.lineNo = lineNo; + args.usr = pArg; + ( *pFunc ) ( args ); + } + else { + this->signal ( status, pFileName, lineNo, + "op=%u, channel=%s, type=%s, count=%lu, ctx=\"%s\"", + op, ca_name ( &chan ), + dbr_type_to_text ( static_cast ( type ) ), + count, pContext ); + } + } +} + +void ca_client_context::signal ( int ca_status, const char * pfilenm, + int lineno, const char * pFormat, ... ) +{ + va_list theArgs; + va_start ( theArgs, pFormat ); + this->vSignal ( ca_status, pfilenm, lineno, pFormat, theArgs); + va_end ( theArgs ); +} + +void ca_client_context :: vSignal ( + int ca_status, const char *pfilenm, + int lineno, const char *pFormat, va_list args ) +{ + static const char *severity[] = + { + "Warning", + "Success", + "Error", + "Info", + "Fatal", + "Fatal", + "Fatal", + "Fatal" + }; + + this->printFormated ( "CA.Client.Exception...............................................\n" ); + + this->printFormated ( " %s: \"%s\"\n", + severity[ CA_EXTRACT_SEVERITY ( ca_status ) ], + ca_message ( ca_status ) ); + + if ( pFormat ) { + this->printFormated ( " Context: \"" ); + this->varArgsPrintFormated ( pFormat, args ); + this->printFormated ( "\"\n" ); + } + + if ( pfilenm ) { + this->printFormated ( " Source File: %s line %d\n", + pfilenm, lineno ); + } + + epicsTime current = epicsTime::getCurrent (); + char date[64]; + current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); + this->printFormated ( " Current Time: %s\n", date ); + + /* + * Terminate execution if unsuccessful + */ + if( ! ( ca_status & CA_M_SUCCESS ) && + CA_EXTRACT_SEVERITY ( ca_status ) != CA_K_WARNING ){ + errlogFlush (); + abort (); + } + + this->printFormated ( "..................................................................\n" ); +} + +void ca_client_context::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + ::printf ( "ca_client_context at %p pndRecvCnt=%u ioSeqNo=%u\n", + static_cast ( this ), + this->pndRecvCnt, this->ioSeqNo ); + + if ( level > 0u ) { + this->pServiceContext->show ( guard, level - 1u ); + ::printf ( "\tpreemptive callback is %s\n", + this->pCallbackGuard.get() ? "disabled" : "enabled" ); + ::printf ( "\tthere are %u unsatisfied IO operations blocking ca_pend_io()\n", + this->pndRecvCnt ); + ::printf ( "\tthe current io sequence number is %u\n", + this->ioSeqNo ); + ::printf ( "IO done event:\n"); + this->ioDone.show ( level - 1u ); + ::printf ( "Synchronous group identifier hash table:\n" ); + this->sgTable.show ( level - 1u ); + } +} + +void ca_client_context::attachToClientCtx () +{ + assert ( ! epicsThreadPrivateGet ( caClientContextId ) ); + epicsThreadPrivateSet ( caClientContextId, this ); +} + +void ca_client_context::incrementOutstandingIO ( + epicsGuard < epicsMutex > & guard, unsigned ioSeqNoIn ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->ioSeqNo == ioSeqNoIn ) { + assert ( this->pndRecvCnt < UINT_MAX ); + this->pndRecvCnt++; + } +} + +void ca_client_context::decrementOutstandingIO ( + epicsGuard < epicsMutex > & guard, unsigned ioSeqNoIn ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->ioSeqNo == ioSeqNoIn ) { + assert ( this->pndRecvCnt > 0u ); + this->pndRecvCnt--; + if ( this->pndRecvCnt == 0u ) { + this->ioDone.signal (); + } + } +} + +// !!!! This routine is only visible in the old interface - or in a new ST interface. +// !!!! In the old interface we restrict thread attach so that calls from threads +// !!!! other than the initializing thread are not allowed if preemptive callback +// !!!! is disabled. This prevents the preemptive callback lock from being released +// !!!! by other threads than the one that locked it. +// +int ca_client_context::pendIO ( const double & timeout ) +{ + // prevent recursion nightmares by disabling calls to + // pendIO () from within a CA callback. + if ( epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { + return ECA_EVDISALLOW; + } + + int status = ECA_NORMAL; + epicsTime beg_time = epicsTime::getCurrent (); + double remaining = timeout; + + epicsGuard < epicsMutex > guard ( this->mutex ); + + this->flush ( guard ); + + while ( this->pndRecvCnt > 0 ) { + if ( remaining < CAC_SIGNIFICANT_DELAY ) { + status = ECA_TIMEOUT; + break; + } + + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->blockForEventAndEnableCallbacks ( this->ioDone, remaining ); + } + + double delay = epicsTime::getCurrent () - beg_time; + if ( delay < timeout ) { + remaining = timeout - delay; + } + else { + remaining = 0.0; + } + } + + this->ioSeqNo++; + this->pndRecvCnt = 0u; + + return status; +} + +// !!!! This routine is only visible in the old interface - or in a new ST interface. +// !!!! In the old interface we restrict thread attach so that calls from threads +// !!!! other than the initializing thread are not allowed if preemptive callback +// !!!! is disabled. This prevents the preemptive callback lock from being released +// !!!! by other threads than the one that locked it. +// +int ca_client_context::pendEvent ( const double & timeout ) +{ + // prevent recursion nightmares by disabling calls to + // pendIO () from within a CA callback. + if ( epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { + return ECA_EVDISALLOW; + } + + epicsTime current = epicsTime::getCurrent (); + + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->flush ( guard ); + } + + // process at least once if preemptive callback is disabled + if ( this->pCallbackGuard.get() ) { + epicsGuardRelease < epicsMutex > cbUnguard ( *this->pCallbackGuard ); + epicsGuard < epicsMutex > guard ( this->mutex ); + + // + // This is needed because in non-preemptive callback mode + // legacy applications that use file descriptor managers + // will register for ca receive thread activity and keep + // calling ca_pend_event until all of the socket data has + // been read. We must guarantee that other threads get a + // chance to run if there is data in any of the sockets. + // + if ( this->fdRegFunc ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + + // remove short udp message sent to wake + // up a file descriptor manager + osiSockAddr tmpAddr; + osiSocklen_t addrSize = sizeof ( tmpAddr.sa ); + char buf = 0; + int status = 0; + do { + status = recvfrom ( this->sock, & buf, sizeof ( buf ), + 0, & tmpAddr.sa, & addrSize ); + } while ( status > 0 ); + } + while ( this->callbackThreadsPending > 0 ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->callbackThreadActivityComplete.wait ( 30.0 ); + } + this->noWakeupSincePend = true; + } + + double elapsed = epicsTime::getCurrent() - current; + double delay; + + if ( timeout > elapsed ) { + delay = timeout - elapsed; + } + else { + delay = 0.0; + } + + if ( delay >= CAC_SIGNIFICANT_DELAY ) { + if ( this->pCallbackGuard.get() ) { + epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); + epicsThreadSleep ( delay ); + } + else { + epicsThreadSleep ( delay ); + } + } + + return ECA_TIMEOUT; +} + +void ca_client_context::blockForEventAndEnableCallbacks ( + epicsEvent & event, const double & timeout ) +{ + if ( this->pCallbackGuard.get() ) { + epicsGuardRelease < epicsMutex > unguard ( *this->pCallbackGuard ); + event.wait ( timeout ); + } + else { + event.wait ( timeout ); + } +} + +void ca_client_context::callbackProcessingInitiateNotify () +{ + // if preemptive callback is enabled then this is a noop + if ( this->pCallbackGuard.get() ) { + bool sendNeeded = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->callbackThreadsPending++; + if ( this->fdRegFunc && this->noWakeupSincePend ) { + this->noWakeupSincePend = false; + sendNeeded = true; + } + } + if ( sendNeeded ) { + _sendWakeupMsg (); + } + } +} + +void ca_client_context :: _sendWakeupMsg () +{ + // send short udp message to wake up a file descriptor manager + // when a message arrives + osiSockAddr tmpAddr; + tmpAddr.ia.sin_family = AF_INET; + tmpAddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); + tmpAddr.ia.sin_port = htons ( this->localPort ); + char buf = 0; + sendto ( this->sock, & buf, sizeof ( buf ), + 0, & tmpAddr.sa, sizeof ( tmpAddr.sa ) ); +} + +void ca_client_context::callbackProcessingCompleteNotify () +{ + // if preemptive callback is enabled then this is a noop + if ( this->pCallbackGuard.get() ) { + bool signalNeeded = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->callbackThreadsPending <= 1 ) { + if ( this->callbackThreadsPending == 1 ) { + this->callbackThreadsPending = 0; + signalNeeded = true; + } + } + else { + this->callbackThreadsPending--; + } + } + if ( signalNeeded ) { + this->callbackThreadActivityComplete.signal (); + } + } +} + +cacChannel & ca_client_context::createChannel ( + epicsGuard < epicsMutex > & guard, const char * pChannelName, + cacChannelNotify & chan, cacChannel::priLev pri ) +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->pServiceContext->createChannel ( + guard, pChannelName, chan, pri ); +} + +void ca_client_context::flush ( epicsGuard < epicsMutex > & guard ) +{ + this->pServiceContext->flush ( guard ); +} + +unsigned ca_client_context::circuitCount () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->pServiceContext->circuitCount ( guard ); +} + +unsigned ca_client_context::beaconAnomaliesSinceProgramStart () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->pServiceContext->beaconAnomaliesSinceProgramStart ( guard ); +} + +void ca_client_context::installCASG ( + epicsGuard < epicsMutex > & guard, CASG & sg ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->sgTable.idAssignAdd ( sg ); +} + +void ca_client_context::uninstallCASG ( + epicsGuard < epicsMutex > & guard, CASG & sg ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->sgTable.remove ( sg ); +} + +CASG * ca_client_context::lookupCASG ( + epicsGuard < epicsMutex > & guard, unsigned idIn ) +{ + guard.assertIdenticalMutex ( this->mutex ); + CASG * psg = this->sgTable.lookup ( idIn ); + if ( psg ) { + if ( ! psg->verify ( guard ) ) { + psg = 0; + } + } + return psg; +} + +void ca_client_context::selfTest () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->sgTable.verify (); + this->pServiceContext->selfTest ( guard ); +} + +epicsMutex & ca_client_context::mutexRef () const +{ + return this->mutex; +} + +cacContext & ca_client_context::createNetworkContext ( + epicsMutex & mutexIn, epicsMutex & cbMutexIn ) +{ + return * new cac ( mutexIn, cbMutexIn, *this ); +} + +void ca_client_context::installDefaultService ( cacService & service ) +{ + epicsThreadOnce ( & cacOnce, cacOnceFunc, 0 ); + + epicsGuard < epicsMutex > guard ( *ca_client_context::pDefaultServiceInstallMutex ); + if ( ca_client_context::pDefaultService ) { + throw std::logic_error + ( "CA in-memory service already installed and can't be replaced"); + } + ca_client_context::pDefaultService = & service; +} + +void epicsShareAPI caInstallDefaultService ( cacService & service ) +{ + ca_client_context::installDefaultService ( service ); +} + +epicsShareFunc int epicsShareAPI ca_clear_subscription ( evid pMon ) +{ + oldChannelNotify & chan = pMon->channel (); + ca_client_context & cac = chan.getClientCtx (); + epicsGuard < epicsMutex > guard ( cac.mutex ); + try { + // if this stalls out on a live circuit then an exception + // can be forthcoming which we must ignore as the clear + // request must always be successful + chan.eliminateExcessiveSendBacklog ( guard ); + } + catch ( cacChannel::notConnected & ) { + // intentionally ignored + } + pMon->cancel ( guard ); + return ECA_NORMAL; +} + +void ca_client_context :: eliminateExcessiveSendBacklog ( + epicsGuard < epicsMutex > & guard, cacChannel & chan ) +{ + if ( chan.requestMessageBytesPending ( guard ) > + ca_client_context :: flushBlockThreshold ) { + if ( this->pCallbackGuard.get() && + this->createdByThread == epicsThreadGetIdSelf () ) { + // we need to be very careful about lock hierarchy + // inversion in this situation + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < epicsMutex > cbunguard ( + * this->pCallbackGuard.get() ); + { + epicsGuard < epicsMutex > nestedGuard ( this->mutex ); + chan.flush ( nestedGuard ); + } + } + } + else { + chan.flush ( guard ); + } + } +} diff --git a/src/ca/cac.cpp b/src/ca/cac.cpp new file mode 100644 index 000000000..42a491fec --- /dev/null +++ b/src/ca/cac.cpp @@ -0,0 +1,1309 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + * + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include +#include +#include // vxWorks 6.0 requires this include + +#include "dbDefs.h" +#include "epicsGuard.h" +#include "epicsVersion.h" +#include "osiProcess.h" +#include "epicsSignal.h" +#include "envDefs.h" +#include "locationException.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "addrList.h" +#include "iocinf.h" +#include "cac.h" +#include "inetAddrID.h" +#include "caServerID.h" +#include "virtualCircuit.h" +#include "syncGroup.h" +#include "nciu.h" +#include "autoPtrRecycle.h" +#include "msgForMultiplyDefinedPV.h" +#include "udpiiu.h" +#include "bhe.h" +#include "net_convert.h" +#include "autoPtrFreeList.h" +#include "noopiiu.h" + +static const char pVersionCAC[] = + "@(#) " EPICS_VERSION_STRING + ", CA Client Library " __DATE__; + +// TCP response dispatch table +const cac::pProtoStubTCP cac::tcpJumpTableCAC [] = +{ + &cac::versionAction, + &cac::eventRespAction, + &cac::badTCPRespAction, + &cac::readRespAction, + &cac::badTCPRespAction, + &cac::badTCPRespAction, + &cac::searchRespAction, + &cac::badTCPRespAction, + &cac::badTCPRespAction, + &cac::badTCPRespAction, + // legacy CA_PROTO_READ_SYNC used as an echo with legacy server + &cac::echoRespAction, + &cac::exceptionRespAction, + &cac::clearChannelRespAction, + &cac::badTCPRespAction, + &cac::badTCPRespAction, + &cac::readNotifyRespAction, + &cac::badTCPRespAction, + &cac::badTCPRespAction, + &cac::createChannelRespAction, + &cac::writeNotifyRespAction, + &cac::badTCPRespAction, + &cac::badTCPRespAction, + &cac::accessRightsRespAction, + &cac::echoRespAction, + &cac::badTCPRespAction, + &cac::badTCPRespAction, + &cac::verifyAndDisconnectChan, + &cac::verifyAndDisconnectChan +}; + +// TCP exception dispatch table +const cac::pExcepProtoStubTCP cac::tcpExcepJumpTableCAC [] = +{ + &cac::defaultExcep, // CA_PROTO_VERSION + &cac::eventAddExcep, // CA_PROTO_EVENT_ADD + &cac::defaultExcep, // CA_PROTO_EVENT_CANCEL + &cac::readExcep, // CA_PROTO_READ + &cac::writeExcep, // CA_PROTO_WRITE + &cac::defaultExcep, // CA_PROTO_SNAPSHOT + &cac::defaultExcep, // CA_PROTO_SEARCH + &cac::defaultExcep, // CA_PROTO_BUILD + &cac::defaultExcep, // CA_PROTO_EVENTS_OFF + &cac::defaultExcep, // CA_PROTO_EVENTS_ON + &cac::defaultExcep, // CA_PROTO_READ_SYNC + &cac::defaultExcep, // CA_PROTO_ERROR + &cac::defaultExcep, // CA_PROTO_CLEAR_CHANNEL + &cac::defaultExcep, // CA_PROTO_RSRV_IS_UP + &cac::defaultExcep, // CA_PROTO_NOT_FOUND + &cac::readNotifyExcep, // CA_PROTO_READ_NOTIFY + &cac::defaultExcep, // CA_PROTO_READ_BUILD + &cac::defaultExcep, // REPEATER_CONFIRM + &cac::defaultExcep, // CA_PROTO_CREATE_CHAN + &cac::writeNotifyExcep, // CA_PROTO_WRITE_NOTIFY + &cac::defaultExcep, // CA_PROTO_CLIENT_NAME + &cac::defaultExcep, // CA_PROTO_HOST_NAME + &cac::defaultExcep, // CA_PROTO_ACCESS_RIGHTS + &cac::defaultExcep, // CA_PROTO_ECHO + &cac::defaultExcep, // REPEATER_REGISTER + &cac::defaultExcep, // CA_PROTO_SIGNAL + &cac::defaultExcep, // CA_PROTO_CREATE_CH_FAIL + &cac::defaultExcep // CA_PROTO_SERVER_DISCONN +}; + +// +// cac::cac () +// +cac::cac ( + epicsMutex & mutualExclusionIn, + epicsMutex & callbackControlIn, + cacContextNotify & notifyIn ) : + _refLocalHostName ( localHostNameCache.getReference () ), + programBeginTime ( epicsTime::getCurrent() ), + connTMO ( CA_CONN_VERIFY_PERIOD ), + mutex ( mutualExclusionIn ), + cbMutex ( callbackControlIn ), + ipToAEngine ( ipAddrToAsciiEngine::allocate () ), + timerQueue ( epicsTimerQueueActive::allocate ( false, + lowestPriorityLevelAbove(epicsThreadGetPrioritySelf()) ) ), + pUserName ( 0 ), + pudpiiu ( 0 ), + tcpSmallRecvBufFreeList ( 0 ), + tcpLargeRecvBufFreeList ( 0 ), + notify ( notifyIn ), + initializingThreadsId ( epicsThreadGetIdSelf() ), + initializingThreadsPriority ( epicsThreadGetPrioritySelf() ), + maxRecvBytesTCP ( MAX_TCP ), + maxContigFrames ( contiguousMsgCountWhichTriggersFlowControl ), + beaconAnomalyCount ( 0u ), + iiuExistenceCount ( 0u ), + cacShutdownInProgress ( false ) +{ + if ( ! osiSockAttach () ) { + throwWithLocation ( caErrorCode (ECA_INTERNAL) ); + } + + try { + long status; + + /* + * Certain os, such as HPUX, do not unblock a socket system call + * when another thread asynchronously calls both shutdown() and + * close(). To solve this problem we need to employ OS specific + * mechanisms. + */ + epicsSignalInstallSigAlarmIgnore (); + epicsSignalInstallSigPipeIgnore (); + + { + char tmp[256]; + size_t len; + osiGetUserNameReturn gunRet; + + gunRet = osiGetUserName ( tmp, sizeof (tmp) ); + if ( gunRet != osiGetUserNameSuccess ) { + tmp[0] = '\0'; + } + len = strlen ( tmp ) + 1; + this->pUserName = new char [ len ]; + strncpy ( this->pUserName, tmp, len ); + } + + this->_serverPort = + envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, + static_cast (CA_SERVER_PORT) ); + + status = envGetDoubleConfigParam ( &EPICS_CA_CONN_TMO, &this->connTMO ); + if ( status ) { + this->connTMO = CA_CONN_VERIFY_PERIOD; + epicsGuard < epicsMutex > cbGuard ( this->cbMutex ); + errlogPrintf ( "EPICS \"%s\" double fetch failed\n", EPICS_CA_CONN_TMO.name ); + errlogPrintf ( "Defaulting \"%s\" = %f\n", EPICS_CA_CONN_TMO.name, this->connTMO ); + } + + long maxBytesAsALong; + status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong ); + if ( status || maxBytesAsALong < 0 ) { + errlogPrintf ( "cac: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); + } + else { + /* allow room for the protocol header so that they get the array size they requested */ + static const unsigned headerSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); + ca_uint32_t maxBytes = ( unsigned ) maxBytesAsALong; + if ( maxBytes < 0xffffffff - headerSize ) { + maxBytes += headerSize; + } + else { + maxBytes = 0xffffffff; + } + if ( maxBytes < MAX_TCP ) { + errlogPrintf ( "cac: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); + } + else { + this->maxRecvBytesTCP = maxBytes; + } + } + freeListInitPvt ( &this->tcpSmallRecvBufFreeList, MAX_TCP, 1 ); + if ( ! this->tcpSmallRecvBufFreeList ) { + throw std::bad_alloc (); + } + + freeListInitPvt ( &this->tcpLargeRecvBufFreeList, this->maxRecvBytesTCP, 1 ); + if ( ! this->tcpLargeRecvBufFreeList ) { + throw std::bad_alloc (); + } + unsigned bufsPerArray = this->maxRecvBytesTCP / comBuf::capacityBytes (); + if ( bufsPerArray > 1u ) { + maxContigFrames = bufsPerArray * + contiguousMsgCountWhichTriggersFlowControl; + } + } + catch ( ... ) { + osiSockRelease (); + delete [] this->pUserName; + if ( this->tcpSmallRecvBufFreeList ) { + freeListCleanup ( this->tcpSmallRecvBufFreeList ); + } + if ( this->tcpLargeRecvBufFreeList ) { + freeListCleanup ( this->tcpLargeRecvBufFreeList ); + } + this->timerQueue.release (); + throw; + } + + /* + * load user configured tcp name server address list, + * create virtual circuits, and add them to server table + */ + ELLLIST dest, tmpList; + + ellInit ( & dest ); + ellInit ( & tmpList ); + + addAddrToChannelAccessAddressList ( &tmpList, &EPICS_CA_NAME_SERVERS, this->_serverPort, false ); + removeDuplicateAddresses ( &dest, &tmpList, 0 ); + + epicsGuard < epicsMutex > guard ( this->mutex ); + + while ( osiSockAddrNode * + pNode = reinterpret_cast < osiSockAddrNode * > ( ellGet ( & dest ) ) ) { + tcpiiu * piiu = NULL; + SearchDestTCP * pdst = new SearchDestTCP ( *this, pNode->addr ); + this->registerSearchDest ( guard, * pdst ); + bool newIIU = findOrCreateVirtCircuit ( + guard, pNode->addr, cacChannel::priorityDefault, + piiu, CA_UKN_MINOR_VERSION, pdst ); + free ( pNode ); + if ( newIIU ) { + piiu->start ( guard ); + } + } +} + +cac::~cac () +{ + // this blocks until the UDP thread exits so that + // it will not sneak in any new clients + // + // lock intentionally not held here so that we dont deadlock + // waiting for the UDP thread to exit while it is waiting to + // get the lock. + { + epicsGuard < epicsMutex > cbGuard ( this->cbMutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pudpiiu ) { + this->pudpiiu->shutdown ( cbGuard, guard ); + + // make sure no new tcp circuits are created + this->cacShutdownInProgress = true; + + // + // shutdown all tcp circuits + // + tsDLIter < tcpiiu > iter = this->circuitList.firstIter (); + while ( iter.valid() ) { + // this causes a clean shutdown to occur + iter->unlinkAllChannels ( cbGuard, guard ); + iter++; + } + } + } + + // + // wait for all tcp threads to exit + // + // this will block for oustanding sends to go out so dont + // hold a lock while waiting + // + { + epicsGuard < epicsMutex > guard ( this->mutex ); + while ( this->iiuExistenceCount > 0 ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->iiuUninstall.wait (); + } + } + + if ( this->pudpiiu ) { + delete this->pudpiiu; + } + + freeListCleanup ( this->tcpSmallRecvBufFreeList ); + freeListCleanup ( this->tcpLargeRecvBufFreeList ); + + delete [] this->pUserName; + + tsSLList < bhe > tmpBeaconList; + this->beaconTable.removeAll ( tmpBeaconList ); + while ( bhe * pBHE = tmpBeaconList.get() ) { + pBHE->~bhe (); + this->bheFreeList.release ( pBHE ); + } + + this->timerQueue.release (); + + this->ipToAEngine.release (); + + errlogFlush (); + + osiSockRelease (); + + // its ok for channels and subscriptions to still + // exist at this point. The user created them and + // its his responsibility to clean them up. +} + +unsigned cac::lowestPriorityLevelAbove ( unsigned priority ) +{ + unsigned abovePriority; + epicsThreadBooleanStatus tbs; + tbs = epicsThreadLowestPriorityLevelAbove ( + priority, & abovePriority ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + abovePriority = priority; + } + return abovePriority; +} + +unsigned cac::highestPriorityLevelBelow ( unsigned priority ) +{ + unsigned belowPriority; + epicsThreadBooleanStatus tbs; + tbs = epicsThreadHighestPriorityLevelBelow ( + priority, & belowPriority ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + belowPriority = priority; + } + return belowPriority; +} + +// +// set the push pending flag on all virtual circuits +// +void cac::flush ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + tsDLIter < tcpiiu > iter = this->circuitList.firstIter (); + while ( iter.valid () ) { + iter->flushRequest ( guard ); + iter++; + } +} + +unsigned cac::circuitCount ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->circuitList.count (); +} + +void cac::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + + ::printf ( "Channel Access Client Context at %p for user %s\n", + static_cast ( this ), this->pUserName ); + // this also supresses the "defined, but not used" + // warning message + ::printf ( "\trevision \"%s\"\n", pVersionCAC ); + + if ( level > 0u ) { + this->serverTable.show ( level - 1u ); + ::printf ( "\tconnection time out watchdog period %f\n", this->connTMO ); + } + + if ( level > 1u ) { + if ( this->pudpiiu ) { + this->pudpiiu->show ( level - 2u ); + } + } + + if ( level > 2u ) { + ::printf ( "Program begin time:\n"); + this->programBeginTime.show ( level - 3u ); + ::printf ( "Channel identifier hash table:\n" ); + this->chanTable.show ( level - 3u ); + ::printf ( "IO identifier hash table:\n" ); + this->ioTable.show ( level - 3u ); + ::printf ( "Beacon source identifier hash table:\n" ); + this->beaconTable.show ( level - 3u ); + ::printf ( "Timer queue:\n" ); + this->timerQueue.show ( level - 3u ); + ::printf ( "IP address to name conversion engine:\n" ); + this->ipToAEngine.show ( level - 3u ); + } + + if ( level > 3u ) { + ::printf ( "Default mutex:\n"); + this->mutex.show ( level - 4u ); + ::printf ( "mutex:\n" ); + this->mutex.show ( level - 4u ); + } +} + +/* + * cac::beaconNotify + */ +void cac::beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, + ca_uint32_t beaconNumber, unsigned protocolRevision ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + if ( ! this->pudpiiu ) { + return; + } + + /* + * look for it in the hash table + */ + bhe *pBHE = this->beaconTable.lookup ( addr ); + if ( pBHE ) { + /* + * return if the beacon period has not changed significantly + */ + if ( ! pBHE->updatePeriod ( guard, this->programBeginTime, + currentTime, beaconNumber, protocolRevision ) ) { + return; + } + } + else { + /* + * This is the first beacon seen from this server. + * Wait until 2nd beacon is seen before deciding + * if it is a new server (or just the first + * time that we have seen a server's beacon + * shortly after the program started up) + */ + pBHE = new ( this->bheFreeList ) + bhe ( this->mutex, currentTime, beaconNumber, addr ); + if ( pBHE ) { + if ( this->beaconTable.add ( *pBHE ) < 0 ) { + pBHE->~bhe (); + this->bheFreeList.release ( pBHE ); + } + } + return; + } + + this->beaconAnomalyCount++; + + this->pudpiiu->beaconAnomalyNotify ( guard ); + +# ifdef DEBUG + { + char buf[128]; + addr.name ( buf, sizeof ( buf ) ); + ::printf ( "New server available: %s\n", buf ); + } +# endif +} + +cacChannel & cac::createChannel ( + epicsGuard < epicsMutex > & guard, const char * pName, + cacChannelNotify & chan, cacChannel::priLev pri ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( pri > cacChannel::priorityMax ) { + throw cacChannel::badPriority (); + } + + if ( pName == 0 || pName[0] == '\0' ) { + throw cacChannel::badString (); + } + + if ( ! this->pudpiiu ) { + this->pudpiiu = new udpiiu ( + guard, this->timerQueue, this->cbMutex, + this->mutex, this->notify, *this, this->_serverPort, + this->searchDestList ); + } + + nciu * pNetChan = new ( this->channelFreeList ) + nciu ( *this, noopIIU, chan, pName, pri ); + this->chanTable.idAssignAdd ( *pNetChan ); + return *pNetChan; +} + +bool cac::findOrCreateVirtCircuit ( + epicsGuard < epicsMutex > & guard, const osiSockAddr & addr, + unsigned priority, tcpiiu *& piiu, unsigned minorVersionNumber, + SearchDestTCP * pSearchDest ) +{ + guard.assertIdenticalMutex ( this->mutex ); + bool newIIU = false; + + if ( piiu ) { + if ( ! piiu->alive ( guard ) ) { + return newIIU; + } + } + else { + try { + autoPtrFreeList < tcpiiu, 32, epicsMutexNOOP > pnewiiu ( + this->freeListVirtualCircuit, + new ( this->freeListVirtualCircuit ) tcpiiu ( + *this, this->mutex, this->cbMutex, this->notify, this->connTMO, + this->timerQueue, addr, this->comBufMemMgr, minorVersionNumber, + this->ipToAEngine, priority, pSearchDest ) ); + + bhe * pBHE = this->beaconTable.lookup ( addr.ia ); + if ( ! pBHE ) { + pBHE = new ( this->bheFreeList ) + bhe ( this->mutex, epicsTime (), 0u, addr.ia ); + if ( this->beaconTable.add ( *pBHE ) < 0 ) { + return newIIU; + } + } + this->serverTable.add ( *pnewiiu ); + this->circuitList.add ( *pnewiiu ); + this->iiuExistenceCount++; + pBHE->registerIIU ( guard, *pnewiiu ); + piiu = pnewiiu.release (); + newIIU = true; + } + catch ( std :: exception & except ) { + errlogPrintf ( + "CAC: exception during virtual circuit creation \"%s\"\n", + except.what () ); + return newIIU; + } + catch ( ... ) { + errlogPrintf ( + "CAC: Nonstandard exception during virtual circuit creation\n" ); + return newIIU; + } + } + return newIIU; +} + +void cac::transferChanToVirtCircuit ( + unsigned cid, unsigned sid, // X aCC 431 + ca_uint16_t typeCode, arrayElementCount count, + unsigned minorVersionNumber, const osiSockAddr & addr, + const epicsTime & currentTime ) +{ + if ( addr.sa.sa_family != AF_INET ) { + return; + } + + epicsGuard < epicsMutex > guard ( this->mutex ); + + /* + * Do not open new circuits while the cac is shutting down + */ + if ( this->cacShutdownInProgress ) { + return; + } + + /* + * ignore search replies for deleted channels + */ + nciu * pChan = this->chanTable.lookup ( cid ); + if ( ! pChan ) { + return; + } + + /* + * Ignore duplicate search replies + */ + osiSockAddr chanAddr = pChan->getPIIU(guard)->getNetworkAddress (guard); + + if ( chanAddr.sa.sa_family != AF_UNSPEC ) { + if ( ! sockAddrAreIdentical ( &addr, &chanAddr ) ) { + char acc[64]; + pChan->getPIIU(guard)->getHostName ( guard, acc, sizeof ( acc ) ); + msgForMultiplyDefinedPV * pMsg = new ( this->mdpvFreeList ) + msgForMultiplyDefinedPV ( this->ipToAEngine, + *this, pChan->pName ( guard ), acc ); + // It is possible for the ioInitiate call below to + // call the callback directly if queue quota is exceeded. + // This callback takes the callback lock and therefore we + // must release the primary mutex here to avoid a lock + // hierarchy inversion. + epicsGuardRelease < epicsMutex > unguard ( guard ); + pMsg->ioInitiate ( addr ); + } + return; + } + + caServerID servID ( addr.ia, pChan->getPriority(guard) ); + tcpiiu * piiu = this->serverTable.lookup ( servID ); + + bool newIIU = findOrCreateVirtCircuit ( + guard, addr, + pChan->getPriority(guard), piiu, minorVersionNumber ); + + // must occur before moving to new iiu + pChan->getPIIU(guard)->uninstallChanDueToSuccessfulSearchResponse ( + guard, *pChan, currentTime ); + piiu->installChannel ( + guard, *pChan, sid, typeCode, count ); + + if ( newIIU ) { + piiu->start ( guard ); + } +} + +void cac::destroyChannel ( + epicsGuard < epicsMutex > & guard, + nciu & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + // uninstall channel so that recv threads + // will not start a new callback for this channel's IO. + if ( this->chanTable.remove ( chan ) != & chan ) { + throw std::logic_error ( "Invalid channel identifier" ); + } + chan.~nciu (); + this->channelFreeList.release ( & chan ); +} + +void cac::disconnectAllIO ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, + nciu & chan, tsDLList < baseNMIU > & ioList ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + char buf[128]; + chan.getHostName ( guard, buf, sizeof ( buf ) ); + + tsDLIter < baseNMIU > pNetIO = ioList.firstIter(); + while ( pNetIO.valid () ) { + tsDLIter < baseNMIU > pNext = pNetIO; + pNext++; + if ( ! pNetIO->isSubscription() ) { + this->ioTable.remove ( pNetIO->getId () ); + } + pNetIO->exception ( guard, *this, ECA_DISCONN, buf ); + pNetIO = pNext; + } +} + +int cac :: printFormated ( + epicsGuard < epicsMutex > & callbackControl, + const char * pformat, ... ) const +{ + va_list theArgs; + va_start ( theArgs, pformat ); + int status = this->varArgsPrintFormated ( callbackControl, pformat, theArgs ); + va_end ( theArgs ); + return status; +} + +netWriteNotifyIO & cac::writeNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, privateInterfaceForIO & icni, + unsigned type, arrayElementCount nElem, const void * pValue, cacWriteNotify & notifyIn ) +{ + guard.assertIdenticalMutex ( this->mutex ); + autoPtrRecycle < netWriteNotifyIO > pIO ( + guard, this->ioTable, *this, + netWriteNotifyIO::factory ( this->freeListWriteNotifyIO, icni, notifyIn ) ); + this->ioTable.idAssignAdd ( *pIO ); + chan.getPIIU(guard)->writeNotifyRequest ( + guard, chan, *pIO, type, nElem, pValue ); + return *pIO.release(); +} + +netReadNotifyIO & cac::readNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, privateInterfaceForIO & icni, + unsigned type, arrayElementCount nElem, cacReadNotify & notifyIn ) +{ + guard.assertIdenticalMutex ( this->mutex ); + autoPtrRecycle < netReadNotifyIO > pIO ( + guard, this->ioTable, *this, + netReadNotifyIO::factory ( this->freeListReadNotifyIO, icni, notifyIn ) ); + this->ioTable.idAssignAdd ( *pIO ); + chan.getPIIU(guard)->readNotifyRequest ( guard, chan, *pIO, type, nElem ); + return *pIO.release(); +} + +bool cac::destroyIO ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & idIn, nciu & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + baseNMIU * pIO = this->ioTable.remove ( idIn ); + if ( pIO ) { + class netSubscription * pSubscr = pIO->isSubscription (); + if ( pSubscr ) { + pSubscr->unsubscribeIfRequired ( guard, chan ); + } + + // this uninstalls from the list and destroys the IO + pIO->exception ( guard, *this, + ECA_CHANDESTROY, chan.pName ( guard ) ); + return true; + } + return false; +} + +void cac::ioShow ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & idIn, unsigned level ) const +{ + baseNMIU * pmiu = this->ioTable.lookup ( idIn ); + if ( pmiu ) { + pmiu->show ( guard, level ); + } +} + +void cac::ioExceptionNotify ( + unsigned idIn, int status, const char * pContext, + unsigned type, arrayElementCount count ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.lookup ( idIn ); + if ( pmiu ) { + pmiu->exception ( guard, *this, status, pContext, type, count ); + } +} + +void cac::ioExceptionNotifyAndUninstall ( + unsigned idIn, int status, const char * pContext, + unsigned type, arrayElementCount count ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.remove ( idIn ); + if ( pmiu ) { + pmiu->exception ( guard, *this, status, pContext, type, count ); + } +} + +void cac::recycleReadNotifyIO ( + epicsGuard < epicsMutex > & guard, netReadNotifyIO & io ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->freeListReadNotifyIO.release ( & io ); +} + +void cac::recycleWriteNotifyIO ( + epicsGuard < epicsMutex > & guard, netWriteNotifyIO & io ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->freeListWriteNotifyIO.release ( & io ); +} + +void cac::recycleSubscription ( + epicsGuard < epicsMutex > & guard, netSubscription & io ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->freeListSubscription.release ( & io ); +} + +netSubscription & cac::subscriptionRequest ( + epicsGuard < epicsMutex > & guard, + nciu & chan, privateInterfaceForIO & privChan, + unsigned type, // X aCC 361 + arrayElementCount nElem, unsigned mask, + cacStateNotify & notifyIn, + bool chanIsInstalled ) +{ + guard.assertIdenticalMutex ( this->mutex ); + autoPtrRecycle < netSubscription > pIO ( + guard, this->ioTable, *this, + netSubscription::factory ( this->freeListSubscription, + privChan, type, nElem, mask, notifyIn ) ); + this->ioTable.idAssignAdd ( *pIO ); + if ( chanIsInstalled ) { + pIO->subscribeIfRequired ( guard, chan ); + } + return *pIO.release (); +} + +bool cac::versionAction ( callbackManager &, tcpiiu & iiu, + const epicsTime &, const caHdrLargeArray & msg, void * ) +{ + iiu.versionRespNotify ( msg ); + return true; +} + +bool cac::echoRespAction ( + callbackManager & mgr, tcpiiu & iiu, + const epicsTime & /* current */, const caHdrLargeArray &, void * ) +{ + iiu.probeResponseNotify ( mgr.cbGuard ); + return true; +} + +bool cac::writeNotifyRespAction ( + callbackManager &, tcpiiu &, + const epicsTime &, const caHdrLargeArray & hdr, void * ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); + if ( pmiu ) { + if ( hdr.m_cid == ECA_NORMAL ) { + pmiu->completion ( guard, *this ); + } + else { + pmiu->exception ( guard, *this, + hdr.m_cid, "write notify request rejected" ); + } + } + return true; +} + +bool cac::readNotifyRespAction ( callbackManager &, tcpiiu & iiu, + const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + /* + * the channel id field is abused for + * read notify status starting with CA V4.1 + */ + int caStatus; + if ( iiu.ca_v41_ok ( guard ) ) { + caStatus = hdr.m_cid; + } + else { + caStatus = ECA_NORMAL; + } + + baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); + // + // The IO destroy routines take the call back mutex + // when uninstalling and deleting the baseNMIU so there is + // no need to worry here about the baseNMIU being deleted while + // it is in use here. + // + if ( pmiu ) { + // if its a circuit-becomes-responsive subscription update + // then we need to reinstall the IO into the table + netSubscription * pSubscr = pmiu->isSubscription (); + if ( pSubscr ) { + // this does *not* assign a new resource id + this->ioTable.add ( *pmiu ); + } + if ( caStatus == ECA_NORMAL ) { + /* + * convert the data buffer from net + * format to host format + */ + caStatus = caNetConvert ( + hdr.m_dataType, pMsgBdy, pMsgBdy, false, hdr.m_count ); + } + if ( caStatus == ECA_NORMAL ) { + pmiu->completion ( guard, *this, + hdr.m_dataType, hdr.m_count, pMsgBdy ); + } + else { + pmiu->exception ( guard, *this, + caStatus, "read failed", + hdr.m_dataType, hdr.m_count ); + } + } + return true; +} + +bool cac::searchRespAction ( callbackManager &, tcpiiu & iiu, + const epicsTime & currentTime, const caHdrLargeArray & msg, + void * /* pMsgBdy */ ) +{ + assert ( this->pudpiiu ); + iiu.searchRespNotify ( currentTime, msg ); + return true; +} + +bool cac::eventRespAction ( callbackManager &, tcpiiu &iiu, + const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) +{ + int caStatus; + + /* + * m_postsize = 0 used to be a subscription cancel confirmation, + * but is now a noop because the IO block is immediately deleted + */ + if ( ! hdr.m_postsize ) { + return true; + } + + epicsGuard < epicsMutex > guard ( this->mutex ); + + /* + * the channel id field is abused for + * read notify status starting with CA V4.1 + */ + if ( iiu.ca_v41_ok ( guard ) ) { + caStatus = hdr.m_cid; + } + else { + caStatus = ECA_NORMAL; + } + + // + // The IO destroy routines take the call back mutex + // when uninstalling and deleting the baseNMIU so there is + // no need to worry here about the baseNMIU being deleted while + // it is in use here. + // + baseNMIU * pmiu = this->ioTable.lookup ( hdr.m_available ); + if ( pmiu ) { + /* + * convert the data buffer from net format to host format + */ + if ( caStatus == ECA_NORMAL ) { + caStatus = caNetConvert ( + hdr.m_dataType, pMsgBdy, pMsgBdy, false, hdr.m_count ); + } + if ( caStatus == ECA_NORMAL ) { + pmiu->completion ( guard, *this, + hdr.m_dataType, hdr.m_count, pMsgBdy ); + } + else { + pmiu->exception ( guard, *this, caStatus, + "subscription update read failed", + hdr.m_dataType, hdr.m_count ); + } + } + return true; +} + +bool cac::readRespAction ( callbackManager &, tcpiiu &, + const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + baseNMIU * pmiu = this->ioTable.remove ( hdr.m_available ); + // + // The IO destroy routines take the call back mutex + // when uninstalling and deleting the baseNMIU so there is + // no need to worry here about the baseNMIU being deleted while + // it is in use here. + // + if ( pmiu ) { + pmiu->completion ( guard, *this, + hdr.m_dataType, hdr.m_count, pMsgBdy ); + } + return true; +} + +bool cac::clearChannelRespAction ( callbackManager &, tcpiiu &, + const epicsTime &, const caHdrLargeArray &, void * /* pMsgBody */ ) +{ + return true; // currently a noop +} + +bool cac::defaultExcep ( + callbackManager &, tcpiiu & iiu, + const caHdrLargeArray &, const char * pCtx, unsigned status ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + char buf[512]; + char hostName[64]; + iiu.getHostName ( guard, hostName, sizeof ( hostName ) ); + sprintf ( buf, "host=%s ctx=%.400s", hostName, pCtx ); + this->notify.exception ( guard, status, buf, 0, 0u ); + return true; +} + +void cac::exception ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, int status, + const char * pContext, const char * pFileName, unsigned lineNo ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + this->notify.exception ( guard, status, pContext, + pFileName, lineNo ); +} + +bool cac::eventAddExcep ( + callbackManager &, tcpiiu &, + const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + this->ioExceptionNotify ( hdr.m_available, status, pCtx, + hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::readExcep ( callbackManager &, tcpiiu &, + const caHdrLargeArray & hdr, + const char * pCtx, unsigned status ) +{ + this->ioExceptionNotifyAndUninstall ( hdr.m_available, + status, pCtx, hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::writeExcep ( + callbackManager & mgr, // X aCC 431 + tcpiiu &, const caHdrLargeArray & hdr, + const char * pCtx, unsigned status ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + nciu * pChan = this->chanTable.lookup ( hdr.m_available ); + if ( pChan ) { + pChan->writeException ( mgr.cbGuard, guard, status, pCtx, + hdr.m_dataType, hdr.m_count ); + } + return true; +} + +bool cac::readNotifyExcep ( callbackManager &, tcpiiu &, + const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + this->ioExceptionNotifyAndUninstall ( hdr.m_available, + status, pCtx, hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::writeNotifyExcep ( callbackManager &, tcpiiu &, + const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ) +{ + this->ioExceptionNotifyAndUninstall ( hdr.m_available, + status, pCtx, hdr.m_dataType, hdr.m_count ); + return true; +} + +bool cac::exceptionRespAction ( callbackManager & cbMutexIn, tcpiiu & iiu, + const epicsTime &, const caHdrLargeArray & hdr, void * pMsgBdy ) +{ + const caHdr * pReq = reinterpret_cast < const caHdr * > ( pMsgBdy ); + unsigned bytesSoFar = sizeof ( *pReq ); + if ( hdr.m_postsize < bytesSoFar ) { + return false; + } + caHdrLargeArray req; + req.m_cmmd = AlignedWireRef < const epicsUInt16 > ( pReq->m_cmmd ); + req.m_postsize = AlignedWireRef < const epicsUInt16 > ( pReq->m_postsize ); + req.m_dataType = AlignedWireRef < const epicsUInt16 > ( pReq->m_dataType ); + req.m_count = AlignedWireRef < const epicsUInt16 > ( pReq->m_count ); + req.m_cid = AlignedWireRef < const epicsUInt32 > ( pReq->m_cid ); + req.m_available = AlignedWireRef < const epicsUInt32 > ( pReq->m_available ); + const ca_uint32_t * pLW = reinterpret_cast < const ca_uint32_t * > ( pReq + 1 ); + if ( req.m_postsize == 0xffff ) { + static const unsigned annexSize = + sizeof ( req.m_postsize ) + sizeof ( req.m_count ); + bytesSoFar += annexSize; + if ( hdr.m_postsize < bytesSoFar ) { + return false; + } + req.m_postsize = AlignedWireRef < const epicsUInt32 > ( pLW[0] ); + req.m_count = AlignedWireRef < const epicsUInt32 > ( pLW[1] ); + pLW += 2u; + } + + // execute the exception message + pExcepProtoStubTCP pStub; + if ( hdr.m_cmmd >= NELEMENTS ( cac::tcpExcepJumpTableCAC ) ) { + pStub = &cac::defaultExcep; + } + else { + pStub = cac::tcpExcepJumpTableCAC [req.m_cmmd]; + } + const char *pCtx = reinterpret_cast < const char * > ( pLW ); + return ( this->*pStub ) ( cbMutexIn, iiu, req, pCtx, hdr.m_available ); +} + +bool cac::accessRightsRespAction ( + callbackManager & mgr, tcpiiu &, // X aCC 431 + const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); + if ( pChan ) { + unsigned ar = hdr.m_available; + caAccessRights accessRights ( + ( ar & CA_PROTO_ACCESS_RIGHT_READ ) ? true : false, + ( ar & CA_PROTO_ACCESS_RIGHT_WRITE ) ? true : false); + pChan->accessRightsStateChange ( accessRights, mgr.cbGuard, guard ); + } + + return true; +} + +bool cac::createChannelRespAction ( + callbackManager & mgr, tcpiiu & iiu, // X aCC 431 + const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); + if ( pChan ) { + unsigned sidTmp; + if ( iiu.ca_v44_ok ( guard ) ) { + sidTmp = hdr.m_available; + } + else { + sidTmp = pChan->getSID (guard); + } + bool wasExpected = iiu.connectNotify ( guard, *pChan ); + if ( wasExpected ) { + pChan->connect ( hdr.m_dataType, hdr.m_count, sidTmp, + mgr.cbGuard, guard ); + } + else { + errlogPrintf ( + "CA Client Library: Ignored duplicate create channel " + "response from CA server?\n" ); + } + } + else if ( iiu.ca_v44_ok ( guard ) ) { + // this indicates a claim response for a resource that does + // not exist in the client - so just remove it from the server + iiu.clearChannelRequest ( guard, hdr.m_available, hdr.m_cid ); + } + + return true; +} + +bool cac::verifyAndDisconnectChan ( + callbackManager & mgr, tcpiiu &, + const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + nciu * pChan = this->chanTable.lookup ( hdr.m_cid ); + if ( ! pChan ) { + return true; + } + this->disconnectChannel ( mgr.cbGuard, guard, *pChan ); + return true; +} + +void cac::disconnectChannel ( + epicsGuard < epicsMutex > & cbGuard, // X aCC 431 + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + assert ( this->pudpiiu ); + chan.disconnectAllIO ( cbGuard, guard ); + chan.getPIIU(guard)->uninstallChan ( guard, chan ); + this->pudpiiu->installDisconnectedChannel ( guard, chan ); + chan.unresponsiveCircuitNotify ( cbGuard, guard ); +} + +bool cac::badTCPRespAction ( callbackManager &, tcpiiu & iiu, + const epicsTime &, const caHdrLargeArray & hdr, void * /* pMsgBody */ ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + char hostName[64]; + iiu.getHostName ( guard, hostName, sizeof ( hostName ) ); + errlogPrintf ( "CAC: Undecipherable TCP message ( bad response type %u ) from %s\n", + hdr.m_cmmd, hostName ); + return false; +} + +bool cac::executeResponse ( callbackManager & mgr, tcpiiu & iiu, + const epicsTime & currentTime, caHdrLargeArray & hdr, char * pMshBody ) +{ + // execute the response message + pProtoStubTCP pStub; + if ( hdr.m_cmmd >= NELEMENTS ( cac::tcpJumpTableCAC ) ) { + pStub = &cac::badTCPRespAction; + } + else { + pStub = cac::tcpJumpTableCAC [hdr.m_cmmd]; + } + return ( this->*pStub ) ( mgr, iiu, currentTime, hdr, pMshBody ); +} + +void cac::selfTest ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + this->chanTable.verify (); + this->ioTable.verify (); + this->beaconTable.verify (); +} + +void cac::destroyIIU ( tcpiiu & iiu ) +{ + { + callbackManager mgr ( this->notify, this->cbMutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); + + if ( iiu.channelCount ( guard ) ) { + char hostNameTmp[64]; + iiu.getHostName ( guard, hostNameTmp, sizeof ( hostNameTmp ) ); + genLocalExcep ( mgr.cbGuard, guard, *this, ECA_DISCONN, hostNameTmp ); + } + osiSockAddr addr = iiu.getNetworkAddress ( guard ); + if ( addr.sa.sa_family == AF_INET ) { + inetAddrID tmp ( addr.ia ); + bhe * pBHE = this->beaconTable.lookup ( tmp ); + if ( pBHE ) { + pBHE->unregisterIIU ( guard, iiu ); + } + } + + assert ( this->pudpiiu ); + iiu.disconnectAllChannels ( mgr.cbGuard, guard, *this->pudpiiu ); + + this->serverTable.remove ( iiu ); + this->circuitList.remove ( iiu ); + } + + // this destroys a timer that takes the primary mutex + // so we must not hold the primary mutex here + // + // this waits for send/recv threads to exit + // this also uses the cac free lists so cac must wait + // for this to finish before it shuts down + + iiu.~tcpiiu (); + + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->freeListVirtualCircuit.release ( & iiu ); + this->iiuExistenceCount--; + // signal iiu uninstall event so that cac can properly shut down + this->iiuUninstall.signal(); + } + // do not touch "this" after lock is released above +} + +double cac::beaconPeriod ( + epicsGuard < epicsMutex > & guard, + const nciu & chan ) const +{ + const netiiu * pIIU = chan.getConstPIIU ( guard ); + if ( pIIU ) { + osiSockAddr addr = pIIU->getNetworkAddress ( guard ); + if ( addr.sa.sa_family == AF_INET ) { + inetAddrID tmp ( addr.ia ); + bhe *pBHE = this->beaconTable.lookup ( tmp ); + if ( pBHE ) { + return pBHE->period ( guard ); + } + } + } + return - DBL_MAX; +} + +void cac::initiateConnect ( + epicsGuard < epicsMutex > & guard, + nciu & chan, netiiu * & piiu ) +{ + guard.assertIdenticalMutex ( this->mutex ); + assert ( this->pudpiiu ); + this->pudpiiu->installNewChannel ( guard, chan, piiu ); +} + +void *cacComBufMemoryManager::allocate ( size_t size ) +{ + return this->freeList.allocate ( size ); +} + +void cacComBufMemoryManager::release ( void * pCadaver ) +{ + this->freeList.release ( pCadaver ); +} + +void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv, + const char * pChannelName, const char * pAcc, const char * pRej ) +{ + char buf[256]; + sprintf ( buf, "Channel: \"%.64s\", Connecting to: %.64s, Ignored: %.64s", + pChannelName, pAcc, pRej ); + { + callbackManager mgr ( this->notify, this->cbMutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); + this->exception ( mgr.cbGuard, guard, ECA_DBLCHNL, buf, __FILE__, __LINE__ ); + } + mfmdpv.~msgForMultiplyDefinedPV (); + this->mdpvFreeList.release ( & mfmdpv ); +} + +void cac::registerSearchDest ( + epicsGuard < epicsMutex > & guard, + SearchDest & req ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->searchDestList.add ( req ); +} diff --git a/src/ca/cac.h b/src/ca/cac.h new file mode 100644 index 000000000..4bac0cdb1 --- /dev/null +++ b/src/ca/cac.h @@ -0,0 +1,470 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author: Jeff Hill + * + */ + +#ifndef cach +#define cach + +#ifdef epicsExportSharedSymbols +# define cach_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "compilerDependencies.h" +#include "ipAddrToAsciiAsynchronous.h" +#include "msgForMultiplyDefinedPV.h" +#include "epicsTimer.h" +#include "epicsEvent.h" +#include "freeList.h" +#include "localHostName.h" + +#ifdef cach_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "nciu.h" +#include "comBuf.h" +#include "bhe.h" +#include "cacIO.h" +#include "netIO.h" +#include "localHostName.h" +#include "virtualCircuit.h" + +class netWriteNotifyIO; +class netReadNotifyIO; +class netSubscription; + +// used to control access to cac's recycle routines which +// should only be indirectly invoked by CAC when its lock +// is applied +class cacRecycle { // X aCC 655 +public: + virtual void recycleReadNotifyIO ( + epicsGuard < epicsMutex > &, netReadNotifyIO &io ) = 0; + virtual void recycleWriteNotifyIO ( + epicsGuard < epicsMutex > &, netWriteNotifyIO &io ) = 0; + virtual void recycleSubscription ( + epicsGuard < epicsMutex > &, netSubscription &io ) = 0; +protected: + virtual ~cacRecycle() {} +}; + +struct CASG; +class inetAddrID; +class caServerID; +struct caHdrLargeArray; + +class cacComBufMemoryManager : public comBufMemoryManager +{ +public: + cacComBufMemoryManager () {} + void * allocate ( size_t ); + void release ( void * ); +private: + tsFreeList < comBuf, 0x20 > freeList; + cacComBufMemoryManager ( const cacComBufMemoryManager & ); + cacComBufMemoryManager & operator = ( const cacComBufMemoryManager & ); +}; + +class notifyGuard { +public: + notifyGuard ( cacContextNotify & ); + ~notifyGuard (); +private: + cacContextNotify & notify; + notifyGuard ( const notifyGuard & ); + notifyGuard & operator = ( const notifyGuard & ); +}; + +class callbackManager : public notifyGuard { +public: + callbackManager ( + cacContextNotify &, + epicsMutex & callbackControl ); + epicsGuard < epicsMutex > cbGuard; +}; + +class cac : + public cacContext, + private cacRecycle, + private callbackForMultiplyDefinedPV +{ +public: + cac ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, + cacContextNotify & ); + virtual ~cac (); + + // beacon management + void beaconNotify ( const inetAddrID & addr, const epicsTime & currentTime, + ca_uint32_t beaconNumber, unsigned protocolRevision ); + unsigned beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & ) const; + + // IO management + void flush ( epicsGuard < epicsMutex > & guard ); + bool executeResponse ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, caHdrLargeArray &, char *pMsgBody ); + + // channel routines + void transferChanToVirtCircuit ( + unsigned cid, unsigned sid, + ca_uint16_t typeCode, arrayElementCount count, + unsigned minorVersionNumber, const osiSockAddr &, + const epicsTime & currentTime ); + cacChannel & createChannel ( + epicsGuard < epicsMutex > & guard, const char * pChannelName, + cacChannelNotify &, cacChannel::priLev ); + void destroyChannel ( + epicsGuard < epicsMutex > &, nciu & ); + void initiateConnect ( + epicsGuard < epicsMutex > &, nciu &, netiiu * & ); + nciu * lookupChannel ( + epicsGuard < epicsMutex > &, const cacChannel::ioid & ); + + // IO requests + netWriteNotifyIO & writeNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, + unsigned type, arrayElementCount nElem, const void * pValue, + cacWriteNotify & ); + netReadNotifyIO & readNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, + unsigned type, arrayElementCount nElem, + cacReadNotify & ); + netSubscription & subscriptionRequest ( + epicsGuard < epicsMutex > &, nciu &, privateInterfaceForIO &, + unsigned type, arrayElementCount nElem, unsigned mask, + cacStateNotify &, bool channelIsInstalled ); + bool destroyIO ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & idIn, + nciu & chan ); + void disconnectAllIO ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, + nciu &, tsDLList < baseNMIU > & ioList ); + + void ioShow ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid &id, unsigned level ) const; + + // sync group routines + CASG * lookupCASG ( epicsGuard < epicsMutex > &, unsigned id ); + void installCASG ( epicsGuard < epicsMutex > &, CASG & ); + void uninstallCASG ( epicsGuard < epicsMutex > &, CASG & ); + + // exception generation + void exception ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, + int status, const char * pContext, + const char * pFileName, unsigned lineNo ); + + // search destination management + void registerSearchDest ( + epicsGuard < epicsMutex > &, SearchDest & req ); + bool findOrCreateVirtCircuit ( + epicsGuard < epicsMutex > &, const osiSockAddr &, + unsigned, tcpiiu *&, unsigned, SearchDestTCP * pSearchDest = NULL ); + + // diagnostics + unsigned circuitCount ( epicsGuard < epicsMutex > & ) const; + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; + int printFormated ( + epicsGuard < epicsMutex > & callbackControl, + const char *pformat, ... ) const; + int varArgsPrintFormated ( + epicsGuard < epicsMutex > & callbackControl, + const char *pformat, va_list args ) const; + double connectionTimeout ( epicsGuard < epicsMutex > & ); + + // buffer management + char * allocateSmallBufferTCP (); + void releaseSmallBufferTCP ( char * ); + unsigned largeBufferSizeTCP () const; + char * allocateLargeBufferTCP (); + void releaseLargeBufferTCP ( char * ); + unsigned maxContiguousFrames ( epicsGuard < epicsMutex > & ) const; + + // misc + const char * userNamePointer () const; + unsigned getInitializingThreadsPriority () const; + epicsMutex & mutexRef (); + void attachToClientCtx (); + void selfTest ( + epicsGuard < epicsMutex > & ) const; + double beaconPeriod ( + epicsGuard < epicsMutex > &, + const nciu & chan ) const; + static unsigned lowestPriorityLevelAbove ( unsigned priority ); + static unsigned highestPriorityLevelBelow ( unsigned priority ); + void destroyIIU ( tcpiiu & iiu ); + + const char * pLocalHostName (); + +private: + epicsSingleton < localHostName > :: reference _refLocalHostName; + chronIntIdResTable < nciu > chanTable; + // + // !!!! There is at this point no good reason + // !!!! to maintain one IO table for all types of + // !!!! IO. It would probably be better to maintain + // !!!! an independent table for each IO type. The + // !!!! new adaptive sized hash table will not + // !!!! waste memory. Making this change will + // !!!! avoid virtual function overhead when + // !!!! accessing the different types of IO. This + // !!!! approach would also probably be safer in + // !!!! terms of detecting damaged protocol. + // + chronIntIdResTable < baseNMIU > ioTable; + resTable < bhe, inetAddrID > beaconTable; + resTable < tcpiiu, caServerID > serverTable; + tsDLList < tcpiiu > circuitList; + tsDLList < SearchDest > searchDestList; + tsFreeList + < class tcpiiu, 32, epicsMutexNOOP > + freeListVirtualCircuit; + tsFreeList + < class netReadNotifyIO, 1024, epicsMutexNOOP > + freeListReadNotifyIO; + tsFreeList + < class netWriteNotifyIO, 1024, epicsMutexNOOP > + freeListWriteNotifyIO; + tsFreeList + < class netSubscription, 1024, epicsMutexNOOP > + freeListSubscription; + tsFreeList + < class nciu, 1024, epicsMutexNOOP > + channelFreeList; + tsFreeList + < class msgForMultiplyDefinedPV, 16 > + mdpvFreeList; + cacComBufMemoryManager comBufMemMgr; + bheFreeStore bheFreeList; + epicsTime programBeginTime; + double connTMO; + // **** lock hierarchy **** + // 1) callback lock must always be acquired before + // the primary mutex if both locks are needed + mutable epicsMutex & mutex; + mutable epicsMutex & cbMutex; + epicsEvent iiuUninstall; + ipAddrToAsciiEngine & ipToAEngine; + epicsTimerQueueActive & timerQueue; + char * pUserName; + class udpiiu * pudpiiu; + void * tcpSmallRecvBufFreeList; + void * tcpLargeRecvBufFreeList; + cacContextNotify & notify; + epicsThreadId initializingThreadsId; + unsigned initializingThreadsPriority; + unsigned maxRecvBytesTCP; + unsigned maxContigFrames; + unsigned beaconAnomalyCount; + unsigned short _serverPort; + unsigned iiuExistenceCount; + bool cacShutdownInProgress; + + void recycleReadNotifyIO ( + epicsGuard < epicsMutex > &, netReadNotifyIO &io ); + void recycleWriteNotifyIO ( + epicsGuard < epicsMutex > &, netWriteNotifyIO &io ); + void recycleSubscription ( + epicsGuard < epicsMutex > &, netSubscription &io ); + + void disconnectChannel ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, nciu & chan ); + + void ioExceptionNotify ( unsigned id, int status, + const char * pContext, unsigned type, arrayElementCount count ); + void ioExceptionNotifyAndUninstall ( unsigned id, int status, + const char * pContext, unsigned type, arrayElementCount count ); + + void pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv, + const char * pChannelName, const char * pAcc, const char * pRej ); + + // recv protocol stubs + bool versionAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool echoRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool writeNotifyRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool searchRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool readNotifyRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool eventRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool readRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool clearChannelRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool exceptionRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool accessRightsRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool createChannelRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool verifyAndDisconnectChan ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + bool badTCPRespAction ( callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + + typedef bool ( cac::*pProtoStubTCP ) ( + callbackManager &, tcpiiu &, + const epicsTime & currentTime, const caHdrLargeArray &, void *pMsgBdy ); + static const pProtoStubTCP tcpJumpTableCAC []; + + bool defaultExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool eventAddExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool readExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool writeExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool clearChanExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool readNotifyExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + bool writeNotifyExcep ( callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + typedef bool ( cac::*pExcepProtoStubTCP ) ( + callbackManager &, tcpiiu &iiu, const caHdrLargeArray &hdr, + const char *pCtx, unsigned status ); + static const pExcepProtoStubTCP tcpExcepJumpTableCAC []; + + cac ( const cac & ); + cac & operator = ( const cac & ); +}; + +inline const char * cac::userNamePointer () const +{ + return this->pUserName; +} + +inline unsigned cac::getInitializingThreadsPriority () const +{ + return this->initializingThreadsPriority; +} + +inline epicsMutex & cac::mutexRef () +{ + return this->mutex; +} + +inline int cac :: varArgsPrintFormated ( + epicsGuard < epicsMutex > & callbackControl, + const char *pformat, va_list args ) const +{ + callbackControl.assertIdenticalMutex ( this->cbMutex ); + return this->notify.varArgsPrintFormated ( pformat, args ); +} + +inline void cac::attachToClientCtx () +{ + this->notify.attachToClientCtx (); +} + +inline char * cac::allocateSmallBufferTCP () +{ + // this locks internally + return ( char * ) freeListMalloc ( this->tcpSmallRecvBufFreeList ); +} + +inline void cac::releaseSmallBufferTCP ( char *pBuf ) +{ + // this locks internally + freeListFree ( this->tcpSmallRecvBufFreeList, pBuf ); +} + +inline unsigned cac::largeBufferSizeTCP () const +{ + return this->maxRecvBytesTCP; +} + +inline char * cac::allocateLargeBufferTCP () +{ + // this locks internally + return ( char * ) freeListMalloc ( this->tcpLargeRecvBufFreeList ); +} + +inline void cac::releaseLargeBufferTCP ( char *pBuf ) +{ + // this locks internally + freeListFree ( this->tcpLargeRecvBufFreeList, pBuf ); +} + +inline unsigned cac::beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->beaconAnomalyCount; +} + +inline notifyGuard::notifyGuard ( cacContextNotify & notifyIn ) : + notify ( notifyIn ) +{ + this->notify.callbackProcessingInitiateNotify (); +} + +inline notifyGuard::~notifyGuard () +{ + this->notify.callbackProcessingCompleteNotify (); +} + +inline callbackManager::callbackManager ( + cacContextNotify & notify, epicsMutex & callbackControl ) : + notifyGuard ( notify ), cbGuard ( callbackControl ) +{ +} + +inline nciu * cac::lookupChannel ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & idIn ) +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->chanTable.lookup ( idIn ); +} + +inline const char * cac :: pLocalHostName () +{ + return _refLocalHostName->pointer (); +} + +inline unsigned cac :: + maxContiguousFrames ( epicsGuard < epicsMutex > & ) const +{ + return maxContigFrames; +} + +inline double cac :: + connectionTimeout ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->connTMO; +} + +#endif // ifdef cach diff --git a/src/ca/cacChannel.cpp b/src/ca/cacChannel.cpp new file mode 100644 index 000000000..c1a52a002 --- /dev/null +++ b/src/ca/cacChannel.cpp @@ -0,0 +1,145 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "localHostName.h" +#include "cacIO.h" + +class CACChannelPrivate { +public: + CACChannelPrivate (); + unsigned getHostName ( char * pBuf, unsigned bufLength ); + const char * pHostName (); +private: + epicsSingleton < localHostName > :: reference + _refLocalHostName; +}; + +static epicsThreadOnceId cacChannelIdOnce = EPICS_THREAD_ONCE_INIT; + +const cacChannel::priLev cacChannel::priorityMax = 99u; +const cacChannel::priLev cacChannel::priorityMin = 0u; +const cacChannel::priLev cacChannel::priorityDefault = priorityMin; +const cacChannel::priLev cacChannel::priorityLinksDB = priorityMax; +const cacChannel::priLev cacChannel::priorityArchive = ( priorityMax - priorityMin ) / 2; +const cacChannel::priLev cacChannel::priorityOPI = priorityMin; + +cacChannel::~cacChannel () +{ +} + +caAccessRights cacChannel::accessRights ( + epicsGuard < epicsMutex > & ) const +{ + static caAccessRights ar ( true, true ); + return ar; +} + +unsigned cacChannel::searchAttempts ( + epicsGuard < epicsMutex > & ) const +{ + return 0u; +} + +double cacChannel::beaconPeriod ( + epicsGuard < epicsMutex > & ) const +{ + return - DBL_MAX; +} + +double cacChannel::receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const +{ + return - DBL_MAX; +} + +bool cacChannel::ca_v42_ok ( + epicsGuard < epicsMutex > & ) const +{ + return true; +} + +bool cacChannel::connected ( + epicsGuard < epicsMutex > & ) const +{ + return true; +} + +CACChannelPrivate :: + CACChannelPrivate() : + _refLocalHostName ( localHostNameCache.getReference () ) +{ +} + +inline unsigned CACChannelPrivate :: + getHostName ( char * pBuf, unsigned bufLength ) +{ + return _refLocalHostName->getName ( pBuf, bufLength ); +} + +inline const char * CACChannelPrivate :: + pHostName () +{ + return _refLocalHostName->pointer (); +} + +static CACChannelPrivate * pCACChannelPrivate = 0; + +// runs once only for each process +extern "C" void cacChannelSetup ( void * ) +{ + pCACChannelPrivate = new CACChannelPrivate (); +} + +// the default is to assume that it is a locally hosted channel +unsigned cacChannel::getHostName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLength ) const throw () +{ + if ( bufLength ) { + epicsThreadOnce ( & cacChannelIdOnce, cacChannelSetup, 0); + return pCACChannelPrivate->getHostName ( pBuf, bufLength ); + } + return 0u; +} + +// the default is to assume that it is a locally hosted channel +const char * cacChannel::pHostName ( + epicsGuard < epicsMutex > & ) const throw () +{ + epicsThreadOnce ( & cacChannelIdOnce, cacChannelSetup, 0); + return pCACChannelPrivate->pHostName (); +} + +cacContext::~cacContext () {} + +cacService::~cacService () {} + + diff --git a/src/ca/cacChannelNotify.cpp b/src/ca/cacChannelNotify.cpp new file mode 100644 index 000000000..08d2cab94 --- /dev/null +++ b/src/ca/cacChannelNotify.cpp @@ -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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "iocinf.h" + +#define epicsExportSharedSymbols +#include "cacIO.h" +#undef epicsExportSharedSymbols + +cacChannelNotify::~cacChannelNotify () +{ +} + diff --git a/src/ca/cacContextNotify.cpp b/src/ca/cacContextNotify.cpp new file mode 100644 index 000000000..a4498ac04 --- /dev/null +++ b/src/ca/cacContextNotify.cpp @@ -0,0 +1,43 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include + +#include "iocinf.h" + +#define epicsExportSharedSymbols +#include "cacIO.h" +#undef epicsExportSharedSymbols + +cacContextNotify::~cacContextNotify () +{ +} + +void cacContextNotify::callbackProcessingInitiateNotify () +{ +} + +void cacContextNotify::callbackProcessingCompleteNotify () +{ +} + + + diff --git a/src/ca/cacIO.h b/src/ca/cacIO.h new file mode 100644 index 000000000..8086f1239 --- /dev/null +++ b/src/ca/cacIO.h @@ -0,0 +1,373 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef cacIOh +#define cacIOh + +// +// Open Issues +// ----------- +// +// 1) A status code from the old client side interface is passed +// to the exception notify callback. Should we just pass a string? +// If so, then how do they detect the type of error and recover. +// Perhaps we should call a different vf for each type of exception. +// +// 2) Some exception types are present here but there is no common +// exception base class in use. +// +// 3) Should I be passing the channel reference in cacChannelNotify? +// +// 4) Should the code for caAccessRights not be inline so that this +// interface is version independent. +// +// + +#include +#include + +#ifdef epicsExportSharedSymbols +# define cacIOh_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "tsDLList.h" +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsThread.h" + +#ifdef cacIOh_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + + +class cacChannel; + +typedef unsigned long arrayElementCount; + +// 1) this should not be passing caerr.h status to the exception callback +// 2) needless-to-say the data should be passed here using the new data access API +class epicsShareClass cacWriteNotify { // X aCC 655 +public: + virtual ~cacWriteNotify () = 0; + virtual void completion ( epicsGuard < epicsMutex > & ) = 0; +// we should probably have a different vf for each type of exception ???? + virtual void exception ( + epicsGuard < epicsMutex > &, + int status, const char * pContext, + unsigned type, arrayElementCount count ) = 0; +}; + +// 1) this should not be passing caerr.h status to the exception callback +// 2) needless-to-say the data should be passed here using the new data access API +class epicsShareClass cacReadNotify { // X aCC 655 +public: + virtual ~cacReadNotify () = 0; + virtual void completion ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pData ) = 0; +// we should probably have a different vf for each type of exception ???? + virtual void exception ( + epicsGuard < epicsMutex > &, int status, + const char * pContext, unsigned type, + arrayElementCount count ) = 0; +}; + +// 1) this should not be passing caerr.h status to the exception callback +// 2) needless-to-say the data should be passed here using the new data access API +class epicsShareClass cacStateNotify { // X aCC 655 +public: + virtual ~cacStateNotify () = 0; + virtual void current ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pData ) = 0; +// we should probably have a different vf for each type of exception ???? + virtual void exception ( + epicsGuard < epicsMutex > &, int status, + const char *pContext, unsigned type, + arrayElementCount count ) = 0; +}; + +class caAccessRights { +public: + caAccessRights ( + bool readPermit = false, + bool writePermit = false, + bool operatorConfirmationRequest = false); + void setReadPermit (); + void setWritePermit (); + void setOperatorConfirmationRequest (); + void clrReadPermit (); + void clrWritePermit (); + void clrOperatorConfirmationRequest (); + bool readPermit () const; + bool writePermit () const; + bool operatorConfirmationRequest () const; +private: + bool f_readPermit:1; + bool f_writePermit:1; + bool f_operatorConfirmationRequest:1; +}; + +class epicsShareClass cacChannelNotify { // X aCC 655 +public: + virtual ~cacChannelNotify () = 0; + virtual void connectNotify ( epicsGuard < epicsMutex > & ) = 0; + virtual void disconnectNotify ( epicsGuard < epicsMutex > & ) = 0; + virtual void serviceShutdownNotify ( + epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; + virtual void accessRightsNotify ( + epicsGuard < epicsMutex > &, const caAccessRights & ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext ) = 0; +// we should probably have a different vf for each type of exception ???? + virtual void readException ( + epicsGuard < epicsMutex > &, int status, const char *pContext, + unsigned type, arrayElementCount count, void *pValue ) = 0; +// we should probably have a different vf for each type of exception ???? + virtual void writeException ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + unsigned type, arrayElementCount count ) = 0; +}; + +// +// Notes +// 1) This interface assumes that when a channel is deleted then all +// attached IO is deleted. This is left over from the old interface, +// but perhaps is a bad practice that should be eliminated? If so, +// then the IO should not store or use a pointer to the channel. +// +class epicsShareClass cacChannel { +public: + typedef unsigned priLev; + static const priLev priorityMax; + static const priLev priorityMin; + static const priLev priorityDefault; + static const priLev priorityLinksDB; + static const priLev priorityArchive; + static const priLev priorityOPI; + + typedef unsigned ioid; + enum ioStatus { iosSynch, iosAsynch }; + + cacChannel ( cacChannelNotify & ); + virtual void destroy ( + epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; + cacChannelNotify & notify () const; // required ????? + virtual unsigned getName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw () = 0; + // !! deprecated, avoid use !! + virtual const char * pName ( + epicsGuard < epicsMutex > & guard ) const throw () = 0; + virtual void show ( + epicsGuard < epicsMutex > &, + unsigned level ) const = 0; + virtual void initiateConnect ( + epicsGuard < epicsMutex > & ) = 0; + virtual unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; + virtual void flush ( + epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; + virtual ioStatus read ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + cacReadNotify &, ioid * = 0 ) = 0; + virtual void write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + const void *pValue ) = 0; + virtual ioStatus write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + const void *pValue, cacWriteNotify &, ioid * = 0 ) = 0; + virtual void subscribe ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, unsigned mask, cacStateNotify &, + ioid * = 0 ) = 0; + virtual void ioCancel ( + epicsGuard < epicsMutex > & mutualExclusionGuard, + const ioid & ) = 0; + virtual void ioShow ( + epicsGuard < epicsMutex > &, + const ioid &, unsigned level ) const = 0; + virtual short nativeType ( + epicsGuard < epicsMutex > & ) const = 0; + virtual arrayElementCount nativeElementCount ( + epicsGuard < epicsMutex > & ) const = 0; + virtual caAccessRights accessRights ( + epicsGuard < epicsMutex > & ) const; + virtual unsigned searchAttempts ( + epicsGuard < epicsMutex > & ) const; + virtual double beaconPeriod ( + epicsGuard < epicsMutex > & ) const; // negative DBL_MAX if UKN + virtual double receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const; // negative DBL_MAX if UKN + virtual bool ca_v42_ok ( + epicsGuard < epicsMutex > & ) const; + virtual bool connected ( + epicsGuard < epicsMutex > & ) const; + virtual unsigned getHostName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLength ) const throw (); + // !! deprecated, avoid use !! + virtual const char * pHostName ( + epicsGuard < epicsMutex > & guard ) const throw (); + + // exceptions + class badString {}; + class badType {}; + class badPriority {}; + class outOfBounds {}; + class badEventSelection {}; + class noWriteAccess {}; + class noReadAccess {}; + class notConnected {}; + class unsupportedByService {}; + class msgBodyCacheTooSmall {}; // hopefully this one goes away in the future + class requestTimedOut {}; + +protected: + virtual ~cacChannel () = 0; + +private: + cacChannelNotify & callback; + cacChannel ( const cacChannel & ); + cacChannel & operator = ( const cacChannel & ); +}; + +class epicsShareClass cacContext { // X aCC 655 +public: + virtual ~cacContext (); + virtual cacChannel & createChannel ( + epicsGuard < epicsMutex > &, + const char * pChannelName, cacChannelNotify &, + cacChannel::priLev = cacChannel::priorityDefault ) = 0; + virtual void flush ( + epicsGuard < epicsMutex > & ) = 0; + virtual unsigned circuitCount ( + epicsGuard < epicsMutex > & ) const = 0; + virtual void selfTest ( + epicsGuard < epicsMutex > & ) const = 0; + virtual unsigned beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & ) const = 0; + virtual void show ( + epicsGuard < epicsMutex > &, unsigned level ) const = 0; +}; + +class epicsShareClass cacContextNotify { // X aCC 655 +public: + virtual ~cacContextNotify () = 0; + virtual cacContext & createNetworkContext ( + epicsMutex & mutualExclusion, epicsMutex & callbackControl ) = 0; +// we should probably have a different vf for each type of exception ???? + virtual void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo ) = 0; +// perhaps this should be phased out in deference to the exception mechanism + virtual int varArgsPrintFormated ( const char * pformat, va_list args ) const = 0; +// backwards compatibility (from here down) + virtual void attachToClientCtx () = 0; + virtual void callbackProcessingInitiateNotify () = 0; + virtual void callbackProcessingCompleteNotify () = 0; +}; + +// **** Lock Hierarchy **** +// callbackControl must be taken before mutualExclusion if both are held at +// the same time +class epicsShareClass cacService { // X aCC 655 +public: + virtual ~cacService () = 0; + virtual cacContext & contextCreate ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, + cacContextNotify & ) = 0; +}; + +epicsShareFunc void epicsShareAPI caInstallDefaultService ( cacService & service ); + +epicsShareExtern epicsThreadPrivateId caClientCallbackThreadId; + +inline cacChannel::cacChannel ( cacChannelNotify & notify ) : + callback ( notify ) +{ +} + +inline cacChannelNotify & cacChannel::notify () const +{ + return this->callback; +} + +inline caAccessRights::caAccessRights ( + bool readPermit, bool writePermit, bool operatorConfirmationRequest) : + f_readPermit ( readPermit ), f_writePermit ( writePermit ), + f_operatorConfirmationRequest ( operatorConfirmationRequest ) {} + +inline void caAccessRights::setReadPermit () +{ + this->f_readPermit = true; +} + +inline void caAccessRights::setWritePermit () +{ + this->f_writePermit = true; +} + +inline void caAccessRights::setOperatorConfirmationRequest () +{ + this->f_operatorConfirmationRequest = true; +} + +inline void caAccessRights::clrReadPermit () +{ + this->f_readPermit = false; +} + +inline void caAccessRights::clrWritePermit () +{ + this->f_writePermit = false; +} + +inline void caAccessRights::clrOperatorConfirmationRequest () +{ + this->f_operatorConfirmationRequest = false; +} + +inline bool caAccessRights::readPermit () const +{ + return this->f_readPermit; +} + +inline bool caAccessRights::writePermit () const +{ + return this->f_writePermit; +} + +inline bool caAccessRights::operatorConfirmationRequest () const +{ + return this->f_operatorConfirmationRequest; +} + +#endif // ifndef cacIOh diff --git a/src/ca/cacReadNotify.cpp b/src/ca/cacReadNotify.cpp new file mode 100644 index 000000000..23284c8df --- /dev/null +++ b/src/ca/cacReadNotify.cpp @@ -0,0 +1,30 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +#define epicsExportSharedSymbols +#include "cacIO.h" +#undef epicsExportSharedSymbols + +cacReadNotify::~cacReadNotify () +{ +} diff --git a/src/ca/cacStateNotify.cpp b/src/ca/cacStateNotify.cpp new file mode 100644 index 000000000..08852489a --- /dev/null +++ b/src/ca/cacStateNotify.cpp @@ -0,0 +1,30 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +#define epicsExportSharedSymbols +#include "cacIO.h" +#undef epicsExportSharedSymbols + +cacStateNotify::~cacStateNotify () +{ +} diff --git a/src/ca/cacWriteNotify.cpp b/src/ca/cacWriteNotify.cpp new file mode 100644 index 000000000..13d47cd45 --- /dev/null +++ b/src/ca/cacWriteNotify.cpp @@ -0,0 +1,30 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "iocinf.h" + +#define epicsExportSharedSymbols +#include "cacIO.h" +#undef epicsExportSharedSymbols + +cacWriteNotify::~cacWriteNotify () +{ +} diff --git a/src/ca/cadef.h b/src/ca/cadef.h new file mode 100644 index 000000000..7095512e3 --- /dev/null +++ b/src/ca/cadef.h @@ -0,0 +1,903 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + */ + +#ifndef INCLcadefh +#define INCLcadefh + +/* + * done in two ifdef steps so that we will remain compatible with + * traditional C + */ +#ifndef CA_DONT_INCLUDE_STDARGH +# include +#endif + +#ifdef epicsExportSharedSymbols +# define INCLcadefh_accessh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsThread.h" + +#ifdef INCLcadefh_accessh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + + +#include "caerr.h" +#include "db_access.h" +#include "caeventmask.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct oldChannelNotify *chid; +typedef chid chanId; /* for when the structures field name is "chid" */ +typedef long chtype; +typedef struct oldSubscription *evid; +typedef double ca_real; + +/* arguments passed to user connection handlers */ +struct connection_handler_args { + chanId chid; /* channel id */ + long op; /* one of CA_OP_CONN_UP or CA_OP_CONN_DOWN */ +}; + +typedef void caCh (struct connection_handler_args args); + +typedef struct ca_access_rights { + unsigned read_access:1; + unsigned write_access:1; +} caar; + +/* arguments passed to user access rights handlers */ +struct access_rights_handler_args { + chanId chid; /* channel id */ + caar ar; /* new access rights state */ +}; + +typedef void caArh (struct access_rights_handler_args args); + +/* The conversion routine to call for each type */ +#define VALID_TYPE(TYPE) (((unsigned short)TYPE)<=LAST_BUFFER_TYPE) + +/* + * Arguments passed to event handlers and get/put call back handlers. + * + * The status field below is the CA ECA_XXX status of the requested + * operation which is saved from when the operation was attempted in the + * server and copied back to the clients call back routine. + * If the status is not ECA_NORMAL then the dbr pointer will be NULL + * and the requested operation can not be assumed to be successful. + */ +typedef struct event_handler_args { + void *usr; /* user argument supplied with request */ + chanId chid; /* channel id */ + long type; /* the type of the item returned */ + long count; /* the element count of the item returned */ + const void *dbr; /* a pointer to the item returned */ + int status; /* ECA_XXX status of the requested op from the server */ +} evargs; +typedef void caEventCallBackFunc (struct event_handler_args); + +epicsShareFunc void epicsShareAPI ca_test_event +( + struct event_handler_args +); + +/* arguments passed to user exception handlers */ +struct exception_handler_args { + void *usr; /* user argument supplied when installed */ + chanId chid; /* channel id (may be nill) */ + long type; /* type requested */ + long count; /* count requested */ + void *addr; /* user's address to write results of CA_OP_GET */ + long stat; /* channel access ECA_XXXX status code */ + long op; /* CA_OP_GET, CA_OP_PUT, ..., CA_OP_OTHER */ + const char *ctx; /* a character string containing context info */ + const char *pFile; /* source file name (may be NULL) */ + unsigned lineNo; /* source file line number (may be zero) */ +}; + +typedef unsigned CA_SYNC_GID; + +/* + * External OP codes for CA operations + */ +#define CA_OP_GET 0 +#define CA_OP_PUT 1 +#define CA_OP_CREATE_CHANNEL 2 +#define CA_OP_ADD_EVENT 3 +#define CA_OP_CLEAR_EVENT 4 +#define CA_OP_OTHER 5 + +/* + * used with connection_handler_args + */ +#define CA_OP_CONN_UP 6 +#define CA_OP_CONN_DOWN 7 + +/* depricated */ +#define CA_OP_SEARCH 2 + +/* + * provides efficient test and display of channel access errors + */ +#define SEVCHK(CA_ERROR_CODE, MESSAGE_STRING) \ +{ \ + int ca_unique_status_name = (CA_ERROR_CODE); \ + if(!(ca_unique_status_name & CA_M_SUCCESS)) \ + ca_signal_with_file_and_lineno( \ + ca_unique_status_name, \ + (MESSAGE_STRING), \ + __FILE__, \ + __LINE__); \ +} + + +#define TYPENOTCONN (-1) /* the channel's native type when disconnected */ +epicsShareFunc short epicsShareAPI ca_field_type (chid chan); +epicsShareFunc unsigned long epicsShareAPI ca_element_count (chid chan); +epicsShareFunc const char * epicsShareAPI ca_name (chid chan); +epicsShareFunc void epicsShareAPI ca_set_puser (chid chan, void *puser); +epicsShareFunc void * epicsShareAPI ca_puser (chid chan); +epicsShareFunc unsigned epicsShareAPI ca_read_access (chid chan); +epicsShareFunc unsigned epicsShareAPI ca_write_access (chid chan); + +/* + * cs_ - `channel state' + * + * cs_never_conn valid chid, IOC not found + * cs_prev_conn valid chid, IOC was found, but unavailable + * cs_conn valid chid, IOC was found, still available + * cs_closed channel deleted by user + */ +enum channel_state {cs_never_conn, cs_prev_conn, cs_conn, cs_closed}; +epicsShareFunc enum channel_state epicsShareAPI ca_state (chid chan); + +/************************************************************************/ +/* Perform Library Initialization */ +/* */ +/* Must be called once before calling any of the other routines */ +/************************************************************************/ +epicsShareFunc int epicsShareAPI ca_task_initialize (void); +enum ca_preemptive_callback_select +{ ca_disable_preemptive_callback, ca_enable_preemptive_callback }; +epicsShareFunc int epicsShareAPI ca_context_create (enum ca_preemptive_callback_select select); +epicsShareFunc void epicsShareAPI ca_detach_context (); + +/************************************************************************/ +/* Remove CA facility from your task */ +/* */ +/* Normally called automatically at task exit */ +/************************************************************************/ +epicsShareFunc int epicsShareAPI ca_task_exit (void); +epicsShareFunc void epicsShareAPI ca_context_destroy (void); + +typedef unsigned capri; +#define CA_PRIORITY_MAX 99 +#define CA_PRIORITY_MIN 0 +#define CA_PRIORITY_DEFAULT CA_PRIORITY_MIN + +#define CA_PRIORITY_DB_LINKS 80 +#define CA_PRIORITY_ARCHIVE 20 +#define CA_PRIORITY_OPI 0 + +/* + * ca_create_channel () + * + * pChanName R channel name string + * pConnStateCallback R address of connection state change + * callback function + * pUserPrivate R placed in the channel's user private field + * o can be fetched later by ca_puser(CHID) + * o passed as void * arg to *pConnectCallback above + * priority R priority level in the server 0 - 100 + * pChanID RW channel id written here + */ +epicsShareFunc int epicsShareAPI ca_create_channel +( + const char *pChanName, + caCh *pConnStateCallback, + void *pUserPrivate, + capri priority, + chid *pChanID +); + +/* + * ca_change_connection_event() + * + * chan R channel identifier + * pfunc R address of connection call-back function + */ +epicsShareFunc int epicsShareAPI ca_change_connection_event +( + chid chan, + caCh * pfunc +); + +/* + * ca_replace_access_rights_event () + * + * chan R channel identifier + * pfunc R address of access rights call-back function + */ +epicsShareFunc int epicsShareAPI ca_replace_access_rights_event ( + chid chan, + caArh *pfunc +); + +/* + * ca_add_exception_event () + * + * replace the default exception handler + * + * pfunc R address of exception call-back function + * pArg R copy of this pointer passed to exception + * call-back function + */ +typedef void caExceptionHandler (struct exception_handler_args); +epicsShareFunc int epicsShareAPI ca_add_exception_event +( + caExceptionHandler *pfunc, + void *pArg +); + +/* + * ca_clear_channel() + * - deallocate resources reserved for a channel + * + * chanId R channel ID + */ +epicsShareFunc int epicsShareAPI ca_clear_channel +( + chid chanId +); + +/************************************************************************/ +/* Write a value to a channel */ +/************************************************************************/ +/* + * ca_bput() + * + * WARNING: this copies the new value from a string (dbr_string_t) + * (and not as an integer) + * + * chan R channel identifier + * pValue R new channel value string copied from this location + */ +#define ca_bput(chan, pValue) \ +ca_array_put(DBR_STRING, 1u, chan, (const dbr_string_t *) (pValue)) + +/* + * ca_rput() + * + * WARNING: this copies the new value from a dbr_float_t + * + * chan R channel identifier + * pValue R new channel value copied from this location + */ +#define ca_rput(chan,pValue) \ +ca_array_put(DBR_FLOAT, 1u, chan, (const dbr_float_t *) pValue) + +/* + * ca_put() + * + * type R data type from db_access.h + * chan R channel identifier + * pValue R new channel value copied from this location + */ +#define ca_put(type, chan, pValue) ca_array_put (type, 1u, chan, pValue) + +/* + * ca_array_put() + * + * type R data type from db_access.h + * count R array element count + * chan R channel identifier + * pValue R new channel value copied from this location + */ +epicsShareFunc int epicsShareAPI ca_array_put +( + chtype type, + unsigned long count, + chid chanId, + const void * pValue +); + +/* + * ca_array_put_callback() + * + * This routine functions identically to the original ca put request + * with the addition of a callback to the user supplied function + * after recod processing completes in the IOC. The arguments + * to the user supplied callback function are declared in + * the structure event_handler_args and include the pointer + * sized user argument supplied when ca_array_put_callback() is called. + * + * type R data type from db_access.h + * count R array element count + * chan R channel identifier + * pValue R new channel value copied from this location + * pFunc R pointer to call-back function + * pArg R copy of this pointer passed to pFunc + */ +epicsShareFunc int epicsShareAPI ca_array_put_callback +( + chtype type, + unsigned long count, + chid chanId, + const void * pValue, + caEventCallBackFunc * pFunc, + void * pArg +); + +#define ca_put_callback(type, chan, pValue, pFunc, pArg) \ + ca_array_put_callback(type, 1u, chan, pValue, pFunc, pArg) + +/************************************************************************/ +/* Read a value from a channel */ +/************************************************************************/ + +/* + * ca_bget() + * + * WARNING: this copies the new value into a string (dbr_string_t) + * (and not into an integer) + * + * chan R channel identifier + * pValue W channel value copied to this location + */ +#define ca_bget(chan, pValue) \ +ca_array_get(DBR_STRING, 1u, chan, (dbr_string_t *)(pValue)) + +/* + * ca_rget() + * + * WARNING: this copies the new value into a 32 bit float (dbr_float_t) + * + * chan R channel identifier + * pValue W channel value copied to this location + */ +#define ca_rget(chan, pValue) \ +ca_array_get(DBR_FLOAT, 1u, chan, (dbr_float_t *)(pValue)) + +/* + * ca_rget() + * + * type R data type from db_access.h + * chan R channel identifier + * pValue W channel value copied to this location + */ +#define ca_get(type, chan, pValue) ca_array_get(type, 1u, chan, pValue) + +/* + * ca_array_get() + * + * type R data type from db_access.h + * count R array element count + * chan R channel identifier + * pValue W channel value copied to this location + */ +epicsShareFunc int epicsShareAPI ca_array_get +( + chtype type, + unsigned long count, + chid chanId, + void * pValue +); + +/************************************************************************/ +/* Read a value from a channel and run a callback when the value */ +/* returns */ +/* */ +/* */ +/************************************************************************/ +/* + * ca_bget_callback() + * + * WARNING: this returns the new value as a string (dbr_string_t) + * (and not as an integer) + * + * chan R channel identifier + * pFunc R pointer to call-back function + * pArg R copy of this pointer passed to pFunc + */ +#define ca_bget_callback(chan, pFunc, pArg)\ +ca_array_get_callback (DBR_STRING, 1u, chan, pFunc, pArg) + +/* + * ca_rget_callback() + * + * WARNING: this returns the new value as a float (dbr_float_t) + * + * chan R channel identifier + * pFunc R pointer to call-back function + * pArg R copy of this pointer passed to pFunc + */ +#define ca_rget_callback(chan, pFunc, pArg)\ +ca_array_get_callback (DBR_FLOAT, 1u, chan, pFunc, pArg) + +/* + * ca_get_callback() + * + * type R data type from db_access.h + * chan R channel identifier + * pFunc R pointer to call-back function + * pArg R copy of this pointer passed to pFunc + */ +#define ca_get_callback(type, chan, pFunc, pArg)\ +ca_array_get_callback (type, 1u, chan, pFunc, pArg) + +/* + * ca_array_get_callback() + * + * type R data type from db_access.h + * count R array element count + * chan R channel identifier + * pFunc R pointer to call-back function + * pArg R copy of this pointer passed to pFunc + */ +epicsShareFunc int epicsShareAPI ca_array_get_callback +( + chtype type, + unsigned long count, + chid chanId, + caEventCallBackFunc * pFunc, + void * pArg +); + +/************************************************************************/ +/* Specify a function to be executed whenever significant changes */ +/* occur to a channel. */ +/* NOTES: */ +/* 1) Evid may be omited by passing a NULL pointer */ +/* */ +/* 2) An array count of zero specifies the native db count */ +/* */ +/************************************************************************/ + +/* + * ca_create_subscription () + * + * type R data type from db_access.h + * count R array element count + * chan R channel identifier + * mask R event mask - one of {DBE_VALUE, DBE_ALARM, DBE_LOG} + * pFunc R pointer to call-back function + * pArg R copy of this pointer passed to pFunc + * pEventID W event id written at specified address + */ +epicsShareFunc int epicsShareAPI ca_create_subscription +( + chtype type, + unsigned long count, + chid chanId, + long mask, + caEventCallBackFunc * pFunc, + void * pArg, + evid * pEventID +); + +/************************************************************************/ +/* Remove a function from a list of those specified to run */ +/* whenever significant changes occur to a channel */ +/* */ +/************************************************************************/ +/* + * ca_clear_subscription() + * + * eventID R event id + */ +epicsShareFunc int epicsShareAPI ca_clear_subscription +( + evid eventID +); + +epicsShareFunc chid epicsShareAPI ca_evid_to_chid ( evid id ); + + +/************************************************************************/ +/* */ +/* Requested data is not necessarily stable prior to */ +/* return from called subroutine. Call ca_pend_io() */ +/* to guarantee that requested data is stable. Call the routine */ +/* ca_flush_io() to force all outstanding requests to be */ +/* sent out over the network. Significant increases in */ +/* performance have been measured when batching several remote */ +/* requests together into one message. Additional */ +/* improvements can be obtained by performing local processing */ +/* in parallel with outstanding remote processing. */ +/* */ +/* FLOW OF TYPICAL APPLICATION */ +/* */ +/* search() ! Obtain Channel ids */ +/* . ! " */ +/* . ! " */ +/* pend_io ! wait for channels to connect */ +/* */ +/* get() ! several requests for remote info */ +/* get() ! " */ +/* add_event() ! " */ +/* get() ! " */ +/* . */ +/* . */ +/* . */ +/* flush_io() ! send get requests */ +/* ! optional parallel processing */ +/* . ! " */ +/* . ! " */ +/* pend_io() ! wait for replies from get requests */ +/* . ! access to requested data */ +/* . ! " */ +/* pend_event() ! wait for requested events */ +/* */ +/************************************************************************/ + +/************************************************************************/ +/* These routines wait for channel subscription events and call the */ +/* functions specified with add_event when events occur. If the */ +/* timeout is specified as 0 an infinite timeout is assumed. */ +/* ca_flush_io() is called by this routine. If ca_pend_io () */ +/* is called when no IO is outstanding then it will return immediately */ +/* without processing. */ +/************************************************************************/ + +/* + * ca_pend_event() + * + * timeOut R wait for this delay in seconds + */ +epicsShareFunc int epicsShareAPI ca_pend_event (ca_real timeOut); +#define ca_poll() ca_pend_event(1e-12) + +/* + * ca_pend_io() + * + * timeOut R wait for this delay in seconds but return early + * if all get requests (or search requests with null + * connection handler pointer have completed) + */ +epicsShareFunc int epicsShareAPI ca_pend_io (ca_real timeOut); + +/* calls ca_pend_io() if early is true otherwise ca_pend_event() is called */ +epicsShareFunc int epicsShareAPI ca_pend (ca_real timeout, int early); + +/* + * ca_test_io() + * + * returns TRUE when get requests (or search requests with null + * connection handler pointer) are outstanding + */ +epicsShareFunc int epicsShareAPI ca_test_io (void); + +/************************************************************************/ +/* Send out all outstanding messages in the send queue */ +/************************************************************************/ +/* + * ca_flush_io() + */ +epicsShareFunc int epicsShareAPI ca_flush_io (void); + + +/* + * ca_signal() + * + * errorCode R status returned from channel access function + * pCtxStr R context string included with error print out + */ +epicsShareFunc void epicsShareAPI ca_signal +( + long errorCode, + const char *pCtxStr +); + +/* + * ca_signal_with_file_and_lineno() + * errorCode R status returned from channel access function + * pCtxStr R context string included with error print out + * pFileStr R file name string included with error print out + * lineNo R line number included with error print out + * + */ +epicsShareFunc void epicsShareAPI ca_signal_with_file_and_lineno +( + long errorCode, + const char *pCtxStr, + const char *pFileStr, + int lineNo +); + +/* + * ca_signal_formated() + * errorCode R status returned from channel access function + * pFileStr R file name string included with error print out + * lineNo R line number included with error print out + * pFormat R printf dtyle format string (and optional arguments) + * + */ +epicsShareFunc void epicsShareAPI ca_signal_formated (long ca_status, const char *pfilenm, + int lineno, const char *pFormat, ...); + +/* + * ca_host_name_function() + * + * channel R channel identifier + * + * !!!! this function is _not_ thread safe !!!! + */ +epicsShareFunc const char * epicsShareAPI ca_host_name (chid channel); +/* thread safe version */ +epicsShareFunc unsigned epicsShareAPI ca_get_host_name ( chid pChan, + char *pBuf, unsigned bufLength ); + +/* + * CA_ADD_FD_REGISTRATION + * + * call their function with their argument whenever + * a new fd is added or removed + * (for use with a manager of the select system call under UNIX) + * + * if (opened) then fd was created + * if (!opened) then fd was deleted + * + */ +typedef void CAFDHANDLER (void *parg, int fd, int opened); + +/* + * ca_add_fd_registration() + * + * pHandler R pointer to function which is to be called + * when an fd is created or deleted + * pArg R argument passed to above function + */ +epicsShareFunc int epicsShareAPI ca_add_fd_registration +( + CAFDHANDLER *pHandler, + void *pArg +); + + +/* + * CA synch groups + * + * This facility will allow the programmer to create + * any number of synchronization groups. The programmer might then + * interleave IO requests within any of the groups. Once The + * IO operations are initiated then the programmer is free to + * block for IO completion within any one of the groups as needed. + */ + +/* + * ca_sg_create() + * + * create a sync group + * + * pgid W pointer to sync group id that will be written + */ +epicsShareFunc int epicsShareAPI ca_sg_create (CA_SYNC_GID * pgid); + +/* + * ca_sg_delete() + * + * delete a sync group + * + * gid R sync group id + */ +epicsShareFunc int epicsShareAPI ca_sg_delete (const CA_SYNC_GID gid); + +/* + * ca_sg_block() + * + * block for IO performed within a sync group to complete + * + * gid R sync group id + * timeout R wait for this duration prior to timing out + * and returning ECA_TIMEOUT + */ +epicsShareFunc int epicsShareAPI ca_sg_block (const CA_SYNC_GID gid, ca_real timeout); + +/* + * ca_sg_test() + * + * test for sync group IO operations in progress + * + * gid R sync group id + * + * returns one of ECA_BADSYNCGRP, ECA_IOINPROGRESS, ECA_IODONE + */ +epicsShareFunc int epicsShareAPI ca_sg_test (const CA_SYNC_GID gid); + +/* + * ca_sg_reset + * + * gid R sync group id + */ +epicsShareFunc int epicsShareAPI ca_sg_reset(const CA_SYNC_GID gid); + +/* + * ca_sg_array_get() + * + * initiate a get within a sync group + * (essentially a ca_array_get() with a sync group specified) + * + * gid R sync group id + * type R data type from db_access.h + * count R array element count + * chan R channel identifier + * pValue W channel value copied to this location + */ +epicsShareFunc int epicsShareAPI ca_sg_array_get +( + const CA_SYNC_GID gid, + chtype type, + unsigned long count, + chid chan, + void *pValue +); + +#define ca_sg_get(gid, type, chan, pValue) \ +ca_sg_array_get (gid, type, 1u, chan, pValue) + +/* + * ca_sg_array_put() + * + * initiate a put within a sync group + * (essentially a ca_array_put() with a sync group specified) + * + * gid R sync group id + * type R data type from db_access.h + * count R array element count + * chan R channel identifier + * pValue R new channel value copied from this location + */ +epicsShareFunc int epicsShareAPI ca_sg_array_put +( + const CA_SYNC_GID gid, + chtype type, + unsigned long count, + chid chan, + const void *pValue +); + +#define ca_sg_put(gid, type, chan, pValue) \ +ca_sg_array_put (gid, type, 1u, chan, pValue) + +/* + * ca_sg_stat() + * + * print status of a sync group + * + * gid R sync group id + */ +epicsShareFunc int epicsShareAPI ca_sg_stat (CA_SYNC_GID gid); + +epicsShareFunc void epicsShareAPI ca_dump_dbr (chtype type, unsigned count, const void * pbuffer); + + +/* + * ca_v42_ok() + * + * Put call back is available if the CA server is on version is 4.2 + * or higher. + * + * chan R channel identifier + * + * (returns true or false) + */ +epicsShareFunc int epicsShareAPI ca_v42_ok (chid chan); + +/* + * ca_version() + * + * returns the CA version string + */ +epicsShareFunc const char * epicsShareAPI ca_version (void); + +/* + * ca_replace_printf_handler () + * + * for apps that want to change where ca formatted + * text output goes + * + * use two ifdef's for trad C compatibility + * + * ca_printf_func R pointer to new function called when + * CA prints an error message + */ +#ifndef CA_DONT_INCLUDE_STDARGH +typedef int caPrintfFunc (const char *pformat, va_list args); +epicsShareFunc int epicsShareAPI ca_replace_printf_handler ( + caPrintfFunc *ca_printf_func +); +#endif /*CA_DONT_INCLUDE_STDARGH*/ + +/* + * (for testing purposes only) + */ +epicsShareFunc unsigned epicsShareAPI ca_get_ioc_connection_count (void); +epicsShareFunc int epicsShareAPI ca_preemtive_callback_is_enabled (void); +epicsShareFunc void epicsShareAPI ca_self_test (void); +epicsShareFunc unsigned epicsShareAPI ca_beacon_anomaly_count (void); +epicsShareFunc unsigned epicsShareAPI ca_search_attempts (chid chan); +epicsShareFunc double epicsShareAPI ca_beacon_period (chid chan); +epicsShareFunc double epicsShareAPI ca_receive_watchdog_delay (chid chan); + +/* + * used when an auxillary thread needs to join a CA client context started + * by another thread + */ +epicsShareFunc struct ca_client_context * epicsShareAPI ca_current_context (); +epicsShareFunc int epicsShareAPI ca_attach_context ( struct ca_client_context * context ); + + +epicsShareFunc int epicsShareAPI ca_client_status ( unsigned level ); +epicsShareFunc int epicsShareAPI ca_context_status ( struct ca_client_context *, unsigned level ); + +/* + * deprecated + */ +#define ca_build_channel(NAME,XXXXX,CHIDPTR,YYYYY)\ +ca_build_and_connect(NAME, XXXXX, 1, CHIDPTR, YYYYY, 0, 0) +#define ca_array_build(NAME,XXXXX, ZZZZZZ, CHIDPTR,YYYYY)\ +ca_build_and_connect(NAME, XXXXX, ZZZZZZ, CHIDPTR, YYYYY, 0, 0) +epicsShareFunc int epicsShareAPI ca_build_and_connect + ( const char *pChanName, chtype, unsigned long, + chid * pChanID, void *, caCh * pFunc, void * pArg ); +#define ca_search(pChanName, pChanID)\ +ca_search_and_connect (pChanName, pChanID, 0, 0) +epicsShareFunc int epicsShareAPI ca_search_and_connect + ( const char * pChanName, chid * pChanID, + caCh *pFunc, void * pArg ); +epicsShareFunc int epicsShareAPI ca_channel_status (epicsThreadId tid); +epicsShareFunc int epicsShareAPI ca_clear_event ( evid eventID ); +#define ca_add_event(type,chan,pFunc,pArg,pEventID)\ +ca_add_array_event(type,1u,chan,pFunc,pArg,0.0,0.0,0.0,pEventID) +#define ca_add_delta_event(TYPE,CHID,ENTRY,ARG,DELTA,EVID)\ + ca_add_array_event(TYPE,1,CHID,ENTRY,ARG,DELTA,DELTA,0.0,EVID) +#define ca_add_general_event(TYPE,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID)\ +ca_add_array_event(TYPE,1,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID) +#define ca_add_array_event(TYPE,COUNT,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID)\ +ca_add_masked_array_event(TYPE,COUNT,CHID,ENTRY,ARG,P_DELTA,N_DELTA,TO,EVID, DBE_VALUE | DBE_ALARM) +epicsShareFunc int epicsShareAPI ca_add_masked_array_event + ( chtype type, unsigned long count, chid chanId, caEventCallBackFunc * pFunc, + void * pArg, ca_real p_delta, ca_real n_delta, ca_real timeout, + evid * pEventID, long mask ); + +/* + * defunct + */ +epicsShareFunc int epicsShareAPI ca_modify_user_name ( const char *pUserName ); +epicsShareFunc int epicsShareAPI ca_modify_host_name ( const char *pHostName ); + +#ifdef __cplusplus +} +#endif + +/* + * no additions below this endif + */ +#endif /* ifndef INCLcadefh */ + diff --git a/src/ca/caerr.h b/src/ca/caerr.h new file mode 100644 index 000000000..53930962d --- /dev/null +++ b/src/ca/caerr.h @@ -0,0 +1,160 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeffrey O. Hill + * + */ + + +#ifndef INCLcaerrh +#define INCLcaerrh + +#ifdef epicsExportSharedSymbols +# define INCLcaerrh_accessh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +# include "epicsTypes.h" + +#ifdef INCLcaerrh_accessh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +/* CA Status Code Definitions */ + +#define CA_K_INFO 3 /* successful */ +#define CA_K_ERROR 2 /* failed- continue */ +#define CA_K_SUCCESS 1 /* successful */ +#define CA_K_WARNING 0 /* unsuccessful */ +#define CA_K_SEVERE 4 /* failed- quit */ +#define CA_K_FATAL CA_K_ERROR | CA_K_SEVERE + +#define CA_M_MSG_NO 0x0000FFF8 +#define CA_M_SEVERITY 0x00000007 +#define CA_M_LEVEL 0x00000003 +#define CA_M_SUCCESS 0x00000001 +#define CA_M_ERROR 0x00000002 +#define CA_M_SEVERE 0x00000004 + +#define CA_S_MSG_NO 0x0D +#define CA_S_SEVERITY 0x03 + +#define CA_V_MSG_NO 0x03 +#define CA_V_SEVERITY 0x00 +#define CA_V_SUCCESS 0x00 + +/* Define MACROS to extract/insert individual fields from a status value */ + +#define CA_EXTRACT_MSG_NO(code)\ +( ( (code) & CA_M_MSG_NO ) >> CA_V_MSG_NO ) +#define CA_EXTRACT_SEVERITY(code)\ +( ( (code) & CA_M_SEVERITY ) >> CA_V_SEVERITY ) +#define CA_EXTRACT_SUCCESS(code)\ +( ( (code) & CA_M_SUCCESS ) >> CA_V_SUCCESS ) + +#define CA_INSERT_MSG_NO(code)\ +( ((code)<< CA_V_MSG_NO) & CA_M_MSG_NO ) +#define CA_INSERT_SEVERITY(code)\ +( ((code)<< CA_V_SEVERITY)& CA_M_SEVERITY ) +#define CA_INSERT_SUCCESS(code)\ +( ((code)<< CA_V_SUCCESS) & CA_M_SUCCESS ) + +#define DEFMSG(SEVERITY,NUMBER)\ +(CA_INSERT_MSG_NO(NUMBER) | CA_INSERT_SEVERITY(SEVERITY)) + +/* + * In the lines below "defunct" indicates that current release + * servers and client library will not return this error code, but + * servers on earlier releases that communicate with current clients + * might still generate exceptions with these error constants + */ +#define ECA_NORMAL DEFMSG(CA_K_SUCCESS, 0) /* success */ +#define ECA_MAXIOC DEFMSG(CA_K_ERROR, 1) /* defunct */ +#define ECA_UKNHOST DEFMSG(CA_K_ERROR, 2) /* defunct */ +#define ECA_UKNSERV DEFMSG(CA_K_ERROR, 3) /* defunct */ +#define ECA_SOCK DEFMSG(CA_K_ERROR, 4) /* defunct */ +#define ECA_CONN DEFMSG(CA_K_WARNING, 5) /* defunct */ +#define ECA_ALLOCMEM DEFMSG(CA_K_WARNING, 6) +#define ECA_UKNCHAN DEFMSG(CA_K_WARNING, 7) /* defunct */ +#define ECA_UKNFIELD DEFMSG(CA_K_WARNING, 8) /* defunct */ +#define ECA_TOLARGE DEFMSG(CA_K_WARNING, 9) +#define ECA_TIMEOUT DEFMSG(CA_K_WARNING, 10) +#define ECA_NOSUPPORT DEFMSG(CA_K_WARNING, 11) /* defunct */ +#define ECA_STRTOBIG DEFMSG(CA_K_WARNING, 12) /* defunct */ +#define ECA_DISCONNCHID DEFMSG(CA_K_ERROR, 13) /* defunct */ +#define ECA_BADTYPE DEFMSG(CA_K_ERROR, 14) +#define ECA_CHIDNOTFND DEFMSG(CA_K_INFO, 15) /* defunct */ +#define ECA_CHIDRETRY DEFMSG(CA_K_INFO, 16) /* defunct */ +#define ECA_INTERNAL DEFMSG(CA_K_FATAL, 17) +#define ECA_DBLCLFAIL DEFMSG(CA_K_WARNING, 18) /* defunct */ +#define ECA_GETFAIL DEFMSG(CA_K_WARNING, 19) +#define ECA_PUTFAIL DEFMSG(CA_K_WARNING, 20) +#define ECA_ADDFAIL DEFMSG(CA_K_WARNING, 21) /* defunct */ +#define ECA_BADCOUNT DEFMSG(CA_K_WARNING, 22) +#define ECA_BADSTR DEFMSG(CA_K_ERROR, 23) +#define ECA_DISCONN DEFMSG(CA_K_WARNING, 24) +#define ECA_DBLCHNL DEFMSG(CA_K_WARNING, 25) +#define ECA_EVDISALLOW DEFMSG(CA_K_ERROR, 26) +#define ECA_BUILDGET DEFMSG(CA_K_WARNING, 27) /* defunct */ +#define ECA_NEEDSFP DEFMSG(CA_K_WARNING, 28) /* defunct */ +#define ECA_OVEVFAIL DEFMSG(CA_K_WARNING, 29) /* defunct */ +#define ECA_BADMONID DEFMSG(CA_K_ERROR, 30) +#define ECA_NEWADDR DEFMSG(CA_K_WARNING, 31) /* defunct */ +#define ECA_NEWCONN DEFMSG(CA_K_INFO, 32) /* defunct */ +#define ECA_NOCACTX DEFMSG(CA_K_WARNING, 33) /* defunct */ +#define ECA_DEFUNCT DEFMSG(CA_K_FATAL, 34) /* defunct */ +#define ECA_EMPTYSTR DEFMSG(CA_K_WARNING, 35) /* defunct */ +#define ECA_NOREPEATER DEFMSG(CA_K_WARNING, 36) /* defunct */ +#define ECA_NOCHANMSG DEFMSG(CA_K_WARNING, 37) /* defunct */ +#define ECA_DLCKREST DEFMSG(CA_K_WARNING, 38) /* defunct */ +#define ECA_SERVBEHIND DEFMSG(CA_K_WARNING, 39) /* defunct */ +#define ECA_NOCAST DEFMSG(CA_K_WARNING, 40) /* defunct */ +#define ECA_BADMASK DEFMSG(CA_K_ERROR, 41) +#define ECA_IODONE DEFMSG(CA_K_INFO, 42) +#define ECA_IOINPROGRESS DEFMSG(CA_K_INFO, 43) +#define ECA_BADSYNCGRP DEFMSG(CA_K_ERROR, 44) +#define ECA_PUTCBINPROG DEFMSG(CA_K_ERROR, 45) +#define ECA_NORDACCESS DEFMSG(CA_K_WARNING, 46) +#define ECA_NOWTACCESS DEFMSG(CA_K_WARNING, 47) +#define ECA_ANACHRONISM DEFMSG(CA_K_ERROR, 48) +#define ECA_NOSEARCHADDR DEFMSG(CA_K_WARNING, 49) +#define ECA_NOCONVERT DEFMSG(CA_K_WARNING, 50) +#define ECA_BADCHID DEFMSG(CA_K_ERROR, 51) +#define ECA_BADFUNCPTR DEFMSG(CA_K_ERROR, 52) +#define ECA_ISATTACHED DEFMSG(CA_K_WARNING, 53) +#define ECA_UNAVAILINSERV DEFMSG(CA_K_WARNING, 54) +#define ECA_CHANDESTROY DEFMSG(CA_K_WARNING, 55) +#define ECA_BADPRIORITY DEFMSG(CA_K_ERROR, 56) +#define ECA_NOTTHREADED DEFMSG(CA_K_ERROR, 57) +#define ECA_16KARRAYCLIENT DEFMSG(CA_K_WARNING, 58) +#define ECA_CONNSEQTMO DEFMSG(CA_K_WARNING, 59) +#define ECA_UNRESPTMO DEFMSG(CA_K_WARNING, 60) + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc const char * epicsShareAPI ca_message(long ca_status); + +epicsShareExtern const char * ca_message_text []; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ca/caeventmask.h b/src/ca/caeventmask.h new file mode 100644 index 000000000..9b7c2d31d --- /dev/null +++ b/src/ca/caeventmask.h @@ -0,0 +1,44 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef INCLcaeventmaskh +#define INCLcaeventmaskh + +/* + event selections + (If any more than 8 of these are needed then update the + select field in the event_block struct in db_event.c from + unsigned char to unsigned short) + + + DBE_VALUE + Trigger an event when a significant change in the channel's value + occurs. Relies on the monitor deadband field under DCT. + + DBE_ARCHIVE (DBE_LOG) + Trigger an event when an archive significant change in the channel's + valuue occurs. Relies on the archiver monitor deadband field under DCT. + + DBE_ALARM + Trigger an event when the alarm state changes + + DBE_PROPERTY + Trigger an event when a property change (control limit, graphical + limit, status string, enum string ...) occurs. + +*/ + +#define DBE_VALUE (1<<0) +#define DBE_ARCHIVE (1<<1) +#define DBE_LOG DBE_ARCHIVE +#define DBE_ALARM (1<<2) +#define DBE_PROPERTY (1<<3) + +#endif diff --git a/src/ca/casw.cpp b/src/ca/casw.cpp new file mode 100644 index 000000000..d8334b8ca --- /dev/null +++ b/src/ca/casw.cpp @@ -0,0 +1,306 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "envDefs.h" +#include "errlog.h" +#include "osiWireFormat.h" + +#include "bhe.h" +#include "udpiiu.h" +#include "inetAddrID.h" + +// using a wrapper class around the free list avoids +// Tornado 2.0.1 GNU compiler bugs +class bheFreeStoreMgr : public bheMemoryManager { +public: + bheFreeStoreMgr () {} + void * allocate ( size_t ); + void release ( void * ); +private: + tsFreeList < class bhe, 0x100 > freeList; + bheFreeStoreMgr ( const bheFreeStoreMgr & ); + bheFreeStoreMgr & operator = ( const bheFreeStoreMgr & ); +}; + +void * bheFreeStoreMgr::allocate ( size_t size ) +{ + return freeList.allocate ( size ); +} + +void bheFreeStoreMgr::release ( void * pCadaver ) +{ + freeList.release ( pCadaver ); +} + +int main ( int argc, char ** argv ) +{ + epicsMutex mutex; + epicsGuard < epicsMutex > guard ( mutex ); + bheFreeStoreMgr bheFreeList; + epicsTime programBeginTime = epicsTime::getCurrent (); + bool validCommandLine = false; + unsigned interest = 0u; + SOCKET sock; + osiSockAddr addr; + osiSocklen_t addrSize; + char buf [0x4000]; + const char *pCurBuf; + const caHdr *pCurMsg; + ca_uint16_t serverPort; + ca_uint16_t repeaterPort; + int status; + + if ( argc == 1 ) { + validCommandLine = true; + } + else if ( argc == 2 ) { + status = sscanf ( argv[1], " -i%u ", & interest ); + if ( status == 1 ) { + validCommandLine = true; + } + } + else if ( argc == 3 ) { + if ( strcmp ( argv[1], "-i" ) == 0 ) { + status = sscanf ( argv[2], " %u ", & interest ); + if ( status == 1 ) { + validCommandLine = true; + } + } + } + + if ( ! validCommandLine ) { + printf ( "usage: casw <-i interestLevel>\n" ); + return 0; + } + + serverPort = + envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, + static_cast (CA_SERVER_PORT) ); + + repeaterPort = + envGetInetPortConfigParam ( &EPICS_CA_REPEATER_PORT, + static_cast (CA_REPEATER_PORT) ); + + caStartRepeaterIfNotInstalled ( repeaterPort ); + + sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( sock == INVALID_SOCKET ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("casw: unable to create datagram socket because = \"%s\"\n", + sockErrBuf ); + return -1; + } + + memset ( (char *) &addr, 0 , sizeof (addr) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); + addr.ia.sin_port = htons ( 0 ); // any port + status = bind ( sock, &addr.sa, sizeof (addr) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy ( sock ); + errlogPrintf ( "casw: unable to bind to an unconstrained address because = \"%s\"\n", + sockErrBuf ); + return -1; + } + + osiSockIoctl_t yes = true; + status = socket_ioctl ( sock, FIONBIO, &yes ); // X aCC 392 + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy ( sock ); + errlogPrintf ( "casw: unable to set socket to nonblocking state because \"%s\"\n", + sockErrBuf ); + return -1; + } + + unsigned attemptNumber = 0u; + while ( true ) { + caRepeaterRegistrationMessage ( sock, repeaterPort, attemptNumber ); + epicsThreadSleep ( 0.1 ); + addrSize = ( osiSocklen_t ) sizeof ( addr ); + status = recvfrom ( sock, buf, sizeof ( buf ), 0, + &addr.sa, &addrSize ); + if ( status >= static_cast ( sizeof ( *pCurMsg ) ) ) { + pCurMsg = reinterpret_cast < caHdr * > ( buf ); + epicsUInt16 cmmd = AlignedWireRef < const epicsUInt16 > ( pCurMsg->m_cmmd ); + if ( cmmd == REPEATER_CONFIRM ) { + break; + } + } + + attemptNumber++; + if ( attemptNumber > 100 ) { + epicsSocketDestroy ( sock ); + errlogPrintf ( "casw: unable to register with the CA repeater\n" ); + return -1; + } + } + + osiSockIoctl_t no = false; + status = socket_ioctl ( sock, FIONBIO, &no ); // X aCC 392 + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy ( sock ); + errlogPrintf ( "casw: unable to set socket to blocking state because \"%s\"\n", + sockErrBuf ); + return -1; + } + + resTable < bhe, inetAddrID > beaconTable; + while ( true ) { + + addrSize = ( osiSocklen_t ) sizeof ( addr ); + status = recvfrom ( sock, buf, sizeof ( buf ), 0, + &addr.sa, &addrSize ); + if ( status <= 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy ( sock ); + errlogPrintf ("casw: error from recv was = \"%s\"\n", + sockErrBuf ); + return -1; + } + + if ( addr.sa.sa_family != AF_INET ) { + continue; + } + + unsigned byteCount = static_cast ( status ); + pCurMsg = reinterpret_cast < const caHdr * > ( ( pCurBuf = buf ) ); + while ( byteCount ) { + AlignedWireRef < const epicsUInt16 > pstSize ( pCurMsg->m_postsize ); + size_t msgSize = pstSize + sizeof ( *pCurMsg ) ; + if ( msgSize > byteCount ) { + errlogPrintf ( "CASW: udp input protocol violation\n" ); + break; + } + + epicsUInt16 cmmd = AlignedWireRef < const epicsUInt16 > ( pCurMsg->m_cmmd ); + if ( cmmd == CA_PROTO_RSRV_IS_UP ) { + bool anomaly = false; + epicsTime previousTime; + struct sockaddr_in ina; + + /* + * this allows a fan-out server to potentially + * insert the true address of the CA server + * + * old servers: + * 1) set this field to one of the ip addresses of the host _or_ + * 2) set this field to INADDR_ANY + * new servers: + * always set this field to INADDR_ANY + * + * clients always assume that if this + * field is set to something that isnt INADDR_ANY + * then it is the overriding IP address of the server. + */ + ina.sin_family = AF_INET; + ina.sin_addr.s_addr = pCurMsg->m_available; + + if ( pCurMsg->m_count != 0 ) { + ina.sin_port = pCurMsg->m_count; + } + else { + /* + * old servers dont supply this and the + * default port must be assumed + */ + ina.sin_port = htons ( serverPort ); + } + + ca_uint32_t beaconNumber = ntohl ( pCurMsg->m_cid ); + unsigned protocolRevision = ntohs ( pCurMsg->m_dataType ); + + epicsTime currentTime = epicsTime::getCurrent(); + + /* + * look for it in the hash table + */ + bhe *pBHE = beaconTable.lookup ( ina ); + if ( pBHE ) { + previousTime = pBHE->updateTime ( guard ); + anomaly = pBHE->updatePeriod ( + guard, programBeginTime, + currentTime, beaconNumber, protocolRevision ); + } + else { + /* + * This is the first beacon seen from this server. + * Wait until 2nd beacon is seen before deciding + * if it is a new server (or just the first + * time that we have seen a server's beacon + * shortly after the program started up) + */ + pBHE = new ( bheFreeList ) + bhe ( mutex, currentTime, beaconNumber, ina ); + if ( pBHE ) { + if ( beaconTable.add ( *pBHE ) < 0 ) { + pBHE->~bhe (); + bheFreeList.release ( pBHE ); + } + } + } + if ( anomaly || interest > 1 ) { + char date[64]; + currentTime.strftime ( date, sizeof ( date ), + "%Y-%m-%d %H:%M:%S.%09f"); + char host[64]; + ipAddrToA ( &ina, host, sizeof ( host ) ); + const char * pPrefix = ""; + if ( interest > 1 ) { + if ( anomaly ) { + pPrefix = "* "; + } + else { + pPrefix = " "; + } + } + printf ( "%s%-40s %s\n", + pPrefix, host, date ); + if ( anomaly && interest > 0 ) { + printf ( "\testimate=%f current=%f\n", + pBHE->period ( guard ), + currentTime - previousTime ); + } + fflush(stdout); + } + } + pCurBuf += msgSize; + pCurMsg = reinterpret_cast < const caHdr * > ( pCurBuf ); + byteCount -= msgSize; + } + } +} diff --git a/src/ca/catime.c b/src/ca/catime.c new file mode 100644 index 000000000..cccd940bc --- /dev/null +++ b/src/ca/catime.c @@ -0,0 +1,685 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * CA performance test + * + * History + * joh 09-12-89 Initial release + * joh 12-20-94 portability + * + * + */ + +#include +#include +#include +#include +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "epicsAssert.h" +#include "epicsTime.h" +#include "cadef.h" +#include "caProto.h" + +#include "caDiagnostics.h" + +#ifndef NULL +#define NULL 0 +#endif + +#define WAIT_FOR_ACK + +typedef struct testItem { + chid chix; + char name[128]; + int type; + int count; + void * pValue; +} ti; + +typedef void tf ( ti *pItems, unsigned iterations, unsigned *pInlineIter ); + +/* + * test_pend() + */ +static void test_pend( +ti *pItems, +unsigned iterations, +unsigned *pInlineIter +) +{ + unsigned i; + int status; + + for (i=0; itype, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_put( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + } +#ifdef WAIT_FOR_ACK + status = ca_array_get (DBR_INT, 1, pItems[0].chix, &val); + SEVCHK (status, NULL); + ca_pend_io(100.0); +#endif + status = ca_array_put( + pItems[0].type, + pItems[0].count, + pItems[0].chix, + pItems[0].pValue); + SEVCHK (status, NULL); + status = ca_flush_io(); + SEVCHK (status, NULL); + + *pInlineIter = 10; +} + +/* + * test_get () + */ +static void test_get( +ti *pItems, +unsigned iterations, +unsigned *pInlineIter +) +{ + ti *pi; + int status; + + for (pi=pItems; pi<&pItems[iterations]; pi++) { + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + } + status = ca_pend_io(1e20); + SEVCHK (status, NULL); + + *pInlineIter = 10; +} + +/* + * test_wait () + */ +static void test_wait ( +ti *pItems, +unsigned iterations, +unsigned *pInlineIter +) +{ + ti *pi; + int status; + + for (pi=pItems; pi<&pItems[iterations]; pi++) { + status = ca_array_get( + pi->type, + pi->count, + pi->chix, + pi->pValue); + SEVCHK (status, NULL); + status = ca_pend_io(100.0); + SEVCHK (status, NULL); + } + + *pInlineIter = 1; +} + +/* + * measure_get_latency + */ +static void measure_get_latency (ti *pItems, unsigned iterations) +{ + epicsTimeStamp end_time; + epicsTimeStamp start_time; + double delay; + double X = 0u; + double XX = 0u; + double max = DBL_MIN; + double min = DBL_MAX; + double mean; + double stdDev; + ti *pi; + int status; + + for ( pi = pItems; pi < &pItems[iterations]; pi++ ) { + epicsTimeGetCurrent ( &start_time ); + status = ca_array_get ( pi->type, pi->count, + pi->chix, pi->pValue ); + SEVCHK ( status, NULL ); + status = ca_pend_io ( 100.0 ); + SEVCHK ( status, NULL ); + + epicsTimeGetCurrent ( &end_time ); + + delay = epicsTimeDiffInSeconds ( &end_time,&start_time ); + + X += delay; + XX += delay*delay; + + if ( delay > max ) { + max = delay; + } + + if ( delay < min ) { + min = delay; + } + } + + mean = X/iterations; + stdDev = sqrt ( XX/iterations - mean*mean ); + printf ( + "Get Latency - " + "mean = %3.1f uS, " + "std dev = %3.1f uS, " + "min = %3.1f uS " + "max = %3.1f uS\n", + mean * 1e6, stdDev * 1e6, + min * 1e6, max * 1e6 ); +} + +/* + * printSearchStat() + */ +static void printSearchStat ( const ti * pi, unsigned iterations ) +{ + unsigned i; + double X = 0u; + double XX = 0u; + double max = DBL_MIN; + double min = DBL_MAX; + double mean; + double stdDev; + + for ( i = 0; i < iterations; i++ ) { + double retry = ca_search_attempts ( pi[i].chix ); + X += retry; + XX += retry * retry; + if ( retry > max ) { + max = retry; + } + if ( retry < min ) { + min = retry; + } + } + + mean = X / iterations; + stdDev = sqrt( XX / iterations - mean * mean ); + printf ( + "Search tries per chan - " + "mean = %3.1f " + "std dev = %3.1f " + "min = %3.1f " + "max = %3.1f\n", + mean, stdDev, min, max); +} + +/* + * timeIt () + */ +void timeIt ( tf *pfunc, ti *pItems, unsigned iterations, + unsigned nBytesSent, unsigned nBytesRecv ) +{ + epicsTimeStamp end_time; + epicsTimeStamp start_time; + double delay; + unsigned inlineIter; + + epicsTimeGetCurrent ( &start_time ); + (*pfunc) ( pItems, iterations, &inlineIter ); + epicsTimeGetCurrent ( &end_time ); + delay = epicsTimeDiffInSeconds ( &end_time, &start_time ); + if ( delay > 0.0 ) { + double freq = ( iterations * inlineIter ) / delay; + printf ( "Per Op, %8.4f uS ( %8.4f MHz )", + 1e6 / freq, freq / 1e6 ); + if ( pItems != NULL ) { + printf(", %8.4f snd Mbps, %8.4f rcv Mbps\n", + (inlineIter*nBytesSent*CHAR_BIT)/(delay*1e6), + (inlineIter*nBytesRecv*CHAR_BIT)/(delay*1e6) ); + } + else { + printf ("\n"); + } + } +} + +/* + * test () + */ +static void test ( ti *pItems, unsigned iterations ) +{ + unsigned payloadSize, dblPayloadSize; + unsigned nBytesSent, nBytesRecv; + + payloadSize = + dbr_size_n ( pItems[0].type, pItems[0].count ); + payloadSize = CA_MESSAGE_ALIGN ( payloadSize ); + + dblPayloadSize = dbr_size [ DBR_DOUBLE ]; + dblPayloadSize = CA_MESSAGE_ALIGN ( dblPayloadSize ); + + if ( payloadSize > dblPayloadSize ) { + unsigned factor = payloadSize / dblPayloadSize; + while ( factor ) { + if ( iterations > 10 * factor ) { + iterations /= factor; + break; + } + factor /= 2; + } + } + + printf ( "\t### async put test ###\n"); + nBytesSent = sizeof ( caHdr ) + CA_MESSAGE_ALIGN( payloadSize ); + nBytesRecv = 0u; + timeIt ( test_put, pItems, iterations, + nBytesSent * iterations, + nBytesRecv * iterations ); + + printf ( "\t### async get test ###\n"); + nBytesSent = sizeof ( caHdr ); + nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize ); + timeIt ( test_get, pItems, iterations, + nBytesSent * ( iterations ), + nBytesRecv * ( iterations ) ); + + printf ("\t### synch get test ###\n"); + nBytesSent = sizeof ( caHdr ); + nBytesRecv = sizeof ( caHdr ) + CA_MESSAGE_ALIGN ( payloadSize ); + if ( iterations > 100 ) { + iterations /= 100; + } + else if ( iterations > 10 ) { + iterations /= 10; + } + timeIt ( test_wait, pItems, iterations, + nBytesSent * iterations, + nBytesRecv * iterations ); +} + +/* + * catime () + */ +int catime ( const char * channelName, + unsigned channelCount, enum appendNumberFlag appNF ) +{ + unsigned i; + int j; + unsigned strsize; + unsigned nBytesSent, nBytesRecv; + ti *pItemList; + + if ( channelCount == 0 ) { + printf ( "channel count was zero\n" ); + return 0; + } + + pItemList = calloc ( channelCount, sizeof (ti) ); + if ( ! pItemList ) { + return -1; + } + + SEVCHK ( ca_context_create ( ca_disable_preemptive_callback ), + "Unable to initialize" ); + + if ( appNF == appendNumber ) { + printf ( "Testing with %u channels named %snnn\n", + channelCount, channelName ); + } + else { + printf ( "Testing with %u channels named %s\n", + channelCount, channelName ); + } + + strsize = sizeof ( pItemList[0].name ) - 1; + nBytesSent = 0; + nBytesRecv = 0; + for ( i=0; i < channelCount; i++ ) { + if ( appNF == appendNumber ) { + sprintf ( pItemList[i].name,"%.*s%.6u", + (int) (strsize - 15u), channelName, i ); + } + else { + strncpy ( pItemList[i].name, channelName, strsize); + } + pItemList[i].name[strsize]= '\0'; + pItemList[i].count = 0; + pItemList[i].pValue = 0; + nBytesSent += 2 * ( CA_MESSAGE_ALIGN ( strlen ( pItemList[i].name ) ) + + sizeof (caHdr) ); + nBytesRecv += 2 * sizeof (caHdr); + } + + printf ( "Channel Connect Test\n" ); + printf ( "--------------------\n" ); + timeIt ( test_search, pItemList, channelCount, nBytesSent, nBytesRecv ); + printSearchStat ( pItemList, channelCount ); + + for ( i = 0; i < channelCount; i++ ) { + size_t count = ca_element_count ( pItemList[i].chix ); + size_t size = sizeof ( dbr_string_t ) * count; + pItemList[i].count = count; + pItemList[i].pValue = malloc ( size ); + assert ( pItemList[i].pValue ); + } + + printf ( + "channel name=%s, native type=%d, native count=%u\n", + ca_name (pItemList[0].chix), + ca_field_type (pItemList[0].chix), + pItemList[0].count ); + + printf ("Pend Event Test\n"); + printf ( "----------------\n" ); + timeIt ( test_pend, NULL, 100, 0, 0 ); + + for ( i = 0; i < channelCount; i++ ) { + dbr_float_t * pFltVal = ( dbr_float_t * ) pItemList[i].pValue; + double val = i; + val /= channelCount; + for ( j = 0; j < pItemList[i].count; j++ ) { + pFltVal[j] = (dbr_float_t) val; + } + pItemList[i].type = DBR_FLOAT; + } + printf ( "DBR_FLOAT Test\n" ); + printf ( "--------------\n" ); + test ( pItemList, channelCount ); + + for ( i = 0; i < channelCount; i++ ) { + dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue; + double val = i; + val /= channelCount; + for ( j = 0; j < pItemList[i].count; j++ ) { + pDblVal[j] = (dbr_double_t) val; + } + pItemList[i].type = DBR_DOUBLE; + } + printf ( "DBR_DOUBLE Test\n" ); + printf ( "---------------\n" ); + test ( pItemList, channelCount ); + + + for ( i = 0; i < channelCount; i++ ) { + dbr_string_t * pStrVal = ( dbr_string_t * ) pItemList[i].pValue; + double val = i; + val /= channelCount; + for ( j = 0; j < pItemList[i].count; j++ ) { + sprintf ( pStrVal[j], "%f", val ); + } + pItemList[i].type = DBR_STRING; + } + printf ( "DBR_STRING Test\n" ); + printf ( "---------------\n" ); + test ( pItemList, channelCount ); + + for ( i = 0; i < channelCount; i++ ) { + dbr_int_t * pIntVal = ( dbr_int_t * ) pItemList[i].pValue; + double val = i; + val /= channelCount; + for ( j = 0; j < pItemList[i].count; j++ ) { + pIntVal[j] = (dbr_int_t) val; + } + pItemList[i].type = DBR_INT; + } + printf ( "DBR_INT Test\n" ); + printf ( "------------\n" ); + test ( pItemList, channelCount ); + + printf ( "Get Latency Test\n" ); + printf ( "----------------\n" ); + for ( i = 0; i < channelCount; i++ ) { + dbr_double_t * pDblVal = ( dbr_double_t * ) pItemList[i].pValue; + for ( j = 0; j < pItemList[i].count; j++ ) { + pDblVal[j] = 0; + } + pItemList[i].type = DBR_DOUBLE; + } + measure_get_latency ( pItemList, channelCount ); + + printf ( "Free Channel Test\n" ); + printf ( "-----------------\n" ); + timeIt ( test_free, pItemList, channelCount, 0, 0 ); + + SEVCHK ( ca_task_exit (), "Unable to free resources at exit" ); + + for ( i = 0; i < channelCount; i++ ) { + free ( pItemList[i].pValue ); + } + + free ( pItemList ); + + return CATIME_OK; +} + + + + diff --git a/src/ca/catimeMain.c b/src/ca/catimeMain.c new file mode 100644 index 000000000..598ec453b --- /dev/null +++ b/src/ca/catimeMain.c @@ -0,0 +1,53 @@ +/*************************************************************************\ +* 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 +#include + +#include "caDiagnostics.h" + +static const unsigned defaultIterations = 10000u; + +int main ( int argc, char **argv ) +{ + const char *pUsage = " [ []]"; + + if ( argc > 1 ) { + char *pname = argv[1]; + if ( argc > 2 ) { + int iterations = atoi (argv[2]); + if ( iterations > 0) { + if ( argc > 3 ) { + if ( argc == 4 ) { + int status; + unsigned appendNumberBool; + status = sscanf ( argv[3], " %u ", &appendNumberBool ); + if ( status == 1 ) { + if ( appendNumberBool ) { + return catime ( pname, (unsigned) iterations, appendNumber ); + } + else { + return catime ( pname, (unsigned) iterations, dontAppendNumber ); + } + } + } + } + else { + return catime ( pname, (unsigned) iterations, dontAppendNumber ); + } + } + } + else { + return catime ( pname, defaultIterations, dontAppendNumber ); + } + } + printf ( "usage: %s %s\n", argv[0], pUsage); + return -1; +} diff --git a/src/ca/comBuf.cpp b/src/ca/comBuf.cpp new file mode 100644 index 000000000..94245d387 --- /dev/null +++ b/src/ca/comBuf.cpp @@ -0,0 +1,66 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "comBuf.h" +#include "errlog.h" + +bool comBuf::flushToWire ( wireSendAdapter & wire, const epicsTime & currentTime ) +{ + unsigned index = this->nextReadIndex; + unsigned finalIndex = this->commitIndex; + while ( index < finalIndex ) { + unsigned nBytes = wire.sendBytes ( + &this->buf[index], finalIndex - index, currentTime ); + if ( nBytes == 0u ) { + this->nextReadIndex = index; + return false; + } + index += nBytes; + } + this->nextReadIndex = index; + return true; +} + +// throwing the exception from a function that isnt inline +// shrinks the GNU compiled object code +void comBuf::throwInsufficentBytesException () +{ + throw comBuf::insufficentBytesAvailable (); +} + +void comBuf::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +comBufMemoryManager::~comBufMemoryManager () {} diff --git a/src/ca/comBuf.h b/src/ca/comBuf.h new file mode 100644 index 000000000..2f6c6faa1 --- /dev/null +++ b/src/ca/comBuf.h @@ -0,0 +1,336 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef comBufh +#define comBufh + +#include +#include + +#include "epicsAssert.h" +#include "epicsTypes.h" +#include "tsFreeList.h" +#include "tsDLList.h" +#include "osiWireFormat.h" +#include "compilerDependencies.h" + +static const unsigned comBufSize = 0x4000; + +// this wrapper avoids Tornado 2.0.1 compiler bugs +class comBufMemoryManager { +public: + virtual ~comBufMemoryManager (); + virtual void * allocate ( size_t ) = 0; + virtual void release ( void * ) = 0; +}; + +class wireSendAdapter { // X aCC 655 +public: + virtual unsigned sendBytes ( const void * pBuf, + unsigned nBytesInBuf, + const class epicsTime & currentTime ) = 0; +protected: + virtual ~wireSendAdapter() {} +}; + +enum swioCircuitState { + swioConnected, + swioPeerHangup, + swioPeerAbort, + swioLinkFailure, + swioLocalAbort +}; +struct statusWireIO { + unsigned bytesCopied; + swioCircuitState circuitState; +}; + +class wireRecvAdapter { // X aCC 655 +public: + virtual void recvBytes ( void * pBuf, + unsigned nBytesInBuf, statusWireIO & ) = 0; +protected: + virtual ~wireRecvAdapter() {} +}; + +class comBuf : public tsDLNode < comBuf > { +public: + class insufficentBytesAvailable {}; + comBuf (); + unsigned unoccupiedBytes () const; + unsigned occupiedBytes () const; + unsigned uncommittedBytes () const; + static unsigned capacityBytes (); + void clear (); + unsigned copyInBytes ( const void *pBuf, unsigned nBytes ); + unsigned push ( comBuf & ); + template < class T > + bool push ( const T & value ); + template < class T > + unsigned push ( const T * pValue, unsigned nElem ); + unsigned push ( const epicsInt8 * pValue, unsigned nElem ); + unsigned push ( const epicsUInt8 * pValue, unsigned nElem ); + unsigned push ( const epicsOldString * pValue, unsigned nElem ); + void commitIncomming (); + void clearUncommittedIncomming (); + bool copyInAllBytes ( const void *pBuf, unsigned nBytes ); + unsigned copyOutBytes ( void *pBuf, unsigned nBytes ); + bool copyOutAllBytes ( void *pBuf, unsigned nBytes ); + unsigned removeBytes ( unsigned nBytes ); + bool flushToWire ( wireSendAdapter &, const epicsTime & currentTime ); + void fillFromWire ( wireRecvAdapter &, statusWireIO & ); + struct popStatus { + bool success; + bool nowEmpty; + }; + template < class T > + popStatus pop ( T & ); + static void throwInsufficentBytesException (); + void * operator new ( size_t size, + comBufMemoryManager & ); + epicsPlacementDeleteOperator (( void *, comBufMemoryManager & )) +private: + unsigned commitIndex; + unsigned nextWriteIndex; + unsigned nextReadIndex; + epicsUInt8 buf [ comBufSize ]; + void * operator new ( size_t size ); + void operator delete ( void * ); + template < class T > + bool push ( const T * ); // disabled +}; + +inline void * comBuf::operator new ( size_t size, + comBufMemoryManager & mgr ) +{ + return mgr.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void comBuf::operator delete ( void * pCadaver, + comBufMemoryManager & mgr ) +{ + mgr.release ( pCadaver ); +} +#endif + +inline comBuf::comBuf () : commitIndex ( 0u ), + nextWriteIndex ( 0u ), nextReadIndex ( 0u ) +{ +} + +inline void comBuf :: clear () +{ + this->commitIndex = 0u; + this->nextWriteIndex = 0u; + this->nextReadIndex = 0u; +} + +inline unsigned comBuf :: unoccupiedBytes () const +{ + return sizeof ( this->buf ) - this->nextWriteIndex; +} + +inline unsigned comBuf :: occupiedBytes () const +{ + return this->commitIndex - this->nextReadIndex; +} + +inline unsigned comBuf :: uncommittedBytes () const +{ + return this->nextWriteIndex - this->commitIndex; +} + +inline unsigned comBuf :: push ( comBuf & bufIn ) +{ + unsigned nBytes = this->copyInBytes ( + & bufIn.buf[ bufIn.nextReadIndex ], + bufIn.commitIndex - bufIn.nextReadIndex ); + bufIn.nextReadIndex += nBytes; + return nBytes; +} + +inline unsigned comBuf :: capacityBytes () +{ + return comBufSize; +} + +inline void comBuf :: fillFromWire ( + wireRecvAdapter & wire, statusWireIO & stat ) +{ + wire.recvBytes ( + & this->buf[this->nextWriteIndex], + sizeof ( this->buf ) - this->nextWriteIndex, stat ); + if ( stat.circuitState == swioConnected ) { + this->nextWriteIndex += stat.bytesCopied; + } +} + +template < class T > +inline bool comBuf :: push ( const T & value ) +{ + unsigned index = this->nextWriteIndex; + unsigned available = sizeof ( this->buf ) - index; + if ( sizeof ( value ) > available ) { + return false; + } + WireSet ( value, & this->buf[index] ); + this->nextWriteIndex = index + sizeof ( value ); + return true; +} + +inline unsigned comBuf :: push ( const epicsInt8 *pValue, unsigned nElem ) +{ + return copyInBytes ( pValue, nElem ); +} + +inline unsigned comBuf :: push ( const epicsUInt8 *pValue, unsigned nElem ) +{ + return copyInBytes ( pValue, nElem ); +} + +inline unsigned comBuf :: push ( const epicsOldString * pValue, unsigned nElem ) +{ + unsigned index = this->nextWriteIndex; + unsigned available = sizeof ( this->buf ) - index; + unsigned nBytes = sizeof ( *pValue ) * nElem; + if ( nBytes > available ) { + nElem = available / sizeof ( *pValue ); + nBytes = nElem * sizeof ( *pValue ); + } + memcpy ( &this->buf[ index ], pValue, nBytes ); + this->nextWriteIndex = index + nBytes; + return nElem; +} + +template < class T > +unsigned comBuf :: push ( const T * pValue, unsigned nElem ) +{ + unsigned index = this->nextWriteIndex; + unsigned available = sizeof ( this->buf ) - index; + unsigned nBytes = sizeof ( *pValue ) * nElem; + if ( nBytes > available ) { + nElem = available / sizeof ( *pValue ); + } + for ( unsigned i = 0u; i < nElem; i++ ) { + // allow native floating point formats to be converted to IEEE + WireSet( pValue[i], &this->buf[index] ); + index += sizeof ( *pValue ); + } + this->nextWriteIndex = index; + return nElem; +} + +inline void comBuf :: commitIncomming () +{ + this->commitIndex = this->nextWriteIndex; +} + +inline void comBuf :: clearUncommittedIncomming () +{ + this->nextWriteIndex = this->commitIndex; +} + +inline bool comBuf :: copyInAllBytes ( const void *pBuf, unsigned nBytes ) +{ + unsigned index = this->nextWriteIndex; + unsigned available = sizeof ( this->buf ) - index; + if ( nBytes <= available ) { + memcpy ( & this->buf[index], pBuf, nBytes ); + this->nextWriteIndex = index + nBytes; + return true; + } + return false; +} + +inline unsigned comBuf :: copyInBytes ( const void * pBuf, unsigned nBytes ) +{ + unsigned index = this->nextWriteIndex; + unsigned available = sizeof ( this->buf ) - index; + if ( nBytes > available ) { + nBytes = available; + } + memcpy ( & this->buf[index], pBuf, nBytes ); + this->nextWriteIndex = index + nBytes; + return nBytes; +} + +inline bool comBuf :: copyOutAllBytes ( void * pBuf, unsigned nBytes ) +{ + unsigned index = this->nextReadIndex; + unsigned occupied = this->commitIndex - index; + if ( nBytes <= occupied ) { + memcpy ( pBuf, &this->buf[index], nBytes); + this->nextReadIndex = index + nBytes; + return true; + } + return false; +} + +inline unsigned comBuf :: copyOutBytes ( void *pBuf, unsigned nBytes ) +{ + unsigned index = this->nextReadIndex; + unsigned occupied = this->commitIndex - index; + if ( nBytes > occupied ) { + nBytes = occupied; + } + memcpy ( pBuf, &this->buf[index], nBytes); + this->nextReadIndex = index + nBytes; + return nBytes; +} + +inline unsigned comBuf :: removeBytes ( unsigned nBytes ) +{ + unsigned index = this->nextReadIndex; + unsigned occupied = this->commitIndex - index; + if ( nBytes > occupied ) { + nBytes = occupied; + } + this->nextReadIndex = index + nBytes; + return nBytes; +} + +template < class T > +comBuf :: popStatus comBuf :: pop ( T & returnVal ) +{ + unsigned nrIndex = this->nextReadIndex; + unsigned popIndex = nrIndex + sizeof ( returnVal ); + unsigned cIndex = this->commitIndex; + popStatus status; + status.success = true; + status.nowEmpty = false; + if ( popIndex >= cIndex ) { + if ( popIndex == cIndex ) { + status.nowEmpty = true; + } + else { + status.success = false; + return status; + } + } + WireGet ( & this->buf[ nrIndex ], returnVal ); + this->nextReadIndex = popIndex; + return status; +} + +#endif // ifndef comBufh diff --git a/src/ca/comQueRecv.cpp b/src/ca/comQueRecv.cpp new file mode 100644 index 000000000..db797fd65 --- /dev/null +++ b/src/ca/comQueRecv.cpp @@ -0,0 +1,269 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "iocinf.h" +#include "virtualCircuit.h" + +comQueRecv::comQueRecv ( comBufMemoryManager & comBufMemoryManagerIn ): + comBufMemMgr ( comBufMemoryManagerIn ), nBytesPending ( 0u ) +{ +} + +comQueRecv::~comQueRecv () +{ + this->clear (); +} + +void comQueRecv::clear () +{ + comBuf *pBuf; + while ( ( pBuf = this->bufs.get () ) ) { + pBuf->~comBuf (); + this->comBufMemMgr.release ( pBuf ); + } + this->nBytesPending = 0u; +} + +unsigned comQueRecv::copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes ) +{ + unsigned totalBytes = 0u; + do { + comBuf * pComBuf = this->bufs.first (); + if ( ! pComBuf ) { + this->nBytesPending -= totalBytes; + return totalBytes; + } + totalBytes += pComBuf->copyOutBytes ( &pBuf[totalBytes], nBytes - totalBytes ); + if ( pComBuf->occupiedBytes () == 0u ) { + this->bufs.remove ( *pComBuf ); + pComBuf->~comBuf (); + this->comBufMemMgr.release ( pComBuf ); + } + } + while ( totalBytes < nBytes ); + this->nBytesPending -= totalBytes; + return totalBytes; +} + +unsigned comQueRecv::removeBytes ( unsigned nBytes ) +{ + unsigned totalBytes = 0u; + unsigned bytesLeft = nBytes; + while ( bytesLeft ) { + comBuf * pComBuf = this->bufs.first (); + if ( ! pComBuf ) { + this->nBytesPending -= totalBytes; + return totalBytes; + } + unsigned nBytesThisTime = pComBuf->removeBytes ( bytesLeft ); + if ( pComBuf->occupiedBytes () == 0u ) { + this->bufs.remove ( *pComBuf ); + pComBuf->~comBuf (); + this->comBufMemMgr.release ( pComBuf ); + } + if ( nBytesThisTime == 0u) { + break; + } + totalBytes += nBytesThisTime; + bytesLeft = nBytes - totalBytes; + } + this->nBytesPending -= totalBytes; + return totalBytes; +} + +void comQueRecv::popString ( epicsOldString *pStr ) +{ + for ( unsigned i = 0u; i < sizeof ( *pStr ); i++ ) { + pStr[0][i] = this->popInt8 (); + } +} + +void comQueRecv::pushLastComBufReceived ( comBuf & bufIn ) + +{ + bufIn.commitIncomming (); + comBuf * pComBuf = this->bufs.last (); + if ( pComBuf ) { + if ( pComBuf->unoccupiedBytes() ) { + this->nBytesPending += pComBuf->push ( bufIn ); + pComBuf->commitIncomming (); + } + } + unsigned bufBytes = bufIn.occupiedBytes(); + if ( bufBytes ) { + this->nBytesPending += bufBytes; + this->bufs.add ( bufIn ); + } + else { + bufIn.~comBuf (); + this->comBufMemMgr.release ( & bufIn ); + } +} + +// 1) split between buffers expected to run slower +// 2) using canonical unsigned tmp avoids ANSI C conversions to int +// 3) cast required because sizeof(unsigned) >= sizeof(epicsUInt32) +epicsUInt16 comQueRecv::multiBufferPopUInt16 () +{ + epicsUInt16 tmp; + if ( this->occupiedBytes() >= sizeof (tmp) ) { + unsigned byte1 = this->popUInt8 (); + unsigned byte2 = this->popUInt8 (); + tmp = static_cast ( ( byte1 << 8u ) | byte2 ); + } + else { + comBuf::throwInsufficentBytesException (); + tmp = 0u; + } + return tmp; +} + +// 1) split between buffers expected to run slower +// 2) using canonical unsigned temporary avoids ANSI C conversions to int +// 3) cast required because sizeof(unsigned) >= sizeof(epicsUInt32) +epicsUInt32 comQueRecv::multiBufferPopUInt32 () +{ + epicsUInt32 tmp; + if ( this->occupiedBytes() >= sizeof (tmp) ) { + // 1) split between buffers expected to run slower + // 2) using canonical unsigned temporary avoids ANSI C conversions to int + // 3) cast required because sizeof(unsigned) >= sizeof(epicsUInt32) + unsigned byte1 = this->popUInt8(); + unsigned byte2 = this->popUInt8(); + unsigned byte3 = this->popUInt8(); + unsigned byte4 = this->popUInt8(); + tmp = static_cast + ( ( byte1 << 24u ) | ( byte2 << 16u ) | //X aCC 392 + ( byte3 << 8u ) | byte4 ); + } + else { + comBuf::throwInsufficentBytesException (); + tmp = 0u; // avoid compiler warnings + } + return tmp; +} + +void comQueRecv::removeAndDestroyBuf ( comBuf & buf ) +{ + this->bufs.remove ( buf ); + buf.~comBuf (); + this->comBufMemMgr.release ( & buf ); +} + +epicsUInt8 comQueRecv::popUInt8 () +{ + comBuf * pComBuf = this->bufs.first (); + if ( ! pComBuf ) { + comBuf::throwInsufficentBytesException (); + } + epicsUInt8 tmp = '\0'; + comBuf::popStatus status = pComBuf->pop ( tmp ); + if ( ! status.success ) { + comBuf::throwInsufficentBytesException (); + } + if ( status.nowEmpty ) { + this->removeAndDestroyBuf ( *pComBuf ); + } + this->nBytesPending--; + return tmp; +} + +epicsUInt16 comQueRecv::popUInt16 () +{ + comBuf * pComBuf = this->bufs.first (); + if ( ! pComBuf ) { + comBuf::throwInsufficentBytesException (); + } + // try first for all in one buffer efficent version + epicsUInt16 tmp = 0; + comBuf::popStatus status = pComBuf->pop ( tmp ); + if ( status.success ) { + this->nBytesPending -= sizeof ( epicsUInt16 ); + if ( status.nowEmpty ) { + this->removeAndDestroyBuf ( *pComBuf ); + } + return tmp; + } + return this->multiBufferPopUInt16 (); +} + +epicsUInt32 comQueRecv::popUInt32 () +{ + comBuf *pComBuf = this->bufs.first (); + if ( ! pComBuf ) { + comBuf::throwInsufficentBytesException (); + } + // try first for all in one buffer efficent version + epicsUInt32 tmp = 0; + comBuf::popStatus status = pComBuf->pop ( tmp ); + if ( status.success ) { + this->nBytesPending -= sizeof ( epicsUInt32 ); + if ( status.nowEmpty ) { + this->removeAndDestroyBuf ( *pComBuf ); + } + return tmp; + } + return this->multiBufferPopUInt32 (); +} + +bool comQueRecv::popOldMsgHeader ( caHdrLargeArray & msg ) +{ + // try first for all in one buffer efficent version + comBuf * pComBuf = this->bufs.first (); + if ( ! pComBuf ) { + return false; + } + unsigned avail = pComBuf->occupiedBytes (); + if ( avail >= sizeof ( caHdr ) ) { + pComBuf->pop ( msg.m_cmmd ); + ca_uint16_t smallPostsize = 0; + pComBuf->pop ( smallPostsize ); + msg.m_postsize = smallPostsize; + pComBuf->pop ( msg.m_dataType ); + ca_uint16_t smallCount = 0; + pComBuf->pop ( smallCount ); + msg.m_count = smallCount; + pComBuf->pop ( msg.m_cid ); + pComBuf->pop ( msg.m_available ); + this->nBytesPending -= sizeof ( caHdr ); + if ( avail == sizeof ( caHdr ) ) { + this->removeAndDestroyBuf ( *pComBuf ); + } + return true; + } + else if ( this->occupiedBytes () >= sizeof ( caHdr ) ) { + msg.m_cmmd = this->popUInt16 (); + msg.m_postsize = this->popUInt16 (); + msg.m_dataType = this->popUInt16 (); + msg.m_count = this->popUInt16 (); + msg.m_cid = this->popUInt32 (); + msg.m_available = this->popUInt32 (); + return true; + } + else { + return false; + } +} + diff --git a/src/ca/comQueRecv.h b/src/ca/comQueRecv.h new file mode 100644 index 000000000..7f3153d8d --- /dev/null +++ b/src/ca/comQueRecv.h @@ -0,0 +1,111 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef comQueRecvh +#define comQueRecvh + +#include "comBuf.h" + +class comQueRecv { +public: + comQueRecv ( comBufMemoryManager & ); + ~comQueRecv (); + unsigned occupiedBytes () const; + unsigned copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes ); + unsigned removeBytes ( unsigned nBytes ); + void pushLastComBufReceived ( comBuf & ); + void clear (); + bool popOldMsgHeader ( struct caHdrLargeArray & ); + epicsInt8 popInt8 (); + epicsUInt8 popUInt8 (); + epicsInt16 popInt16 (); + epicsUInt16 popUInt16 (); + epicsInt32 popInt32 (); + epicsUInt32 popUInt32 (); + epicsFloat32 popFloat32 (); + epicsFloat64 popFloat64 (); + void popString ( epicsOldString * ); +private: + tsDLList < comBuf > bufs; + comBufMemoryManager & comBufMemMgr; + unsigned nBytesPending; + epicsUInt16 multiBufferPopUInt16 (); + epicsUInt32 multiBufferPopUInt32 (); + void removeAndDestroyBuf ( comBuf & ); + comQueRecv ( const comQueRecv & ); + comQueRecv & operator = ( const comQueRecv & ); +}; + +inline unsigned comQueRecv::occupiedBytes () const +{ + return this->nBytesPending; +} + +inline epicsInt8 comQueRecv::popInt8 () +{ + return static_cast < epicsInt8 > ( this->popUInt8() ); +} + +inline epicsInt16 comQueRecv::popInt16 () +{ + return static_cast < epicsInt16 > ( this->popUInt16() ); +} + +inline epicsInt32 comQueRecv::popInt32 () +{ + return static_cast < epicsInt32 > ( this->popUInt32() ); +} + +// this has been optimized to aligned convert, maybe more could be done, +// but since it is currently not used ... +inline epicsFloat32 comQueRecv::popFloat32 () +{ + union { + epicsUInt8 _wire[ sizeof ( epicsFloat32 ) ]; + epicsFloat32 _fp; + } tmp; + // optimizer will unroll this loop + for ( unsigned i = 0u; i < sizeof ( tmp._wire ); i++ ) { + tmp._wire[i] = this->popUInt8 (); + } + return AlignedWireRef < epicsFloat32 > ( tmp._fp ); +} + +// this has been optimized to aligned convert, maybe more could be done, +// but since it is currently not used ... +inline epicsFloat64 comQueRecv::popFloat64 () +{ + union { + epicsUInt8 _wire[ sizeof ( epicsFloat64 ) ]; + epicsFloat64 _fp; + } tmp; + // optimizer will unroll this loop + for ( unsigned i = 0u; i < sizeof ( tmp._wire ); i++ ) { + tmp._wire[i] = this->popUInt8 (); + } + return AlignedWireRef < epicsFloat64 > ( tmp._fp ); +} + +#endif // ifndef comQueRecvh diff --git a/src/ca/comQueSend.cpp b/src/ca/comQueSend.cpp new file mode 100644 index 000000000..5ae150be2 --- /dev/null +++ b/src/ca/comQueSend.cpp @@ -0,0 +1,411 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +// +// Requirements: +// 1) Allow sufficent headroom so that users will be able to perform +// a reasonable amount of IO within CA callbacks without experiencing +// a push/pull deadlock. If a potential push/pull deadlock situation +// occurs then detect and avoid it and provide diagnotic to the user +// via special status. +// 2) Return status to the user when there is insufficent memory to +// queue a complete message. +// 3) return status to the user when a message cant be flushed because +// a connection dropped. +// 4) Do not allocate too much memory in exception situatons (such as +// after a circuit disconnect). +// 5) Avoid allocating more memory than is absolutely necessary to meet +// the above requirements. +// 6) Message fragments must never be sent to the IOC when there isnt +// enough memory to queue part of a message (we also must not force +// a disconnect because the client is starved for memory). +// 7) avoid the need to check status for each byte pushed into the +// protocol stream. +// +// Implementation: +// 1) When queuing a complete message, first test to see if a flush is +// required. If it is a receive thread scheduals the flush with the +// send thread, and otherwise directly execute the system call. The +// send thread must run at a higher priority than the receive thread +// if we are to minimize memory consumption. +// 2) Preallocate space for the entire message prior to copying in the +// message so that message fragments are not flushed out just prior +// to detecting that memory is unavailable. +// 3) Return a special error constant when the following situations +// are detected when the user is attempting to queue a request +// from within a user callback executed by a receive thread: +// a) A user is queuing more requests that demand a response from a +// callback than are removed by the response that initiated the +// callback, and this situation persists for many callbacks until +// all buffering in the system is exausted. +// b) A user is queuing many requests that demand a response from one +// callback until all buffering in the system is exausted. +// c) Some combination of both (a) nad (b). +// +// + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "virtualCircuit.h" +#include "db_access.h" // for dbr_short_t etc + +// nill message alignment pad bytes +const char cacNillBytes [] = +{ + 0, 0, 0, 0, + 0, 0, 0, 0 +}; + +comQueSend::comQueSend ( wireSendAdapter & wireIn, + comBufMemoryManager & comBufMemMgrIn ): + comBufMemMgr ( comBufMemMgrIn ), wire ( wireIn ), + nBytesPending ( 0u ) +{ +} + +comQueSend::~comQueSend () +{ + this->clear (); +} + +void comQueSend::clear () +{ + comBuf *pBuf; + + while ( ( pBuf = this->bufs.get () ) ) { + this->nBytesPending -= pBuf->occupiedBytes (); + pBuf->~comBuf (); + this->comBufMemMgr.release ( pBuf ); + } + this->pFirstUncommited = tsDLIter < comBuf > (); + assert ( this->nBytesPending == 0 ); +} + +void comQueSend::copy_dbr_string ( const void * pValue ) +{ + this->push ( static_cast < const char * > ( pValue ), MAX_STRING_SIZE ); +} + +void comQueSend::copy_dbr_short ( const void * pValue ) +{ + this->push ( * static_cast ( pValue ) ); +} + +void comQueSend::copy_dbr_float ( const void * pValue ) +{ + this->push ( * static_cast ( pValue ) ); +} + +void comQueSend::copy_dbr_char ( const void * pValue ) +{ + this->push ( * static_cast ( pValue ) ); +} + +void comQueSend::copy_dbr_long ( const void * pValue ) +{ + this->push ( * static_cast ( pValue ) ); +} + +void comQueSend::copy_dbr_double ( const void * pValue ) +{ + this->push ( * static_cast ( pValue ) ); +} + +void comQueSend::copy_dbr_invalid ( const void * ) +{ + throw cacChannel::badType (); +} + +const comQueSend::copyScalarFunc_t comQueSend::dbrCopyScalar [39] = { + &comQueSend::copy_dbr_string, + &comQueSend::copy_dbr_short, + &comQueSend::copy_dbr_float, + &comQueSend::copy_dbr_short, // DBR_ENUM + &comQueSend::copy_dbr_char, + &comQueSend::copy_dbr_long, + &comQueSend::copy_dbr_double, + &comQueSend::copy_dbr_invalid, // DBR_STS_SHORT + &comQueSend::copy_dbr_invalid, // DBR_STS_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_STS_ENUM + &comQueSend::copy_dbr_invalid, // DBR_STS_CHAR + &comQueSend::copy_dbr_invalid, // DBR_STS_LONG + &comQueSend::copy_dbr_invalid, // DBR_STS_DOUBLE + &comQueSend::copy_dbr_invalid, // DBR_TIME_STRING + &comQueSend::copy_dbr_invalid, // DBR_TIME_INT + &comQueSend::copy_dbr_invalid, // DBR_TIME_SHORT + &comQueSend::copy_dbr_invalid, // DBR_TIME_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_TIME_ENUM + &comQueSend::copy_dbr_invalid, // DBR_TIME_CHAR + &comQueSend::copy_dbr_invalid, // DBR_TIME_LONG + &comQueSend::copy_dbr_invalid, // DBR_TIME_DOUBLE + &comQueSend::copy_dbr_invalid, // DBR_GR_STRING + &comQueSend::copy_dbr_invalid, // DBR_GR_SHORT + &comQueSend::copy_dbr_invalid, // DBR_GR_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_GR_ENUM + &comQueSend::copy_dbr_invalid, // DBR_GR_CHAR + &comQueSend::copy_dbr_invalid, // DBR_GR_LONG + &comQueSend::copy_dbr_invalid, // DBR_GR_DOUBLE + &comQueSend::copy_dbr_invalid, // DBR_CTRL_STRING + &comQueSend::copy_dbr_invalid, // DBR_CTRL_SHORT + &comQueSend::copy_dbr_invalid, // DBR_CTRL_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_CTRL_ENUM + &comQueSend::copy_dbr_invalid, // DBR_CTRL_CHAR + &comQueSend::copy_dbr_invalid, // DBR_CTRL_LONG + &comQueSend::copy_dbr_invalid, // DBR_CTRL_DOUBLE + &comQueSend::copy_dbr_short, // DBR_PUT_ACKT + &comQueSend::copy_dbr_short, // DBR_PUT_ACKS + &comQueSend::copy_dbr_invalid, // DBR_STSACK_STRING + &comQueSend::copy_dbr_invalid // DBR_CLASS_NAME +}; + +void comQueSend::copy_dbr_string ( const void *pValue, unsigned nElem ) +{ + this->push ( static_cast < const char * > ( pValue ), nElem * MAX_STRING_SIZE ); +} + +void comQueSend::copy_dbr_short ( const void *pValue, unsigned nElem ) +{ + this->push ( static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_float ( const void *pValue, unsigned nElem ) +{ + this->push ( static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_char ( const void *pValue, unsigned nElem ) +{ + this->push ( static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_long ( const void *pValue, unsigned nElem ) +{ + this->push ( static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_double ( const void *pValue, unsigned nElem ) +{ + this->push ( static_cast ( pValue ), nElem ); +} + +void comQueSend::copy_dbr_invalid ( const void *, unsigned ) +{ + throw cacChannel::badType (); +} + +const comQueSend::copyVectorFunc_t comQueSend::dbrCopyVector [39] = { + &comQueSend::copy_dbr_string, + &comQueSend::copy_dbr_short, + &comQueSend::copy_dbr_float, + &comQueSend::copy_dbr_short, // DBR_ENUM + &comQueSend::copy_dbr_char, + &comQueSend::copy_dbr_long, + &comQueSend::copy_dbr_double, + &comQueSend::copy_dbr_invalid, // DBR_STS_SHORT + &comQueSend::copy_dbr_invalid, // DBR_STS_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_STS_ENUM + &comQueSend::copy_dbr_invalid, // DBR_STS_CHAR + &comQueSend::copy_dbr_invalid, // DBR_STS_LONG + &comQueSend::copy_dbr_invalid, // DBR_STS_DOUBLE + &comQueSend::copy_dbr_invalid, // DBR_TIME_STRING + &comQueSend::copy_dbr_invalid, // DBR_TIME_INT + &comQueSend::copy_dbr_invalid, // DBR_TIME_SHORT + &comQueSend::copy_dbr_invalid, // DBR_TIME_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_TIME_ENUM + &comQueSend::copy_dbr_invalid, // DBR_TIME_CHAR + &comQueSend::copy_dbr_invalid, // DBR_TIME_LONG + &comQueSend::copy_dbr_invalid, // DBR_TIME_DOUBLE + &comQueSend::copy_dbr_invalid, // DBR_GR_STRING + &comQueSend::copy_dbr_invalid, // DBR_GR_SHORT + &comQueSend::copy_dbr_invalid, // DBR_GR_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_GR_ENUM + &comQueSend::copy_dbr_invalid, // DBR_GR_CHAR + &comQueSend::copy_dbr_invalid, // DBR_GR_LONG + &comQueSend::copy_dbr_invalid, // DBR_GR_DOUBLE + &comQueSend::copy_dbr_invalid, // DBR_CTRL_STRING + &comQueSend::copy_dbr_invalid, // DBR_CTRL_SHORT + &comQueSend::copy_dbr_invalid, // DBR_CTRL_FLOAT + &comQueSend::copy_dbr_invalid, // DBR_CTRL_ENUM + &comQueSend::copy_dbr_invalid, // DBR_CTRL_CHAR + &comQueSend::copy_dbr_invalid, // DBR_CTRL_LONG + &comQueSend::copy_dbr_invalid, // DBR_CTRL_DOUBLE + &comQueSend::copy_dbr_short, // DBR_PUT_ACKT + &comQueSend::copy_dbr_short, // DBR_PUT_ACKS + &comQueSend::copy_dbr_invalid, // DBR_STSACK_STRING + &comQueSend::copy_dbr_invalid // DBR_CLASS_NAME +}; + +comBuf * comQueSend::popNextComBufToSend () +{ + comBuf *pBuf = this->bufs.get (); + if ( pBuf ) { + unsigned nBytesThisBuf = pBuf->occupiedBytes (); + if ( nBytesThisBuf ) { + assert ( this->nBytesPending >= nBytesThisBuf ); + this->nBytesPending -= nBytesThisBuf; + } + else { + this->bufs.push ( *pBuf ); + pBuf = 0; + } + } + else { + assert ( this->nBytesPending == 0u ); + } + return pBuf; +} + +void comQueSend::insertRequestHeader ( + ca_uint16_t request, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t requestDependent, bool v49Ok ) +{ + if ( payloadSize < 0xffff && nElem < 0xffff ) { + comBuf * pComBuf = this->bufs.last (); + if ( ! pComBuf || pComBuf->unoccupiedBytes() < 16u ) { + pComBuf = newComBuf (); + this->pushComBuf ( *pComBuf ); + } + pComBuf->push ( request ); + pComBuf->push ( static_cast < ca_uint16_t > ( payloadSize ) ); + pComBuf->push ( dataType ); + pComBuf->push ( static_cast < ca_uint16_t > ( nElem ) ); + pComBuf->push ( cid ); + pComBuf->push ( requestDependent ); + } + else if ( v49Ok ) { + comBuf * pComBuf = this->bufs.last (); + if ( ! pComBuf || pComBuf->unoccupiedBytes() < 24u ) { + pComBuf = newComBuf (); + this->pushComBuf ( *pComBuf ); + } + pComBuf->push ( request ); + pComBuf->push ( static_cast < ca_uint16_t > ( 0xffff ) ); + pComBuf->push ( dataType ); + pComBuf->push ( static_cast < ca_uint16_t > ( 0u ) ); + pComBuf->push ( cid ); + pComBuf->push ( requestDependent ); + pComBuf->push ( payloadSize ); + pComBuf->push ( nElem ); + } + else { + throw cacChannel::outOfBounds (); + } +} + +void comQueSend::insertRequestWithPayLoad ( + ca_uint16_t request, unsigned dataType, arrayElementCount nElem, + ca_uint32_t cid, ca_uint32_t requestDependent, + const void * pPayload, bool v49Ok ) +{ + if ( INVALID_DB_REQ ( dataType ) ) { + throw cacChannel::badType (); + } + if ( dataType >= comQueSendCopyDispatchSize ) { + throw cacChannel::badType(); + } + ca_uint32_t size = 0u; + ca_uint32_t payloadSize = 0u; + if ( nElem == 1 ) { + if ( dataType == DBR_STRING ) { + const char * pStr = static_cast < const char * > ( pPayload ); + size = strlen ( pStr ) + 1u; + if ( size > MAX_STRING_SIZE ) { + throw cacChannel::outOfBounds(); + } + payloadSize = CA_MESSAGE_ALIGN ( size ); + this->insertRequestHeader ( request, payloadSize, + static_cast ( dataType ), + nElem, cid, requestDependent, v49Ok ); + this->pushString ( pStr, size ); + } + else { + size = dbr_size[dataType]; + payloadSize = CA_MESSAGE_ALIGN ( size ); + this->insertRequestHeader ( request, payloadSize, + static_cast ( dataType ), + nElem, cid, requestDependent, v49Ok ); + ( this->*dbrCopyScalar [dataType] ) ( pPayload ); + } + } + else { + arrayElementCount maxBytes; + if ( v49Ok ) { + maxBytes = 0xffffffff; + } + else { + maxBytes = MAX_TCP - sizeof ( caHdr ); + } + arrayElementCount maxElem = + ( maxBytes - sizeof (dbr_double_t) - dbr_size[dataType] ) / + dbr_value_size[dataType]; + if ( nElem >= maxElem ) { + throw cacChannel::outOfBounds(); + } + // the above checks verify that the total size + // is lest that 0xffffffff + size = static_cast < ca_uint32_t > + ( dbr_size_n ( dataType, nElem ) ); // X aCC 392 + payloadSize = CA_MESSAGE_ALIGN ( size ); + this->insertRequestHeader ( request, payloadSize, + static_cast ( dataType ), + static_cast < ca_uint32_t > ( nElem ), + cid, requestDependent, v49Ok ); + ( this->*dbrCopyVector [dataType] ) ( pPayload, nElem ); + } + // set pad bytes to nill + unsigned padSize = payloadSize - size; + if ( padSize ) { + this->pushString ( cacNillBytes, payloadSize - size ); + } +} + +void comQueSend::commitMsg () +{ + while ( this->pFirstUncommited.valid() ) { + this->nBytesPending += this->pFirstUncommited->uncommittedBytes (); + this->pFirstUncommited->commitIncomming (); + this->pFirstUncommited++; + } + // printf ( "NBP: %u\n", this->nBytesPending ); +} + + +void comQueSend::clearUncommitedMsg () +{ + while ( this->pFirstUncommited.valid() ) { + tsDLIter < comBuf > next = this->pFirstUncommited; + next++; + this->pFirstUncommited->clearUncommittedIncomming (); + if ( this->pFirstUncommited->occupiedBytes() == 0u ) { + this->bufs.remove ( *this->pFirstUncommited ); + this->pFirstUncommited->~comBuf (); + this->comBufMemMgr.release ( this->pFirstUncommited.pointer() ); + } + this->pFirstUncommited = next; + } +} + diff --git a/src/ca/comQueSend.h b/src/ca/comQueSend.h new file mode 100644 index 000000000..808285e7a --- /dev/null +++ b/src/ca/comQueSend.h @@ -0,0 +1,238 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef comQueSendh +#define comQueSendh + +#include + +#include "tsDLList.h" +#include "comBuf.h" + +#define comQueSendCopyDispatchSize 39 + +class epicsMutex; +template < class T > class epicsGuard; + +class comQueSendMsgMinder { +public: + comQueSendMsgMinder ( + class comQueSend &, epicsGuard < epicsMutex > & ); + ~comQueSendMsgMinder (); + void commit (); +private: + class comQueSend * pSendQue; +}; + +// +// Notes. +// o calling popNextComBufToSend() will clear any uncommitted bytes +// +class comQueSend { +public: + comQueSend ( wireSendAdapter &, comBufMemoryManager & ); + ~comQueSend (); + void clear (); + unsigned occupiedBytes () const; + bool flushEarlyThreshold ( unsigned nBytesThisMsg ) const; + bool flushBlockThreshold () const; + void pushUInt16 ( const ca_uint16_t value ); + void pushUInt32 ( const ca_uint32_t value ); + void pushFloat32 ( const ca_float32_t value ); + void pushString ( const char *pVal, unsigned nChar ); + void insertRequestHeader ( + ca_uint16_t request, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t requestDependent, bool v49Ok ); + void insertRequestWithPayLoad ( + ca_uint16_t request, unsigned dataType, arrayElementCount nElem, + ca_uint32_t cid, ca_uint32_t requestDependent, + const void * pPayload, bool v49Ok ); + comBuf * popNextComBufToSend (); +private: + comBufMemoryManager & comBufMemMgr; + tsDLList < comBuf > bufs; + tsDLIter < comBuf > pFirstUncommited; + wireSendAdapter & wire; + unsigned nBytesPending; + + typedef void ( comQueSend::*copyScalarFunc_t ) ( + const void * pValue ); + static const copyScalarFunc_t dbrCopyScalar [comQueSendCopyDispatchSize]; + void copy_dbr_string ( const void * pValue ); + void copy_dbr_short ( const void * pValue ); + void copy_dbr_float ( const void * pValue ); + void copy_dbr_char ( const void * pValue ); + void copy_dbr_long ( const void * pValue ); + void copy_dbr_double ( const void * pValue ); + void copy_dbr_invalid ( const void * pValue ); + + typedef void ( comQueSend::*copyVectorFunc_t ) ( + const void * pValue, unsigned nElem ); + static const copyVectorFunc_t dbrCopyVector [comQueSendCopyDispatchSize]; + void copy_dbr_string ( const void *pValue, unsigned nElem ); + void copy_dbr_short ( const void *pValue, unsigned nElem ); + void copy_dbr_float ( const void *pValue, unsigned nElem ); + void copy_dbr_char ( const void *pValue, unsigned nElem ); + void copy_dbr_long ( const void *pValue, unsigned nElem ); + void copy_dbr_double ( const void *pValue, unsigned nElem ); + void copy_dbr_invalid ( const void * pValue, unsigned nElem ); + + void pushComBuf ( comBuf & ); + comBuf * newComBuf (); + + void beginMsg (); + void commitMsg (); + void clearUncommitedMsg (); + + friend class comQueSendMsgMinder; + + // + // visual C++ versions 6 & 7 do not allow out of + // class member template function definition + // + template < class T > + inline void push ( const T *pVal, const unsigned nElem ) + { + comBuf * pLastBuf = this->bufs.last (); + unsigned nCopied; + if ( pLastBuf ) { + nCopied = pLastBuf->push ( pVal, nElem ); + } + else { + nCopied = 0u; + } + while ( nElem > nCopied ) { + comBuf * pComBuf = newComBuf (); + nCopied += pComBuf->push + ( &pVal[nCopied], nElem - nCopied ); + this->pushComBuf ( *pComBuf ); + } + } + + // + // visual C++ versions 6 and 7 do not allow out of + // class member template function definition + // + template < class T > + inline void push ( const T & val ) + { + comBuf * pComBuf = this->bufs.last (); + if ( pComBuf && pComBuf->push ( val ) ) { + return; + } + pComBuf = newComBuf (); + bool success = pComBuf->push ( val ); + assert ( success ); + this->pushComBuf ( *pComBuf ); + } + + template < class T > + inline void push ( const T * ); // disabled + + comQueSend ( const comQueSend & ); + comQueSend & operator = ( const comQueSend & ); +}; + +extern const char cacNillBytes[]; + +inline comQueSendMsgMinder::comQueSendMsgMinder ( + class comQueSend & sendQueIn, epicsGuard < epicsMutex > & ) : + pSendQue ( & sendQueIn ) +{ + sendQueIn.beginMsg (); +} + +inline comQueSendMsgMinder::~comQueSendMsgMinder () +{ + if ( this->pSendQue ) { + this->pSendQue->clearUncommitedMsg (); + } +} + +inline void comQueSendMsgMinder::commit () +{ + if ( this->pSendQue ) { + this->pSendQue->commitMsg (); + this->pSendQue = 0; + } +} + +inline void comQueSend::beginMsg () +{ + this->pFirstUncommited = this->bufs.lastIter (); +} + +inline void comQueSend::pushUInt16 ( const ca_uint16_t value ) +{ + this->push ( value ); +} + +inline void comQueSend::pushUInt32 ( const ca_uint32_t value ) +{ + this->push ( value ); +} + +inline void comQueSend::pushFloat32 ( const ca_float32_t value ) +{ + this->push ( value ); +} + +inline void comQueSend::pushString ( const char *pVal, unsigned nChar ) +{ + this->push ( pVal, nChar ); +} + +inline void comQueSend::pushComBuf ( comBuf & cb ) +{ + this->bufs.add ( cb ); + if ( ! this->pFirstUncommited.valid() ) { + this->pFirstUncommited = this->bufs.lastIter (); + } +} + +inline unsigned comQueSend::occupiedBytes () const +{ + return this->nBytesPending; +} + +inline bool comQueSend::flushBlockThreshold () const +{ + return ( this->nBytesPending > 16 * comBuf::capacityBytes () ); +} + +inline bool comQueSend::flushEarlyThreshold ( unsigned nBytesThisMsg ) const +{ + return ( this->nBytesPending + nBytesThisMsg > 4 * comBuf::capacityBytes () ); +} + +// wrapping this with a function avoids WRS T2.2 Cygnus GNU compiler bugs +inline comBuf * comQueSend::newComBuf () +{ + return new ( this->comBufMemMgr ) comBuf; +} + +#endif // ifndef comQueSendh diff --git a/src/ca/convert.cpp b/src/ca/convert.cpp new file mode 100644 index 000000000..851711774 --- /dev/null +++ b/src/ca/convert.cpp @@ -0,0 +1,1440 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * C O N V E R T . C + * + * Author: D. Kersteins + * + * + * NOTES: + * + * 1) All routines in this file have an encode argument which + * determines if we are converting from the standard format to + * the local format or vise versa. To date only float and double data + * types must be converted differently depending on the encode + * argument - joh + * + */ + +#include + +#include "dbDefs.h" +#include "osiSock.h" +#include "osiWireFormat.h" + +#define epicsExportSharedSymbols +#include "net_convert.h" +#include "iocinf.h" +#include "caProto.h" +#include "caerr.h" + +/* + * NOOP if this isnt required + */ +#ifdef EPICS_CONVERSION_REQUIRED + +/* + * if hton is true then it is a host to network conversion + * otherwise vise-versa + * + * net format: big endian and IEEE float + */ +typedef void ( * CACVRTFUNCPTR ) ( + const void *pSrc, void *pDest, int hton, arrayElementCount count ); + +inline void dbr_htond ( + const dbr_double_t * pHost, dbr_double_t * pNet ) +{ + AlignedWireRef < epicsFloat64 > tmp ( *pNet ); + tmp = *pHost; +} +inline void dbr_ntohd ( + const dbr_double_t * pNet, dbr_double_t * pHost ) +{ + *pHost = AlignedWireRef < const epicsFloat64 > ( *pNet ); +} +inline void dbr_htonf ( + const dbr_float_t * pHost, dbr_float_t * pNet ) +{ + AlignedWireRef < epicsFloat32 > tmp ( *pNet ); + tmp = *pHost; +} +inline void dbr_ntohf ( + const dbr_float_t * pNet, dbr_float_t * pHost ) +{ + *pHost = AlignedWireRef < const epicsFloat32 > ( *pNet ); +} + +inline epicsUInt16 dbr_ntohs( const epicsUInt16 & net ) +{ + return AlignedWireRef < const epicsUInt16 > ( net ); +} + +inline epicsUInt16 dbr_htons ( const epicsUInt16 & host ) +{ + epicsUInt16 tmp; + AlignedWireRef < epicsUInt16 > awr ( tmp ); + awr = host; + return tmp; +} + +inline epicsUInt32 dbr_ntohl( const epicsUInt32 & net ) +{ + return AlignedWireRef < const epicsUInt32 > ( net ); +} + +inline epicsUInt32 dbr_htonl ( const epicsUInt32 & host ) +{ + epicsUInt32 tmp; + AlignedWireRef < epicsUInt32 > awr ( tmp ); + awr = host; + return tmp; +} + +/* + * if hton is true then it is a host to network conversion + * otherwise vise-versa + * + * net format: big endian and IEEE float + * + */ + +/* + * CVRT_STRING() + */ +static void cvrt_string( +const void *s, /* source */ +void *d, /* destination */ +int /* encode */, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + char *pSrc = (char *) s; + char *pDest = (char *) d; + + /* convert "in place" -> nothing to do */ + if (s == d) + return; + memcpy ( pDest, pSrc, num*MAX_STRING_SIZE ); +} + +/* + * CVRT_SHORT() + */ +static void cvrt_short( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + dbr_short_t *pSrc = (dbr_short_t *) s; + dbr_short_t *pDest = (dbr_short_t *) d; + + if(encode){ + for(arrayElementCount i=0; i nothing to do */ + if (s == d) + return; + for( arrayElementCount i=0; istatus = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + /* convert "in place" -> nothing else to do */ + if (s == d) + return; + + memcpy ( pDest->value, pSrc->value, (MAX_STRING_SIZE * num) ); + +} + +/**************************************************************************** +** cvrt_sts_short(s,d) +** struct dbr_sts_int *s pointer to source struct +** struct dbr_sts_int *d pointer to destination struct +** int encode; boolean, if true vax to ieee +** else ieee to vax +** +** converts fields ofstruct in HOST format to ieee format +** or +** converts fields of struct in NET format to fields with HOST +** format +****************************************************************************/ + +static void cvrt_sts_short( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_sts_int *pSrc = (struct dbr_sts_int *) s; + struct dbr_sts_int *pDest = (struct dbr_sts_int *) d; + + /* convert vax to ieee or ieee to vax format -- same code*/ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + if (num == 1) /* single value */ + pDest->value = dbr_ntohs(pSrc->value); + else /* array chan-- multiple pts */ + { + cvrt_short(&pSrc->value, &pDest->value, encode, num); + } +} +/**************************************************************************** +** cvrt_sts_float(s,d) +** struct dbr_sts_float *s pointer to source struct +** struct dbr_sts_float *d pointer to destination struct +** int encode; boolean, if true vax to ieee +** else ieee to vax +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_sts_float( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_sts_float *pSrc = (struct dbr_sts_float *) s; + struct dbr_sts_float *pDest = (struct dbr_sts_float *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + cvrt_float(&pSrc->value, &pDest->value, encode, num); +} + +/**************************************************************************** +** cvrt_sts_double(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_sts_double( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_sts_double *pSrc = (struct dbr_sts_double *) s; + struct dbr_sts_double *pDest = (struct dbr_sts_double *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + cvrt_double(&pSrc->value, &pDest->value, encode, num); +} + +/**************************************************************************** +** cvrt_sts_enum(s,d) +** struct dbr_sts_enum *s pointer to source struct +** struct dbr_sts_enum *d pointer to destination struct +** int encode; boolean, if true vax to ieee +** else ieee to vax +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_sts_enum( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_sts_enum *pSrc = (struct dbr_sts_enum *) s; + struct dbr_sts_enum *pDest = (struct dbr_sts_enum *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + if (num == 1) + pDest->value = dbr_ntohs(pSrc->value); + else { + cvrt_enum(&pSrc->value,&pDest->value,encode,num); + } +} + +/**************************************************************************** +** cvrt_gr_short() +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_gr_short( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_gr_int *pSrc = (struct dbr_gr_int *) s; + struct dbr_gr_int *pDest = (struct dbr_gr_int *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + + pDest->upper_disp_limit = dbr_ntohs(pSrc->upper_disp_limit); + pDest->lower_disp_limit = dbr_ntohs(pSrc->lower_disp_limit); + pDest->upper_alarm_limit = dbr_ntohs(pSrc->upper_alarm_limit); + pDest->upper_warning_limit = dbr_ntohs(pSrc->upper_warning_limit); + pDest->lower_alarm_limit = dbr_ntohs(pSrc->lower_alarm_limit); + pDest->lower_warning_limit = dbr_ntohs(pSrc->lower_warning_limit); + + if (num == 1) + pDest->value = dbr_ntohs(pSrc->value); + else { + cvrt_short(&pSrc->value, &pDest->value, encode,num); + } +} + +/**************************************************************************** +** cvrt_gr_char() +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_gr_char( +const void *s, /* source */ +void *d, /* destination */ +int /*encode*/, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_gr_char *pSrc = (struct dbr_gr_char *) s; + struct dbr_gr_char *pDest = (struct dbr_gr_char *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + if (s == d) /* source == dest -> no more conversions */ + return; + + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + + pDest->upper_disp_limit = pSrc->upper_disp_limit; + pDest->lower_disp_limit = pSrc->lower_disp_limit; + pDest->upper_alarm_limit = pSrc->upper_alarm_limit; + pDest->upper_warning_limit = pSrc->upper_warning_limit; + pDest->lower_alarm_limit = pSrc->lower_alarm_limit; + pDest->lower_warning_limit = pSrc->lower_warning_limit; + + if (num == 1) + pDest->value = pSrc->value; + else { + memcpy((char *)&pDest->value, (char *)&pSrc->value, num); + } +} + +/**************************************************************************** +** cvrt_gr_long() +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_gr_long( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_gr_long *pSrc = (struct dbr_gr_long *) s; + struct dbr_gr_long *pDest = (struct dbr_gr_long *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + + pDest->upper_disp_limit = dbr_ntohl(pSrc->upper_disp_limit); + pDest->lower_disp_limit = dbr_ntohl(pSrc->lower_disp_limit); + pDest->upper_alarm_limit = dbr_ntohl(pSrc->upper_alarm_limit); + pDest->upper_warning_limit = dbr_ntohl(pSrc->upper_warning_limit); + pDest->lower_alarm_limit = dbr_ntohl(pSrc->lower_alarm_limit); + pDest->lower_warning_limit = dbr_ntohl(pSrc->lower_warning_limit); + + if (num == 1) + pDest->value = dbr_ntohl(pSrc->value); + else { + cvrt_long(&pSrc->value, &pDest->value, encode, num); + } +} + +/**************************************************************************** +** cvrt_gr_enum(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_gr_enum( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_gr_enum *pSrc = (struct dbr_gr_enum *) s; + struct dbr_gr_enum *pDest = (struct dbr_gr_enum *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->no_str = dbr_ntohs(pSrc->no_str); + if ( s != d ) { + memcpy((void *)pDest->strs,(void *)pSrc->strs,sizeof(pSrc->strs)); + } + + if (num == 1) /* single value */ + pDest->value = dbr_ntohs(pSrc->value); + else /* array chan-- multiple pts */ + { + cvrt_enum(&(pSrc->value), &(pDest->value), encode, num); + } +} + +/**************************************************************************** +** cvrt_gr_double(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_gr_double( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_gr_double *pSrc = (struct dbr_gr_double *) s; + struct dbr_gr_double *pDest = (struct dbr_gr_double *) d; + + /* these are same for vax to ieee or ieee to vax */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->precision = dbr_ntohs(pSrc->precision); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + + if (encode) /* vax to ieee convert */ + { + if (num == 1){ + dbr_htond(&pSrc->value, &pDest->value); + } + else { + cvrt_double(&pSrc->value, &pDest->value, encode,num); + } + dbr_htond(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); + dbr_htond(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_htond(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_htond(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_htond(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_htond(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + } + else /* ieee to vax convert */ + { + if (num == 1){ + dbr_ntohd(&pSrc->value, &pDest->value); + } + else { + cvrt_double(&pSrc->value, &pDest->value, encode,num); + } + dbr_ntohd(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); + dbr_ntohd(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_ntohd(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_ntohd(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_ntohd(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_ntohd(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + } +} + + +/**************************************************************************** +** cvrt_gr_float(s,d) +** struct dbr_gr_float *d pointer to destination struct +** int encode; boolean, if true vax to ieee +** else ieee to vax +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_gr_float( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_gr_float *pSrc = (struct dbr_gr_float *) s; + struct dbr_gr_float *pDest = (struct dbr_gr_float *) d; + + /* these are same for vax to ieee or ieee to vax */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->precision = dbr_ntohs(pSrc->precision); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + + if (encode) /* vax to ieee convert */ + { + if (num == 1){ + dbr_htonf(&pSrc->value, &pDest->value); + } + else { + cvrt_float(&pSrc->value, &pDest->value, encode,num); + } + dbr_htonf(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); + dbr_htonf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_htonf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_htonf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_htonf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_htonf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + } + else /* ieee to vax convert */ + { + if (num == 1){ + dbr_ntohf(&pSrc->value, &pDest->value); + } + else { + cvrt_float(&pSrc->value, &pDest->value, encode,num); + } + dbr_ntohf(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); + dbr_ntohf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_ntohf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_ntohf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_ntohf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_ntohf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + } +} + + + +/**************************************************************************** +** cvrt_ctrl_short(s,d) +** struct dbr_gr_int *s pointer to source struct +** struct dbr_gr_int *d pointer to destination struct +** int encode; boolean, if true vax to ieee +** else ieee to vax +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_ctrl_short( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_ctrl_int *pSrc = (struct dbr_ctrl_int *) s; + struct dbr_ctrl_int *pDest = (struct dbr_ctrl_int *) d; + + /* vax to ieee or ieee to vax -- same code */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + + pDest->upper_disp_limit = dbr_ntohs(pSrc->upper_disp_limit); + pDest->lower_disp_limit = dbr_ntohs(pSrc->lower_disp_limit); + pDest->upper_alarm_limit = dbr_ntohs(pSrc->upper_alarm_limit); + pDest->upper_warning_limit = dbr_ntohs(pSrc->upper_warning_limit); + pDest->lower_alarm_limit = dbr_ntohs(pSrc->lower_alarm_limit); + pDest->lower_warning_limit = dbr_ntohs(pSrc->lower_warning_limit); + pDest->lower_ctrl_limit = dbr_ntohs(pSrc->lower_ctrl_limit); + pDest->upper_ctrl_limit = dbr_ntohs(pSrc->upper_ctrl_limit); + + if (num == 1) + pDest->value = dbr_ntohs(pSrc->value); + else { + cvrt_short(&pSrc->value, &pDest->value, encode, num); + } +} + +/**************************************************************************** +** cvrt_ctrl_long(s,d) +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_ctrl_long( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_ctrl_long *pSrc = (struct dbr_ctrl_long*) s; + struct dbr_ctrl_long *pDest = (struct dbr_ctrl_long *) d; + + /* vax to ieee or ieee to vax -- same code */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + + pDest->upper_disp_limit = dbr_ntohl(pSrc->upper_disp_limit); + pDest->lower_disp_limit = dbr_ntohl(pSrc->lower_disp_limit); + pDest->upper_alarm_limit = dbr_ntohl(pSrc->upper_alarm_limit); + pDest->upper_warning_limit = dbr_ntohl(pSrc->upper_warning_limit); + pDest->lower_alarm_limit = dbr_ntohl(pSrc->lower_alarm_limit); + pDest->lower_warning_limit = dbr_ntohl(pSrc->lower_warning_limit); + pDest->lower_ctrl_limit = dbr_ntohl(pSrc->lower_ctrl_limit); + pDest->upper_ctrl_limit = dbr_ntohl(pSrc->upper_ctrl_limit); + + if (num == 1) + pDest->value = dbr_ntohl(pSrc->value); + else { + cvrt_long(&pSrc->value, &pDest->value, encode, num); + } +} + +/**************************************************************************** +** cvrt_ctrl_short(s,d) +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_ctrl_char( +const void *s, /* source */ +void *d, /* destination */ +int /*encode*/, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_ctrl_char *pSrc = (struct dbr_ctrl_char *) s; + struct dbr_ctrl_char *pDest = (struct dbr_ctrl_char *) d; + + /* vax to ieee or ieee to vax -- same code */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + if ( s == d ) + return; + + pDest->upper_disp_limit = pSrc->upper_disp_limit; + pDest->lower_disp_limit = pSrc->lower_disp_limit; + pDest->upper_alarm_limit = pSrc->upper_alarm_limit; + pDest->upper_warning_limit = pSrc->upper_warning_limit; + pDest->lower_ctrl_limit = pSrc->lower_ctrl_limit; + pDest->upper_ctrl_limit = pSrc->upper_ctrl_limit; + + if (num == 1) + pDest->value = pSrc->value; + else { + memcpy((void *)&pDest->value, (void *)&pSrc->value, num); + } +} + +/**************************************************************************** +** cvrt_ctrl_double(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_ctrl_double( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_ctrl_double *pSrc = (struct dbr_ctrl_double *) s; + struct dbr_ctrl_double *pDest = (struct dbr_ctrl_double *) d; + + /* these are the same for ieee to vax or vax to ieee */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->precision = dbr_ntohs(pSrc->precision); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + if (encode) /* vax to ieee convert */ + { + if (num == 1){ + dbr_htond(&pSrc->value, &pDest->value); + } + else { + cvrt_double(&pSrc->value, &pDest->value, encode, num); + } + dbr_htond(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); + dbr_htond(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_htond(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_htond(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_htond(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_htond(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + dbr_htond(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); + dbr_htond(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); + } + else /* ieee to vax convert */ + { + if (num == 1){ + dbr_ntohd(&pSrc->value, &pDest->value); + } + else { + cvrt_double(&pSrc->value, &pDest->value, encode, num); + } + dbr_ntohd(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_ntohd(&pSrc->upper_disp_limit, &pDest->upper_disp_limit); + dbr_ntohd(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_ntohd(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_ntohd(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_ntohd(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + dbr_ntohd(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); + dbr_ntohd(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); + } + +} + + + +/**************************************************************************** +** cvrt_ctrl_float(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_ctrl_float( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_ctrl_float *pSrc = (struct dbr_ctrl_float *) s; + struct dbr_ctrl_float *pDest = (struct dbr_ctrl_float *) d; + + /* these are the same for ieee to vaax or vax to ieee */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->precision = dbr_ntohs(pSrc->precision); + if ( s != d ) { + memcpy(pDest->units,pSrc->units,sizeof(pSrc->units)); + } + if (encode) /* vax to ieee convert */ + { + if (num == 1){ + dbr_htonf(&pSrc->value, &pDest->value); + } + else { + cvrt_float(&pSrc->value, &pDest->value, encode, num); + } + dbr_htonf(&pSrc->upper_disp_limit,&pDest->upper_disp_limit); + dbr_htonf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_htonf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_htonf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_htonf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_htonf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + dbr_htonf(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); + dbr_htonf(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); + } + else /* ieee to vax convert */ + { + if (num == 1){ + dbr_ntohf(&pSrc->value, &pDest->value); + } + else { + cvrt_float(&pSrc->value, &pDest->value, encode, num); + } + dbr_ntohf(&pSrc->lower_disp_limit, &pDest->lower_disp_limit); + dbr_ntohf(&pSrc->upper_disp_limit, &pDest->upper_disp_limit); + dbr_ntohf(&pSrc->upper_alarm_limit, &pDest->upper_alarm_limit); + dbr_ntohf(&pSrc->upper_warning_limit, &pDest->upper_warning_limit); + dbr_ntohf(&pSrc->lower_alarm_limit, &pDest->lower_alarm_limit); + dbr_ntohf(&pSrc->lower_warning_limit, &pDest->lower_warning_limit); + dbr_ntohf(&pSrc->lower_ctrl_limit, &pDest->lower_ctrl_limit); + dbr_ntohf(&pSrc->upper_ctrl_limit, &pDest->upper_ctrl_limit); + } + +} + + +/**************************************************************************** +** cvrt_ctrl_enum(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_ctrl_enum( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_ctrl_enum *pSrc = (struct dbr_ctrl_enum *) s; + struct dbr_ctrl_enum *pDest = (struct dbr_ctrl_enum *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->no_str = dbr_ntohs(pSrc->no_str); + if ( s != d ) { + memcpy((void *)pDest->strs,(void *)pSrc->strs,sizeof(pSrc->strs)); + } + + if (num == 1) /* single value */ + pDest->value = dbr_ntohs(pSrc->value); + else /* array chan-- multiple pts */ + { + cvrt_enum(&(pSrc->value), &(pDest->value), encode, num); + } +} + +/**************************************************************************** +** cvrt_sts_char(s,d) +** struct dbr_sts_int *s pointer to source struct +** struct dbr_sts_int *d pointer to destination struct +** int encode; boolean, if true vax to ieee +** else ieee to vax +** +** converts fields ofstruct in HOST format to ieee format +** or +** converts fields of struct in NET format to fields with HOST +** format +****************************************************************************/ + +static void cvrt_sts_char( +const void *s, /* source */ +void *d, /* destination */ +int /*encode*/, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_sts_char *pSrc = (struct dbr_sts_char *) s; + struct dbr_sts_char *pDest = (struct dbr_sts_char *) d; + + /* convert vax to ieee or ieee to vax format -- same code*/ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + if ( s == d ) + return; + + if (num == 1) /* single value */ + pDest->value = pSrc->value; + else /* array chan-- multiple pts */ + { + memcpy((void *)&pDest->value, (void *)&pSrc->value, num); + } +} + +/**************************************************************************** +** cvrt_sts_long(s,d) +** +** converts fields ofstruct in HOST format to ieee format +** or +** converts fields of struct in NET format to fields with HOST +** format +****************************************************************************/ + +static void cvrt_sts_long( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_sts_long *pSrc = (struct dbr_sts_long *) s; + struct dbr_sts_long *pDest = (struct dbr_sts_long *) d; + + /* convert vax to ieee or ieee to vax format -- same code*/ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + + if (num == 1) /* single value */ + pDest->value = dbr_ntohl(pSrc->value); + else /* array chan-- multiple pts */ + { + cvrt_long(&pDest->value, &pSrc->value, encode, num); + } +} + + +/**************************************************************************** +** cvrt_time_string(s,d) +** +** converts fields of struct in HOST format to NET format +** or +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_time_string( +const void *s, /* source */ +void *d, /* destination */ +int /*encode*/, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_time_string *pSrc = (struct dbr_time_string *) s; + struct dbr_time_string *pDest = (struct dbr_time_string *) d; + + /* convert ieee to vax format or vax to ieee */ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); + pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); + + if ( s != d ) { + memcpy(pDest->value, pSrc->value, (MAX_STRING_SIZE * num)); + } +} + +/**************************************************************************** +** cvrt_time_short(s,d) +** +** converts fields ofstruct in HOST format to ieee format +** or +** converts fields of struct in NET format to fields with HOST +** format +****************************************************************************/ + +static void cvrt_time_short( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_time_short *pSrc = (struct dbr_time_short *) s; + struct dbr_time_short *pDest = (struct dbr_time_short *) d; + + /* convert vax to ieee or ieee to vax format -- same code*/ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); + pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); + + if (num == 1) /* single value */ + pDest->value = dbr_ntohs(pSrc->value); + else /* array chan-- multiple pts */ + { + cvrt_short(&pSrc->value, &pDest->value, encode, num); + } +} + +/**************************************************************************** +** cvrt_time_float(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_time_float( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_time_float *pSrc = (struct dbr_time_float *) s; + struct dbr_time_float *pDest = (struct dbr_time_float *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); + pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); + + cvrt_float(&pSrc->value, &pDest->value, encode, num); +} + +/**************************************************************************** +** cvrt_time_double(s,d) +** +** if encode +** converts struct in HOST format to ieee format +** else +** converts fields of struct in NET format to fields with HOST +** format; +****************************************************************************/ + +static void cvrt_time_double( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_time_double *pSrc = (struct dbr_time_double *) s; + struct dbr_time_double *pDest = (struct dbr_time_double *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); + pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); + + cvrt_double(&pSrc->value, &pDest->value, encode, num); +} + + + +/**************************************************************************** +** cvrt_time_enum(s,d) +** +** converts fields of struct in NET format to fields with HOST format +** or +** converts fields of struct in HOST format to fields with NET format +** +****************************************************************************/ + +static void cvrt_time_enum( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_time_enum *pSrc = (struct dbr_time_enum *) s; + struct dbr_time_enum *pDest = (struct dbr_time_enum *) d; + + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); + pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); + if (num == 1) + pDest->value = dbr_ntohs(pSrc->value); + else { + cvrt_enum(&pSrc->value,&pDest->value,encode,num); + } +} + +/**************************************************************************** +** cvrt_sts_char(s,d) +** +** converts fields ofstruct in HOST format to ieee format +** or +** converts fields of struct in NET format to fields with HOST +** format +****************************************************************************/ + +static void cvrt_time_char( +const void *s, /* source */ +void *d, /* destination */ +int /*encode*/, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_time_char *pSrc = (struct dbr_time_char *) s; + struct dbr_time_char *pDest = (struct dbr_time_char *) d; + + /* convert vax to ieee or ieee to vax format -- same code*/ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); + pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); + + if ( s == d ) + return; + + if (num == 1) /* single value */ + pDest->value = pSrc->value; + else /* array chan-- multiple pts */ + { + memcpy((void *)&pDest->value, (void *)&pSrc->value, num); + } +} +/**************************************************************************** +** cvrt_time_long(s,d) +** +** converts fields ofstruct in HOST format to ieee format +** or +** converts fields of struct in NET format to fields with HOST +** format +****************************************************************************/ + +static void cvrt_time_long( +const void *s, /* source */ +void *d, /* destination */ +int encode, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + struct dbr_time_long *pSrc = (struct dbr_time_long *) s; + struct dbr_time_long *pDest = (struct dbr_time_long *) d; + + /* convert vax to ieee or ieee to vax format -- same code*/ + pDest->status = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->stamp.secPastEpoch = dbr_ntohl(pSrc->stamp.secPastEpoch); + pDest->stamp.nsec = dbr_ntohl(pSrc->stamp.nsec); + + if (num == 1) /* single value */ + pDest->value = dbr_ntohl(pSrc->value); + else /* array chan-- multiple pts */ + { + cvrt_long(&pDest->value, &pSrc->value, encode, num); + } +} + +/* + * cvrt_put_ackt() + * + * + * + * + */ +static void cvrt_put_ackt( +const void *s, /* source */ +void *d, /* destination */ +int /*encode*/, /* cvrt HOST to NET if T */ +arrayElementCount num /* number of values */ +) +{ + dbr_put_ackt_t *pSrc = (dbr_put_ackt_t *) s; + dbr_put_ackt_t *pDest = (dbr_put_ackt_t *) d; + arrayElementCount i; + + for(i=0; istatus = dbr_ntohs(pSrc->status); + pDest->severity = dbr_ntohs(pSrc->severity); + pDest->ackt = dbr_ntohs(pSrc->ackt); + pDest->acks = dbr_ntohs(pSrc->acks); + + /* convert "in place" -> nothing else to do */ + if (s == d) + return; + + memcpy(pDest->value, pSrc->value, (MAX_STRING_SIZE * num)); +} + +/* cvrt is (array of) (pointer to) (function returning) int */ +static CACVRTFUNCPTR cac_dbr_cvrt[] = { + cvrt_string, + cvrt_short, + cvrt_float, + cvrt_enum, + cvrt_char, + cvrt_long, + cvrt_double, + + cvrt_sts_string, + cvrt_sts_short, + cvrt_sts_float, + cvrt_sts_enum, + cvrt_sts_char, + cvrt_sts_long, + cvrt_sts_double, + + cvrt_time_string, + cvrt_time_short, + cvrt_time_float, + cvrt_time_enum, + cvrt_time_char, + cvrt_time_long, + cvrt_time_double, + + cvrt_sts_string, /* DBR_GR_STRING identical to dbr_sts_string */ + cvrt_gr_short, + cvrt_gr_float, + cvrt_gr_enum, + cvrt_gr_char, + cvrt_gr_long, + cvrt_gr_double, + + cvrt_sts_string, /* DBR_CTRL_STRING identical to dbr_sts_string */ + cvrt_ctrl_short, + cvrt_ctrl_float, + cvrt_ctrl_enum, + cvrt_ctrl_char, + cvrt_ctrl_long, + cvrt_ctrl_double, + + cvrt_put_ackt, + cvrt_put_ackt, /* DBR_PUT_ACKS identical to DBR_PUT_ACKT */ + cvrt_stsack_string, + cvrt_string +}; + +#endif /* EPICS_CONVERSION_REQUIRED */ + +int caNetConvert ( unsigned type, const void *pSrc, void *pDest, + int hton, arrayElementCount count ) +{ +# ifdef EPICS_CONVERSION_REQUIRED + if ( type >= NELEMENTS ( cac_dbr_cvrt ) ) { + return ECA_BADTYPE; + } + ( * cac_dbr_cvrt [ type ] ) ( pSrc, pDest, hton, count ); +# else + if ( INVALID_DB_REQ ( type ) ) { + return ECA_BADTYPE; + } + if ( pSrc != pDest ) { + memcpy ( pDest, pSrc, dbr_size_n ( type, count ) ); + } +# endif + return ECA_NORMAL; +} + diff --git a/src/ca/db_access.h b/src/ca/db_access.h new file mode 100644 index 000000000..92aa5d011 --- /dev/null +++ b/src/ca/db_access.h @@ -0,0 +1,740 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* base/include/db_access.h */ +/* Author: Bob Dalesio + * Date: 4-4-88 +*/ + +#ifndef INCLdb_accessh +#define INCLdb_accessh + +#include + +#ifdef epicsExportSharedSymbols +# define INCLdb_accessh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsTypes.h" +#include "epicsTime.h" + +#ifdef INCLdb_accessh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_UNITS_SIZE 8 +#define MAX_ENUM_STRING_SIZE 26 +#define MAX_ENUM_STATES 16 + +/* + * architecture independent types + * + * (so far this is sufficient for all archs we have ported to) + */ +typedef epicsOldString dbr_string_t; +typedef epicsUInt8 dbr_char_t; +typedef epicsInt16 dbr_short_t; +typedef epicsUInt16 dbr_ushort_t; +typedef epicsInt16 dbr_int_t; +typedef epicsUInt16 dbr_enum_t; +typedef epicsInt32 dbr_long_t; +typedef epicsUInt32 dbr_ulong_t; +typedef epicsFloat32 dbr_float_t; +typedef epicsFloat64 dbr_double_t; +typedef epicsUInt16 dbr_put_ackt_t; +typedef epicsUInt16 dbr_put_acks_t; +typedef epicsOldString dbr_stsack_string_t; +typedef epicsOldString dbr_class_name_t; + +#ifndef db_accessHFORdb_accessC +/* database field types */ +#define DBF_STRING 0 +#define DBF_INT 1 +#define DBF_SHORT 1 +#define DBF_FLOAT 2 +#define DBF_ENUM 3 +#define DBF_CHAR 4 +#define DBF_LONG 5 +#define DBF_DOUBLE 6 +#define DBF_NO_ACCESS 7 +#define LAST_TYPE DBF_DOUBLE +#define VALID_DB_FIELD(x) ((x >= 0) && (x <= LAST_TYPE)) +#define INVALID_DB_FIELD(x) ((x < 0) || (x > LAST_TYPE)) + +/* data request buffer types */ +#define DBR_STRING DBF_STRING +#define DBR_INT DBF_INT +#define DBR_SHORT DBF_INT +#define DBR_FLOAT DBF_FLOAT +#define DBR_ENUM DBF_ENUM +#define DBR_CHAR DBF_CHAR +#define DBR_LONG DBF_LONG +#define DBR_DOUBLE DBF_DOUBLE +#define DBR_STS_STRING 7 +#define DBR_STS_SHORT 8 +#define DBR_STS_INT DBR_STS_SHORT +#define DBR_STS_FLOAT 9 +#define DBR_STS_ENUM 10 +#define DBR_STS_CHAR 11 +#define DBR_STS_LONG 12 +#define DBR_STS_DOUBLE 13 +#define DBR_TIME_STRING 14 +#define DBR_TIME_INT 15 +#define DBR_TIME_SHORT 15 +#define DBR_TIME_FLOAT 16 +#define DBR_TIME_ENUM 17 +#define DBR_TIME_CHAR 18 +#define DBR_TIME_LONG 19 +#define DBR_TIME_DOUBLE 20 +#define DBR_GR_STRING 21 +#define DBR_GR_SHORT 22 +#define DBR_GR_INT DBR_GR_SHORT +#define DBR_GR_FLOAT 23 +#define DBR_GR_ENUM 24 +#define DBR_GR_CHAR 25 +#define DBR_GR_LONG 26 +#define DBR_GR_DOUBLE 27 +#define DBR_CTRL_STRING 28 +#define DBR_CTRL_SHORT 29 +#define DBR_CTRL_INT DBR_CTRL_SHORT +#define DBR_CTRL_FLOAT 30 +#define DBR_CTRL_ENUM 31 +#define DBR_CTRL_CHAR 32 +#define DBR_CTRL_LONG 33 +#define DBR_CTRL_DOUBLE 34 +#define DBR_PUT_ACKT DBR_CTRL_DOUBLE + 1 +#define DBR_PUT_ACKS DBR_PUT_ACKT + 1 +#define DBR_STSACK_STRING DBR_PUT_ACKS + 1 +#define DBR_CLASS_NAME DBR_STSACK_STRING + 1 +#define LAST_BUFFER_TYPE DBR_CLASS_NAME +#define VALID_DB_REQ(x) ((x >= 0) && (x <= LAST_BUFFER_TYPE)) +#define INVALID_DB_REQ(x) ((x < 0) || (x > LAST_BUFFER_TYPE)) + +/* + * The enumeration "epicsType" is an index to this array + * of type DBR types. In some cases we select the a + * larger type to avoid loss of information + */ +epicsShareExtern const int epicsTypeToDBR_XXXX [lastEpicsType+1]; + +/* + * The DBR_XXXX types are indicies into this array + */ +epicsShareExtern const epicsType DBR_XXXXToEpicsType [LAST_BUFFER_TYPE+1]; + +/* values returned for each field type + * DBR_STRING returns a NULL terminated string + * DBR_SHORT returns an unsigned short + * DBR_INT returns an unsigned short + * DBR_FLOAT returns an IEEE floating point value + * DBR_ENUM returns an unsigned short which is the enum item + * DBR_CHAR returns an unsigned char + * DBR_LONG returns an unsigned long + * DBR_DOUBLE returns a double precision floating point number + * DBR_STS_STRING returns a string status structure (dbr_sts_string) + * DBR_STS_SHORT returns a short status structure (dbr_sts_short) + * DBR_STS_INT returns a short status structure (dbr_sts_int) + * DBR_STS_FLOAT returns a float status structure (dbr_sts_float) + * DBR_STS_ENUM returns an enum status structure (dbr_sts_enum) + * DBR_STS_CHAR returns a char status structure (dbr_sts_char) + * DBR_STS_LONG returns a long status structure (dbr_sts_long) + * DBR_STS_DOUBLE returns a double status structure (dbr_sts_double) + * DBR_TIME_STRING returns a string time structure (dbr_time_string) + * DBR_TIME_SHORT returns a short time structure (dbr_time_short) + * DBR_TIME_INT returns a short time structure (dbr_time_short) + * DBR_TIME_FLOAT returns a float time structure (dbr_time_float) + * DBR_TIME_ENUM returns an enum time structure (dbr_time_enum) + * DBR_TIME_CHAR returns a char time structure (dbr_time_char) + * DBR_TIME_LONG returns a long time structure (dbr_time_long) + * DBR_TIME_DOUBLE returns a double time structure (dbr_time_double) + * DBR_GR_STRING returns a graphic string structure (dbr_gr_string) + * DBR_GR_SHORT returns a graphic short structure (dbr_gr_short) + * DBR_GR_INT returns a graphic short structure (dbr_gr_int) + * DBR_GR_FLOAT returns a graphic float structure (dbr_gr_float) + * DBR_GR_ENUM returns a graphic enum structure (dbr_gr_enum) + * DBR_GR_CHAR returns a graphic char structure (dbr_gr_char) + * DBR_GR_LONG returns a graphic long structure (dbr_gr_long) + * DBR_GR_DOUBLE returns a graphic double structure (dbr_gr_double) + * DBR_CTRL_STRING returns a control string structure (dbr_ctrl_int) + * DBR_CTRL_SHORT returns a control short structure (dbr_ctrl_short) + * DBR_CTRL_INT returns a control short structure (dbr_ctrl_int) + * DBR_CTRL_FLOAT returns a control float structure (dbr_ctrl_float) + * DBR_CTRL_ENUM returns a control enum structure (dbr_ctrl_enum) + * DBR_CTRL_CHAR returns a control char structure (dbr_ctrl_char) + * DBR_CTRL_LONG returns a control long structure (dbr_ctrl_long) + * DBR_CTRL_DOUBLE returns a control double structure (dbr_ctrl_double) + */ +#endif /*db_accessHFORdb_accessC*/ + +/* VALUES WITH STATUS STRUCTURES */ + +/* structure for a string status field */ +struct dbr_sts_string { + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_string_t value; /* current value */ +}; + +/* structure for a string status and ack field */ +struct dbr_stsack_string{ + dbr_ushort_t status; /* status of value */ + dbr_ushort_t severity; /* severity of alarm */ + dbr_ushort_t ackt; /* ack transient? */ + dbr_ushort_t acks; /* ack severity */ + dbr_string_t value; /* current value */ +}; +/* structure for an short status field */ +struct dbr_sts_int{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t value; /* current value */ +}; +struct dbr_sts_short{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t value; /* current value */ +}; + +/* structure for a float status field */ +struct dbr_sts_float{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_float_t value; /* current value */ +}; + +/* structure for a enum status field */ +struct dbr_sts_enum{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_enum_t value; /* current value */ +}; + +/* structure for a char status field */ +struct dbr_sts_char{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_char_t RISC_pad; /* RISC alignment */ + dbr_char_t value; /* current value */ +}; + +/* structure for a long status field */ +struct dbr_sts_long{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_long_t value; /* current value */ +}; + +/* structure for a double status field */ +struct dbr_sts_double{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_long_t RISC_pad; /* RISC alignment */ + dbr_double_t value; /* current value */ +}; + +/* VALUES WITH STATUS AND TIME STRUCTURES */ + +/* structure for a string time field */ +struct dbr_time_string{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + epicsTimeStamp stamp; /* time stamp */ + dbr_string_t value; /* current value */ +}; + +/* structure for an short time field */ +struct dbr_time_short{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + epicsTimeStamp stamp; /* time stamp */ + dbr_short_t RISC_pad; /* RISC alignment */ + dbr_short_t value; /* current value */ +}; + +/* structure for a float time field */ +struct dbr_time_float{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + epicsTimeStamp stamp; /* time stamp */ + dbr_float_t value; /* current value */ +}; + +/* structure for a enum time field */ +struct dbr_time_enum{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + epicsTimeStamp stamp; /* time stamp */ + dbr_short_t RISC_pad; /* RISC alignment */ + dbr_enum_t value; /* current value */ +}; + +/* structure for a char time field */ +struct dbr_time_char{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + epicsTimeStamp stamp; /* time stamp */ + dbr_short_t RISC_pad0; /* RISC alignment */ + dbr_char_t RISC_pad1; /* RISC alignment */ + dbr_char_t value; /* current value */ +}; + +/* structure for a long time field */ +struct dbr_time_long{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + epicsTimeStamp stamp; /* time stamp */ + dbr_long_t value; /* current value */ +}; + +/* structure for a double time field */ +struct dbr_time_double{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + epicsTimeStamp stamp; /* time stamp */ + dbr_long_t RISC_pad; /* RISC alignment */ + dbr_double_t value; /* current value */ +}; + +/* VALUES WITH STATUS AND GRAPHIC STRUCTURES */ + +/* structure for a graphic string */ + /* not implemented; use struct_dbr_sts_string */ + +/* structure for a graphic short field */ +struct dbr_gr_int{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_short_t upper_disp_limit; /* upper limit of graph */ + dbr_short_t lower_disp_limit; /* lower limit of graph */ + dbr_short_t upper_alarm_limit; + dbr_short_t upper_warning_limit; + dbr_short_t lower_warning_limit; + dbr_short_t lower_alarm_limit; + dbr_short_t value; /* current value */ +}; +struct dbr_gr_short{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_short_t upper_disp_limit; /* upper limit of graph */ + dbr_short_t lower_disp_limit; /* lower limit of graph */ + dbr_short_t upper_alarm_limit; + dbr_short_t upper_warning_limit; + dbr_short_t lower_warning_limit; + dbr_short_t lower_alarm_limit; + dbr_short_t value; /* current value */ +}; + +/* structure for a graphic floating point field */ +struct dbr_gr_float{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t precision; /* number of decimal places */ + dbr_short_t RISC_pad0; /* RISC alignment */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_float_t upper_disp_limit; /* upper limit of graph */ + dbr_float_t lower_disp_limit; /* lower limit of graph */ + dbr_float_t upper_alarm_limit; + dbr_float_t upper_warning_limit; + dbr_float_t lower_warning_limit; + dbr_float_t lower_alarm_limit; + dbr_float_t value; /* current value */ +}; + +/* structure for a graphic enumeration field */ +struct dbr_gr_enum{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t no_str; /* number of strings */ + char strs[MAX_ENUM_STATES][MAX_ENUM_STRING_SIZE]; + /* state strings */ + dbr_enum_t value; /* current value */ +}; + +/* structure for a graphic char field */ +struct dbr_gr_char{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_char_t upper_disp_limit; /* upper limit of graph */ + dbr_char_t lower_disp_limit; /* lower limit of graph */ + dbr_char_t upper_alarm_limit; + dbr_char_t upper_warning_limit; + dbr_char_t lower_warning_limit; + dbr_char_t lower_alarm_limit; + dbr_char_t RISC_pad; /* RISC alignment */ + dbr_char_t value; /* current value */ +}; + +/* structure for a graphic long field */ +struct dbr_gr_long{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_long_t upper_disp_limit; /* upper limit of graph */ + dbr_long_t lower_disp_limit; /* lower limit of graph */ + dbr_long_t upper_alarm_limit; + dbr_long_t upper_warning_limit; + dbr_long_t lower_warning_limit; + dbr_long_t lower_alarm_limit; + dbr_long_t value; /* current value */ +}; + +/* structure for a graphic double field */ +struct dbr_gr_double{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t precision; /* number of decimal places */ + dbr_short_t RISC_pad0; /* RISC alignment */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_double_t upper_disp_limit; /* upper limit of graph */ + dbr_double_t lower_disp_limit; /* lower limit of graph */ + dbr_double_t upper_alarm_limit; + dbr_double_t upper_warning_limit; + dbr_double_t lower_warning_limit; + dbr_double_t lower_alarm_limit; + dbr_double_t value; /* current value */ +}; + +/* VALUES WITH STATUS, GRAPHIC and CONTROL STRUCTURES */ + +/* structure for a control string */ + /* not implemented; use struct_dbr_sts_string */ + +/* structure for a control integer */ +struct dbr_ctrl_int{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_short_t upper_disp_limit; /* upper limit of graph */ + dbr_short_t lower_disp_limit; /* lower limit of graph */ + dbr_short_t upper_alarm_limit; + dbr_short_t upper_warning_limit; + dbr_short_t lower_warning_limit; + dbr_short_t lower_alarm_limit; + dbr_short_t upper_ctrl_limit; /* upper control limit */ + dbr_short_t lower_ctrl_limit; /* lower control limit */ + dbr_short_t value; /* current value */ +}; +struct dbr_ctrl_short{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_short_t upper_disp_limit; /* upper limit of graph */ + dbr_short_t lower_disp_limit; /* lower limit of graph */ + dbr_short_t upper_alarm_limit; + dbr_short_t upper_warning_limit; + dbr_short_t lower_warning_limit; + dbr_short_t lower_alarm_limit; + dbr_short_t upper_ctrl_limit; /* upper control limit */ + dbr_short_t lower_ctrl_limit; /* lower control limit */ + dbr_short_t value; /* current value */ +}; + +/* structure for a control floating point field */ +struct dbr_ctrl_float{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t precision; /* number of decimal places */ + dbr_short_t RISC_pad; /* RISC alignment */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_float_t upper_disp_limit; /* upper limit of graph */ + dbr_float_t lower_disp_limit; /* lower limit of graph */ + dbr_float_t upper_alarm_limit; + dbr_float_t upper_warning_limit; + dbr_float_t lower_warning_limit; + dbr_float_t lower_alarm_limit; + dbr_float_t upper_ctrl_limit; /* upper control limit */ + dbr_float_t lower_ctrl_limit; /* lower control limit */ + dbr_float_t value; /* current value */ +}; + +/* structure for a control enumeration field */ +struct dbr_ctrl_enum{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t no_str; /* number of strings */ + char strs[MAX_ENUM_STATES][MAX_ENUM_STRING_SIZE]; + /* state strings */ + dbr_enum_t value; /* current value */ +}; + +/* structure for a control char field */ +struct dbr_ctrl_char{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_char_t upper_disp_limit; /* upper limit of graph */ + dbr_char_t lower_disp_limit; /* lower limit of graph */ + dbr_char_t upper_alarm_limit; + dbr_char_t upper_warning_limit; + dbr_char_t lower_warning_limit; + dbr_char_t lower_alarm_limit; + dbr_char_t upper_ctrl_limit; /* upper control limit */ + dbr_char_t lower_ctrl_limit; /* lower control limit */ + dbr_char_t RISC_pad; /* RISC alignment */ + dbr_char_t value; /* current value */ +}; + +/* structure for a control long field */ +struct dbr_ctrl_long{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_long_t upper_disp_limit; /* upper limit of graph */ + dbr_long_t lower_disp_limit; /* lower limit of graph */ + dbr_long_t upper_alarm_limit; + dbr_long_t upper_warning_limit; + dbr_long_t lower_warning_limit; + dbr_long_t lower_alarm_limit; + dbr_long_t upper_ctrl_limit; /* upper control limit */ + dbr_long_t lower_ctrl_limit; /* lower control limit */ + dbr_long_t value; /* current value */ +}; + +/* structure for a control double field */ +struct dbr_ctrl_double{ + dbr_short_t status; /* status of value */ + dbr_short_t severity; /* severity of alarm */ + dbr_short_t precision; /* number of decimal places */ + dbr_short_t RISC_pad0; /* RISC alignment */ + char units[MAX_UNITS_SIZE]; /* units of value */ + dbr_double_t upper_disp_limit; /* upper limit of graph */ + dbr_double_t lower_disp_limit; /* lower limit of graph */ + dbr_double_t upper_alarm_limit; + dbr_double_t upper_warning_limit; + dbr_double_t lower_warning_limit; + dbr_double_t lower_alarm_limit; + dbr_double_t upper_ctrl_limit; /* upper control limit */ + dbr_double_t lower_ctrl_limit; /* lower control limit */ + dbr_double_t value; /* current value */ +}; + +#define dbr_size_n(TYPE,COUNT)\ +((unsigned)((COUNT)<=0?dbr_size[TYPE]:dbr_size[TYPE]+((COUNT)-1)*dbr_value_size[TYPE])) + +/* size for each type - array indexed by the DBR_ type code */ +epicsShareExtern const unsigned short dbr_size[]; + +/* size for each type's value - array indexed by the DBR_ type code */ +epicsShareExtern const unsigned short dbr_value_size[]; + +#ifndef db_accessHFORdb_accessC +/* class for each type's value */ +enum dbr_value_class { + dbr_class_int, + dbr_class_float, + dbr_class_string, + dbr_class_max}; + +epicsShareExtern const enum dbr_value_class dbr_value_class[LAST_BUFFER_TYPE+1]; + +/* + * ptr to value given a pointer to the structure and the DBR type + */ +#define dbr_value_ptr(PDBR, DBR_TYPE) \ +((void *)(((char *)PDBR)+dbr_value_offset[DBR_TYPE])) + +/* + * ptr to value given a pointer to the structure and the structure declaration + */ +#define dbr_value_ptr_from_structure(PDBR, STRUCTURE)\ +((void *)(((char *)PDBR)+BYTE_OS(STRUCTURE, value))) + +epicsShareExtern const unsigned short dbr_value_offset[LAST_BUFFER_TYPE+1]; + + +/* union for each fetch buffers */ +union db_access_val{ + dbr_string_t strval; /* string max size */ + dbr_short_t shrtval; /* short */ + dbr_short_t intval; /* short */ + dbr_float_t fltval; /* IEEE Float */ + dbr_enum_t enmval; /* item number */ + dbr_char_t charval; /* character */ + dbr_long_t longval; /* long */ + dbr_double_t doubleval; /* double */ + struct dbr_sts_string sstrval; /* string field with status */ + struct dbr_sts_short sshrtval; /* short field with status */ + struct dbr_sts_float sfltval; /* float field with status */ + struct dbr_sts_enum senmval; /* item number with status */ + struct dbr_sts_char schrval; /* char field with status */ + struct dbr_sts_long slngval; /* long field with status */ + struct dbr_sts_double sdblval; /* double field with time */ + struct dbr_time_string tstrval; /* string field with time */ + struct dbr_time_short tshrtval; /* short field with time */ + struct dbr_time_float tfltval; /* float field with time */ + struct dbr_time_enum tenmval; /* item number with time */ + struct dbr_time_char tchrval; /* char field with time */ + struct dbr_time_long tlngval; /* long field with time */ + struct dbr_time_double tdblval; /* double field with time */ + struct dbr_sts_string gstrval; /* graphic string info */ + struct dbr_gr_short gshrtval; /* graphic short info */ + struct dbr_gr_float gfltval; /* graphic float info */ + struct dbr_gr_enum genmval; /* graphic item info */ + struct dbr_gr_char gchrval; /* graphic char info */ + struct dbr_gr_long glngval; /* graphic long info */ + struct dbr_gr_double gdblval; /* graphic double info */ + struct dbr_sts_string cstrval; /* control string info */ + struct dbr_ctrl_short cshrtval; /* control short info */ + struct dbr_ctrl_float cfltval; /* control float info */ + struct dbr_ctrl_enum cenmval; /* control item info */ + struct dbr_ctrl_char cchrval; /* control char info */ + struct dbr_ctrl_long clngval; /* control long info */ + struct dbr_ctrl_double cdblval; /* control double info */ + dbr_put_ackt_t putackt; /* item number */ + dbr_put_acks_t putacks; /* item number */ + struct dbr_sts_string sastrval; /* string field with status */ + dbr_string_t classname; /* string max size */ +}; + +/*---------------------------------------------------------------------------- +* repository for some useful PV database constants and utilities +* +* item dimensions +* db_strval_dim dimension for string values +* db_units_dim dimension for record units text +* db_desc_dim dimension for record description text +* db_name_dim dimension for channel names (record.field\0) +* db_state_dim number of states possible in a state table +* db_state_text_dim dimension for a state text string +* usage: char state_table[db_state_dim][db_state_text_dim] +* +* type checking macros -- return non-zero if condition is true, zero otherwise +* +* int dbf_type_is_valid(type) type is a valid DBF_xxx +* int dbr_type_is_valid(type) type is a valid DBR_xxx +* int dbr_type_is_plain(type) type is a valid plain DBR_xxx +* int dbr_type_is_STS(type) type is a valid DBR_STS_xxx +* int dbr_type_is_TIME(type) type is a valid DBR_TIME_xxx +* int dbr_type_is_GR(type) type is a valid DBR_GR_xxx +* int dbr_type_is_CTRL(type) type is a valid DBR_CTRL_xxx +* int dbr_type_is_STRING(type) type is a valid DBR_STRING_xxx +* int dbr_type_is_SHORT(type) type is a valid DBR_SHORT_xxx +* int dbr_type_is_FLOAT(type) type is a valid DBR_FLOAT_xxx +* int dbr_type_is_ENUM(type) type is a valid DBR_ENUM_xxx +* int dbr_type_is_CHAR(type) type is a valid DBR_CHAR_xxx +* int dbr_type_is_LONG(type) type is a valid DBR_LONG_xxx +* int dbr_type_is_DOUBLE(type) type is a valid DBR_DOUBLE_xxx +* +* type conversion macros +* +* char *dbf_type_to_text(type) returns text matching DBF_xxx +* void dbf_text_to_type(text, type) finds DBF_xxx matching text +* int dbf_type_to_DBR(type) returns DBR_xxx matching DBF_xxx +* int dbf_type_to_DBR_TIME(type) returns DBR_TIME_xxx matching DBF_xxx +* int dbf_type_to_DBR_GR(type) returns DBR_GR_xxx matching DBF_xxx +* int dbf_type_to_DBR_CTRL(type) returns DBR_CTRL_xxx matching DBF_xxx +* char *dbr_type_to_text(type) returns text matching DBR_xxx +* void dbr_text_to_type(text, type) finds DBR_xxx matching text +*---------------------------------------------------------------------------*/ +#define db_strval_dim MAX_STRING_SIZE +#define db_units_dim MAX_UNITS_SIZE +#define db_desc_dim 24 +#define db_name_dim 36 +#define db_state_dim MAX_ENUM_STATES +#define db_state_text_dim MAX_ENUM_STRING_SIZE + +#define dbf_type_is_valid(type) ((type) >= 0 && (type) <= LAST_TYPE) +#define dbr_type_is_valid(type) ((type) >= 0 && (type) <= LAST_BUFFER_TYPE) +#define dbr_type_is_plain(type) \ + ((type) >= DBR_STRING && (type) <= DBR_DOUBLE) +#define dbr_type_is_STS(type) \ + ((type) >= DBR_STS_STRING && (type) <= DBR_STS_DOUBLE) +#define dbr_type_is_TIME(type) \ + ((type) >= DBR_TIME_STRING && (type) <= DBR_TIME_DOUBLE) +#define dbr_type_is_GR(type) \ + ((type) >= DBR_GR_STRING && (type) <= DBR_GR_DOUBLE) +#define dbr_type_is_CTRL(type) \ + ((type) >= DBR_CTRL_STRING && (type) <= DBR_CTRL_DOUBLE) +#define dbr_type_is_STRING(type) \ + ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ + (type)%(LAST_TYPE+1) == DBR_STRING) +#define dbr_type_is_SHORT(type) \ + ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ + (type)%(LAST_TYPE+1) == DBR_SHORT) +#define dbr_type_is_FLOAT(type) \ + ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ + (type)%(LAST_TYPE+1) == DBR_FLOAT) +#define dbr_type_is_ENUM(type) \ + ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ + (type)%(LAST_TYPE+1) == DBR_ENUM) +#define dbr_type_is_CHAR(type) \ + ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ + (type)%(LAST_TYPE+1) == DBR_CHAR) +#define dbr_type_is_LONG(type) \ + ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ + (type)%(LAST_TYPE+1) == DBR_LONG) +#define dbr_type_is_DOUBLE(type) \ + ((type) >= 0 && (type) <= LAST_BUFFER_TYPE && \ + (type)%(LAST_TYPE+1) == DBR_DOUBLE) + +#define dbf_type_to_text(type) \ + ( ((type) >= -1 && (type) < dbf_text_dim-2) ? \ + dbf_text[type+1] : dbf_text_invalid ) + +#define dbf_text_to_type(text, type) \ + for (type=dbf_text_dim-3; type>=0; type--) { \ + if (strcmp(text, dbf_text[type+1]) == 0) \ + break; \ + } + +#define dbr_type_to_text(type) \ + ( ((type) >= 0 && (type) < dbr_text_dim) ? \ + dbr_text[(type)] : dbr_text_invalid ) + +#define dbr_text_to_type(text, type) \ + for (type=dbr_text_dim-2; type>=0; type--) { \ + if (strcmp(text, dbr_text[type]) == 0) \ + break; \ + } + +#define dbf_type_to_DBR(type) \ + (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ + (type) : -1 ) + +#define dbf_type_to_DBR_STS(type) \ + (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ + (type) + (dbf_text_dim-2) : -1 ) + +#define dbf_type_to_DBR_TIME(type) \ + (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ + (type) + 2*(dbf_text_dim-2) : -1 ) + +#define dbf_type_to_DBR_GR(type) \ + (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ + (type) + 3*(dbf_text_dim-2) : -1 ) + +#define dbf_type_to_DBR_CTRL(type) \ + (((type) >= 0 && (type) <= dbf_text_dim-3) ? \ + (type) + 4*(dbf_text_dim-2) : -1 ) + + +epicsShareExtern const char *dbf_text[LAST_TYPE+3]; +epicsShareExtern const short dbf_text_dim; +epicsShareExtern const char *dbf_text_invalid; + +epicsShareExtern const char *dbr_text[LAST_BUFFER_TYPE+1]; +epicsShareExtern const short dbr_text_dim; +epicsShareExtern const char *dbr_text_invalid; +#endif /*db_accessHFORdb_accessC*/ + +#ifdef __cplusplus +} +#endif + +#endif /* INCLdb_accessh */ diff --git a/src/ca/disconnectGovernorTimer.cpp b/src/ca/disconnectGovernorTimer.cpp new file mode 100644 index 000000000..755b29cb3 --- /dev/null +++ b/src/ca/disconnectGovernorTimer.cpp @@ -0,0 +1,110 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// +// L O S A L A M O S +// Los Alamos National Laboratory +// Los Alamos, New Mexico 87545 +// +// Copyright, 1986, The Regents of the University of California. +// +// Author: Jeff Hill +// + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#define epicsExportSharedSymbols +#include "disconnectGovernorTimer.h" +#include "udpiiu.h" +#include "nciu.h" + +static const double disconnectGovernorPeriod = 10.0; // sec + +disconnectGovernorTimer::disconnectGovernorTimer ( + disconnectGovernorNotify & iiuIn, + epicsTimerQueue & queueIn, + epicsMutex & mutexIn ) : + mutex ( mutexIn ), timer ( queueIn.createTimer () ), + iiu ( iiuIn ) +{ +} + +disconnectGovernorTimer::~disconnectGovernorTimer () +{ + this->timer.destroy (); +} + +void disconnectGovernorTimer:: start () +{ + this->timer.start ( *this, disconnectGovernorPeriod ); +} + +void disconnectGovernorTimer::shutdown ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); + this->timer.cancel (); + } + } + while ( nciu * pChan = this->chanList.get () ) { + pChan->channelNode::listMember = + channelNode::cs_none; + pChan->serviceShutdownNotify ( cbGuard, guard ); + } +} + +epicsTimerNotify::expireStatus disconnectGovernorTimer::expire ( + const epicsTime & /* currentTime */ ) // X aCC 361 +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + while ( nciu * pChan = chanList.get () ) { + pChan->channelNode::listMember = + channelNode::cs_none; + this->iiu.govExpireNotify ( guard, *pChan ); + } + return expireStatus ( restart, disconnectGovernorPeriod ); +} + +void disconnectGovernorTimer::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + ::printf ( "disconnect governor timer: with %u channels pending\n", + this->chanList.count () ); + if ( level > 0u ) { + tsDLIterConst < nciu > pChan = this->chanList.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 1u ); + pChan++; + } + } +} + +void disconnectGovernorTimer::installChan ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->chanList.add ( chan ); + chan.channelNode::listMember = channelNode::cs_disconnGov; +} + +void disconnectGovernorTimer::uninstallChan ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->chanList.remove ( chan ); + chan.channelNode::listMember = channelNode::cs_none; +} + +disconnectGovernorNotify::~disconnectGovernorNotify () {} + diff --git a/src/ca/disconnectGovernorTimer.h b/src/ca/disconnectGovernorTimer.h new file mode 100644 index 000000000..06b8a36a8 --- /dev/null +++ b/src/ca/disconnectGovernorTimer.h @@ -0,0 +1,77 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// +// +// +// L O S A L A M O S +// Los Alamos National Laboratory +// Los Alamos, New Mexico 87545 +// +// Copyright, 1986, The Regents of the University of California. +// +// +// Author Jeffrey O. Hill +// johill@lanl.gov +// 505 665 1831 +// + +#ifndef disconnectGovernorTimerh +#define disconnectGovernorTimerh + +#ifdef epicsExportSharedSymbols +# define searchTimerh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsTimer.h" + +#ifdef searchTimerh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "caProto.h" +#include "netiiu.h" + +class disconnectGovernorNotify { // X aCC 655 +public: + virtual ~disconnectGovernorNotify () = 0; + virtual void govExpireNotify ( + epicsGuard < epicsMutex > &, nciu & ) = 0; +}; + +class disconnectGovernorTimer : private epicsTimerNotify { +public: + disconnectGovernorTimer ( + class disconnectGovernorNotify &, epicsTimerQueue &, epicsMutex & ); + virtual ~disconnectGovernorTimer (); + void start (); + void shutdown ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void installChan ( + epicsGuard < epicsMutex > &, nciu & ); + void uninstallChan ( + epicsGuard < epicsMutex > &, nciu & ); + void show ( unsigned level ) const; +private: + tsDLList < nciu > chanList; + epicsMutex & mutex; + epicsTimer & timer; + class disconnectGovernorNotify & iiu; + epicsTimerNotify::expireStatus expire ( const epicsTime & currentTime ); + disconnectGovernorTimer ( const disconnectGovernorTimer & ); + disconnectGovernorTimer & operator = ( const disconnectGovernorTimer & ); +}; + +#endif // ifdef disconnectGovernorTimerh diff --git a/src/ca/evtime.c b/src/ca/evtime.c new file mode 100644 index 000000000..d5cf58382 --- /dev/null +++ b/src/ca/evtime.c @@ -0,0 +1,95 @@ +/*************************************************************************\ +* 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 + +#include "dbDefs.h" +#include "epicsTime.h" +#include "cadef.h" + +void event_handler (struct event_handler_args args); +int evtime (char *pname); + +static unsigned iteration_count; +static epicsUInt32 last_time; + +#ifndef iocCore +int main(int argc, char **argv) +{ + char *pname; + + if(argc == 2){ + pname = argv[1]; + evtime(pname); + } + else{ + printf("usage: %s ", argv[0]); + } + return(0); +} +#endif + +/* + * evtime() + */ +int evtime(char *pname) +{ + chid chan; + int status; + + status = ca_search(pname, &chan); + SEVCHK(status, NULL); + + status = ca_pend_io(10.0); + if(status != ECA_NORMAL){ + printf("%s not found\n", pname); + return 0; + } + + status = ca_add_event( + DBR_FLOAT, + chan, + event_handler, + NULL, + NULL); + SEVCHK(status, __FILE__); + + status = ca_pend_event(0.0); + SEVCHK(status, NULL); +} + + +/* + * event_handler() + * + */ +void event_handler(struct event_handler_args args) +{ + epicsUInt32 current_time; +# define COUNT 0x8000 + double interval; + double delay; + epicsTimeStamp ts; + + if(iteration_count%COUNT == 0){ + epicsTimeGetCurrent(&ts); + current_time = ts.secPastEpoch; + if(last_time != 0){ + interval = current_time - last_time; + delay = interval/COUNT; + printf("Delay = %f sec per event\n", + delay); + } + last_time = current_time; + } + + iteration_count++; +} + diff --git a/src/ca/future_work.txt b/src/ca/future_work.txt new file mode 100644 index 000000000..3f3cb3317 --- /dev/null +++ b/src/ca/future_work.txt @@ -0,0 +1,38 @@ + +Potential upgrades to Channel Access + +o generalized access to abstract data +o use multicast for broadcasting and ax the repeater +o improve client API +o improve security +o name service (wildcard queries) +o PV name prefix (for all PVs) +o client access to process passive and for maximize severity +o detect name conflicts at boot time +o multi priority connections (quality of service) +o reduce protocol overhead +o compressed protocol + + +o If there is a beacon anomaly then this indicates that +the server is _not_ available. Perhaps we should only +reset the search timer interval when we see a beacon +anomaly transition into a sure steady beacon. + +o make certain that a monitor callback canceling itself +does not deadlock. + +o the free list library does not now cause exceptions to +occur (it uses new ( nothrow )), and therefore we should +change the new handlers to be nothrow also if this is +the design goal. + +o test the library when an IOC is running low on memory. + +o The new CA interface should be multi-threaded by default. +We should continue to support a single-threaded interface, +but this would should be restricted so that it always +runs with preemptive callback disable and no threads +may join in. Alternatively, the new CA interface could +be 100% preemptive multi-threaded and the old interface +could be layered on this. diff --git a/src/ca/getCallback.cpp b/src/ca/getCallback.cpp new file mode 100644 index 000000000..e95433dd5 --- /dev/null +++ b/src/ca/getCallback.cpp @@ -0,0 +1,111 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" + +getCallback::getCallback ( oldChannelNotify & chanIn, + caEventCallBackFunc *pFuncIn, void *pPrivateIn ) : + chan ( chanIn ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ) +{ +} + +getCallback::~getCallback () +{ +} + +void getCallback::completion ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount count, const void *pData ) +{ + struct event_handler_args args; + args.usr = this->pPrivate; + args.chid = & this->chan; + args.type = type; + args.count = count; + args.status = ECA_NORMAL; + args.dbr = pData; + caEventCallBackFunc * pFuncTmp = this->pFunc; + // fetch client context and destroy prior to releasing + // the lock and calling cb in case they destroy channel there + this->chan.getClientCtx().destroyGetCallback ( guard, *this ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } +} + +void getCallback::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char * /* pContext */, + unsigned type, arrayElementCount count ) +{ + if ( status != ECA_CHANDESTROY ) { + struct event_handler_args args; + args.usr = this->pPrivate; + args.chid = & this->chan; + args.type = type; + args.count = count; + args.status = status; + args.dbr = 0; + caEventCallBackFunc * pFuncTmp = this->pFunc; + // fetch client context and destroy prior to releasing + // the lock and calling cb in case they destroy channel there + this->chan.getClientCtx().destroyGetCallback ( guard, *this ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } + } + else { + this->chan.getClientCtx().destroyGetCallback ( guard, *this ); + } +} + +void * getCallback::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void getCallback::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + + diff --git a/src/ca/getCopy.cpp b/src/ca/getCopy.cpp new file mode 100644 index 000000000..978676ef0 --- /dev/null +++ b/src/ca/getCopy.cpp @@ -0,0 +1,125 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "errlog.h" + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" +#include "cac.h" + +getCopy::getCopy ( + epicsGuard < epicsMutex > & guard, ca_client_context & cacCtxIn, + oldChannelNotify & chanIn, unsigned typeIn, + arrayElementCount countIn, void * pValueIn ) : + count ( countIn ), cacCtx ( cacCtxIn ), chan ( chanIn ), pValue ( pValueIn ), + ioSeqNo ( 0 ), type ( typeIn ) +{ + this->ioSeqNo = cacCtxIn.sequenceNumberOfOutstandingIO ( guard ); + cacCtxIn.incrementOutstandingIO ( guard, this->ioSeqNo ); +} + +getCopy::~getCopy () +{ +} + +void getCopy::cancel () +{ + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef () ); + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); +} + +void getCopy::completion ( + epicsGuard < epicsMutex > & guard, unsigned typeIn, + arrayElementCount countIn, const void *pDataIn ) +{ + if ( this->type == typeIn ) { + unsigned size = dbr_size_n ( typeIn, countIn ); + memcpy ( this->pValue, pDataIn, size ); + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); + this->cacCtx.destroyGetCopy ( guard, *this ); + // this object destroyed by preceding function call + } + else { + this->exception ( guard, ECA_INTERNAL, + "bad data type match in get copy back response", + typeIn, countIn); + // this object destroyed by preceding function call + } +} + +void getCopy::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char *pContext, + unsigned /* typeIn */, arrayElementCount /* countIn */ ) +{ + oldChannelNotify & chanTmp ( this->chan ); + unsigned typeTmp ( this->type ); + arrayElementCount countTmp ( this->count ); + ca_client_context & caClientCtx ( this->cacCtx ); + // fetch client context and destroy prior to releasing + // the lock and calling cb in case they destroy channel there + this->cacCtx.destroyGetCopy ( guard, *this ); + if ( status != ECA_CHANDESTROY ) { + caClientCtx.exception ( guard, status, pContext, + __FILE__, __LINE__, chanTmp, typeTmp, + countTmp, CA_OP_GET ); + } +} + +void getCopy::show ( unsigned level ) const +{ + int tmpType = static_cast ( this->type ); + ::printf ( "read copy IO at %p, type %s, element count %lu\n", + static_cast ( this ), dbf_type_to_text ( tmpType ), this->count ); + if ( level > 0u ) { + ::printf ( "\tIO sequence number %u, user's storage %p\n", + this->ioSeqNo, static_cast ( this->pValue ) ); + } +} + +void * getCopy::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void getCopy::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + diff --git a/src/ca/hostNameCache.cpp b/src/ca/hostNameCache.cpp new file mode 100644 index 000000000..c3d105c06 --- /dev/null +++ b/src/ca/hostNameCache.cpp @@ -0,0 +1,92 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#include "hostNameCache.h" +#include "epicsGuard.h" + +hostNameCache::hostNameCache ( + const osiSockAddr & addr, ipAddrToAsciiEngine & engine ) : + dnsTransaction ( engine.createTransaction() ), nameLength ( 0 ) +{ + sockAddrToDottedIP ( &addr.sa, hostNameBuf, sizeof ( hostNameBuf ) ); + hostNameBuf[ sizeof ( hostNameBuf ) - 1 ] = '\0'; + nameLength = strlen ( hostNameBuf ); + this->dnsTransaction.ipAddrToAscii ( addr, *this ); +} + +void hostNameCache::destroy () +{ + delete this; +} + +hostNameCache::~hostNameCache () +{ + this->dnsTransaction.release (); +} + +void hostNameCache::transactionComplete ( const char * pHostNameIn ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + // a few legacy clients have a direct pointer to this buffer so we + // set the entrire string to nill terminators before we start copying + // in the name (this reduces the chance that another thread will see + // garbage characters). + size_t newNameLen = strlen ( pHostNameIn ); + if ( newNameLen > sizeof ( this->hostNameBuf ) - 1u ) { + newNameLen = sizeof ( this->hostNameBuf ) - 1u; + } + strncpy ( this->hostNameBuf, "", sizeof ( this->hostNameBuf ) ); + strncpy ( this->hostNameBuf, pHostNameIn, sizeof ( this->hostNameBuf ) - 1 ); + this->nameLength = newNameLen; +} + +unsigned hostNameCache::getName ( + char * pBuf, unsigned bufSize ) const +{ + if ( bufSize == 0u ) { + return 0u; + } + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->nameLength > 0u ) { + if ( this->nameLength < bufSize ) { + strcpy ( pBuf, this->hostNameBuf ); + return this->nameLength; + } + else { + unsigned reducedSize = bufSize - 1u; + strncpy ( pBuf, this->hostNameBuf, reducedSize ); + pBuf [ reducedSize ] = '\0'; + return reducedSize; + } + } + else { + osiSockAddr tmpAddr = this->dnsTransaction.address (); + return sockAddrToDottedIP ( &tmpAddr.sa, pBuf, bufSize ); + } +} + diff --git a/src/ca/hostNameCache.h b/src/ca/hostNameCache.h new file mode 100644 index 000000000..a4eacfbb3 --- /dev/null +++ b/src/ca/hostNameCache.h @@ -0,0 +1,61 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef hostNameCacheh +#define hostNameCacheh + +#ifdef epicsExportSharedSymbols +# define hostNameCache_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "ipAddrToAsciiAsynchronous.h" +#include "epicsMutex.h" + +#ifdef hostNameCache_epicsExportSharedSymbols +# define epicsExportSharedSymbols +#endif + +class hostNameCache : public ipAddrToAsciiCallBack { +public: + hostNameCache ( const osiSockAddr & addr, ipAddrToAsciiEngine & engine ); + ~hostNameCache (); + void destroy (); + void transactionComplete ( const char * pHostName ); + unsigned getName ( char *pBuf, unsigned bufLength ) const; + const char * pointer () const; +private: + char hostNameBuf [128]; + mutable epicsMutex mutex; + ipAddrToAsciiTransaction & dnsTransaction; + unsigned nameLength; +}; + +inline const char * hostNameCache::pointer () const +{ + return this->hostNameBuf; +} + +#endif // #ifndef hostNameCacheh diff --git a/src/ca/inetAddrID.h b/src/ca/inetAddrID.h new file mode 100644 index 000000000..978787599 --- /dev/null +++ b/src/ca/inetAddrID.h @@ -0,0 +1,72 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#ifndef inetAddrIDh +#define inetAddrIDh + +#include "osiSock.h" +#include "resourceLib.h" + +class inetAddrID { +public: + inetAddrID ( const struct sockaddr_in & addrIn ); + bool operator == ( const inetAddrID & ) const; + resTableIndex hash () const; + void name ( char *pBuf, unsigned bufSize ) const; +private: + struct sockaddr_in addr; +}; + +inline inetAddrID::inetAddrID ( const struct sockaddr_in & addrIn ) : + addr ( addrIn ) +{ +} + +inline bool inetAddrID::operator == ( const inetAddrID &rhs ) const +{ + if ( this->addr.sin_addr.s_addr == rhs.addr.sin_addr.s_addr ) { + if ( this->addr.sin_port == rhs.addr.sin_port ) { + return true; + } + } + return false; +} + +inline resTableIndex inetAddrID::hash () const +{ + const unsigned inetAddrMinIndexBitWidth = 8u; + const unsigned inetAddrMaxIndexBitWidth = 32u; + unsigned index; + index = this->addr.sin_addr.s_addr; + index ^= this->addr.sin_port; + index ^= this->addr.sin_port >> 8u; + return integerHash ( inetAddrMinIndexBitWidth, + inetAddrMaxIndexBitWidth, index ); +} + +inline void inetAddrID::name ( char *pBuf, unsigned bufSize ) const +{ + ipAddrToDottedIP ( &this->addr, pBuf, bufSize ); +} + +#endif // ifdef inetAddrID + + diff --git a/src/ca/iocinf.cpp b/src/ca/iocinf.cpp new file mode 100644 index 000000000..d6fac8178 --- /dev/null +++ b/src/ca/iocinf.cpp @@ -0,0 +1,264 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include +#include +#include +#include +#include + +#include "envDefs.h" +#include "epicsAssert.h" +#include "errlog.h" +#include "osiWireFormat.h" + +#define epicsExportSharedSymbols +#include "addrList.h" +#undef epicsExportSharedSymbols + +#include "iocinf.h" + +/* + * getToken() + */ +static char *getToken ( const char **ppString, char *pBuf, unsigned bufSIze ) +{ + bool tokenFound = false; + const char *pToken; + unsigned i; + + pToken = *ppString; + while ( isspace (*pToken) && *pToken ){ + pToken++; + } + + for ( i=0u; iname); + fprintf ( stderr, "\tBad internet address or host name: '%s'\n", pToken); + continue; + } + + if ( ignoreNonDefaultPort && ntohs ( addr.sin_port ) != port ) { + continue; + } + + pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); + if (pNewNode==NULL) { + fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n"); + return; + } + + pNewNode->addr.ia = addr; + + /* + * LOCK applied externally + */ + ellAdd (pList, &pNewNode->node); + } + + return; +} + +/* + * removeDuplicateAddresses () + */ +extern "C" void epicsShareAPI removeDuplicateAddresses + ( ELLLIST *pDestList, ELLLIST *pSrcList, int silent ) +{ + ELLNODE *pRawNode; + + while ( (pRawNode = ellGet ( pSrcList ) ) ) { + STATIC_ASSERT ( offsetof (osiSockAddrNode, node) == 0 ); + osiSockAddrNode *pNode = reinterpret_cast ( pRawNode ); + osiSockAddrNode *pTmpNode; + + if ( pNode->addr.sa.sa_family == AF_INET ) { + + pTmpNode = (osiSockAddrNode *) ellFirst (pDestList); // X aCC 749 + while ( pTmpNode ) { + if (pTmpNode->addr.sa.sa_family == AF_INET) { + if ( pNode->addr.ia.sin_addr.s_addr == pTmpNode->addr.ia.sin_addr.s_addr && + pNode->addr.ia.sin_port == pTmpNode->addr.ia.sin_port ) { + if ( ! silent ) { + char buf[64]; + ipAddrToDottedIP ( &pNode->addr.ia, buf, sizeof (buf) ); + fprintf ( stderr, + "Warning: Duplicate EPICS CA Address list entry \"%s\" discarded\n", buf ); + } + free (pNode); + pNode = NULL; + break; + } + } + pTmpNode = (osiSockAddrNode *) ellNext (&pTmpNode->node); // X aCC 749 + } + if (pNode) { + ellAdd (pDestList, &pNode->node); + } + } + else { + ellAdd (pDestList, &pNode->node); + } + } +} + +/* + * forcePort () + */ +static void forcePort ( ELLLIST *pList, unsigned short port ) +{ + osiSockAddrNode *pNode; + + pNode = ( osiSockAddrNode * ) ellFirst ( pList ); // X aCC 749 + while ( pNode ) { + if ( pNode->addr.sa.sa_family == AF_INET ) { + pNode->addr.ia.sin_port = htons ( port ); + } + pNode = ( osiSockAddrNode * ) ellNext ( &pNode->node ); // X aCC 749 + } +} + + +/* + * configureChannelAccessAddressList () + */ +extern "C" void epicsShareAPI configureChannelAccessAddressList + ( ELLLIST *pList, SOCKET sock, unsigned short port ) +{ + ELLLIST tmpList; + char *pstr; + char yesno[32u]; + int yes; + + /* + * dont load the list twice + */ + assert ( ellCount (pList) == 0 ); // X aCC 392 + + ellInit ( &tmpList ); // X aCC 392 + + /* + * Check to see if the user has disabled + * initializing the search b-cast list + * from the interfaces found. + */ + yes = true; + pstr = envGetConfigParam ( &EPICS_CA_AUTO_ADDR_LIST, + sizeof (yesno), yesno ); + if ( pstr ) { + if ( strstr ( pstr, "no" ) || strstr ( pstr, "NO" ) ) { + yes = false; + } + } + + /* + * LOCK is for piiu->destAddr list + * (lock outside because this is used by the server also) + */ + if (yes) { + ELLLIST bcastList; + osiSockAddr addr; + ellInit ( &bcastList ); // X aCC 392 + addr.ia.sin_family = AF_UNSPEC; + osiSockDiscoverBroadcastAddresses ( &bcastList, sock, &addr ); + forcePort ( &bcastList, port ); + removeDuplicateAddresses ( &tmpList, &bcastList, 1 ); + if ( ellCount ( &tmpList ) == 0 ) { // X aCC 392 + osiSockAddrNode *pNewNode; + pNewNode = (osiSockAddrNode *) calloc ( 1, sizeof (*pNewNode) ); + if ( pNewNode ) { + /* + * if no interfaces found then look for local channels + * with the loop back interface + */ + pNewNode->addr.ia.sin_family = AF_INET; + pNewNode->addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); + pNewNode->addr.ia.sin_port = htons ( port ); + ellAdd ( &tmpList, &pNewNode->node ); + } + else { + errlogPrintf ( "configureChannelAccessAddressList(): no memory available for configuration\n" ); + } + } + } + addAddrToChannelAccessAddressList ( &tmpList, &EPICS_CA_ADDR_LIST, port, false ); + + removeDuplicateAddresses ( pList, &tmpList, 0 ); +} + + +/* + * printChannelAccessAddressList () + */ +extern "C" void epicsShareAPI printChannelAccessAddressList ( const ELLLIST *pList ) +{ + osiSockAddrNode *pNode; + + ::printf ( "Channel Access Address List\n" ); + pNode = (osiSockAddrNode *) ellFirst ( pList ); // X aCC 749 + while (pNode) { + char buf[64]; + ipAddrToA ( &pNode->addr.ia, buf, sizeof ( buf ) ); + ::printf ( "%s\n", buf ); + pNode = (osiSockAddrNode *) ellNext ( &pNode->node ); // X aCC 749 + } +} diff --git a/src/ca/iocinf.h b/src/ca/iocinf.h new file mode 100644 index 000000000..8a26fdbf5 --- /dev/null +++ b/src/ca/iocinf.h @@ -0,0 +1,77 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef INCiocinfh +#define INCiocinfh + +#ifdef DEBUG +# define debugPrintf(argsInParen) ::printf argsInParen +#else +# define debugPrintf(argsInParen) +#endif + +#if defined ( CLOCKS_PER_SEC ) +# define CAC_SIGNIFICANT_DELAY ( 1.0 / CLOCKS_PER_SEC ) +#else +# define CAC_SIGNIFICANT_DELAY (1.0 / 1000000u) +#endif + +/* + * these two control the period of connection verifies + * (echo requests) - CA_CONN_VERIFY_PERIOD - and how + * long we will wait for an echo reply before we + * give up and flag the connection for disconnect + * - CA_ECHO_TIMEOUT. + * + * CA_CONN_VERIFY_PERIOD is normally obtained from an + * EPICS environment variable. + */ +static const double CA_ECHO_TIMEOUT = 5.0; /* (sec) disconn no echo reply tmo */ +static const double CA_CONN_VERIFY_PERIOD = 30.0; /* (sec) how often to request echo */ + +/* + * this determines the number of messages received + * without a delay in between before we go into + * monitor flow control + * + * turning this down effects maximum throughput + * because we dont get an optimal number of bytes + * per network frame + */ +static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u; + +class caErrorCode { +public: + caErrorCode ( int status ) : code ( status ) {}; +private: + int code; +}; + +/* + * CA internal functions + */ +#define genLocalExcep( CBGUARD, GUARD, CAC, STAT, PCTX ) \ +(CAC).exception ( CBGUARD, GUARD, STAT, PCTX, __FILE__, __LINE__ ) + +#endif // ifdef INCiocinfh diff --git a/src/ca/localHostName.cpp b/src/ca/localHostName.cpp new file mode 100644 index 000000000..b0b96bb47 --- /dev/null +++ b/src/ca/localHostName.cpp @@ -0,0 +1,66 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "osiSock.h" + +#include "localHostName.h" + +epicsSingleton < localHostName > localHostNameCache; + +localHostName::localHostName () : + attachedToSockLib ( osiSockAttach () != 0 ), length ( 0u ) +{ + const char * pErrStr = ""; + int status = -1; + if ( this->attachedToSockLib ) { + status = gethostname ( + this->cache, sizeof ( this->cache ) ); + } + if ( status ) { + strncpy ( this->cache, pErrStr, sizeof ( this->cache ) ); + } + this->cache [ sizeof ( this->cache ) - 1u ] = '\0'; + this->length = strlen ( this->cache ); +} + +localHostName::~localHostName () +{ + if ( this->attachedToSockLib ) { + osiSockRelease (); + } +} + +unsigned localHostName::getName ( + char * pBuf, unsigned bufLength ) const +{ + if ( bufLength ) { + strncpy ( pBuf, this->cache, bufLength ); + if ( this->length < bufLength ) { + return this->length; + } + else { + unsigned reducedSize = bufLength - 1; + pBuf [ reducedSize ] = '\0'; + return reducedSize; + } + } + return 0u; +} diff --git a/src/ca/localHostName.h b/src/ca/localHostName.h new file mode 100644 index 000000000..f116b8140 --- /dev/null +++ b/src/ca/localHostName.h @@ -0,0 +1,65 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#ifndef localHostNameh +#define localHostNameh + +#include + +#ifdef epicsExportSharedSymbols +# define localHostNameh_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsSingleton.h" + +#ifdef localHostNameh_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +#endif + +class localHostName { +public: + localHostName (); + ~localHostName (); + const char * pointer () const; + unsigned getName ( char * pBuf, unsigned bufLength ) const; + unsigned nameLength () const; +private: + bool attachedToSockLib; + unsigned length; + char cache [128]; +}; + +extern epicsSingleton < localHostName > localHostNameCache; + +inline unsigned localHostName::nameLength () const +{ + return this->length; +} + +inline const char * localHostName::pointer () const +{ + return this->cache; +} + +#endif // ifndef localHostNameh + + diff --git a/src/ca/msgForMultiplyDefinedPV.cpp b/src/ca/msgForMultiplyDefinedPV.cpp new file mode 100644 index 000000000..516bb63bb --- /dev/null +++ b/src/ca/msgForMultiplyDefinedPV.cpp @@ -0,0 +1,98 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "msgForMultiplyDefinedPV.h" +#include "cac.h" +#include "caerr.h" // for ECA_DBLCHNL + +msgForMultiplyDefinedPV::msgForMultiplyDefinedPV ( + ipAddrToAsciiEngine & engine, + callbackForMultiplyDefinedPV & cbIn, + const char * pChannelName, const char * pAcc ) : + dnsTransaction ( engine.createTransaction () ), cb ( cbIn ) +{ + strncpy ( this->acc, pAcc, sizeof ( this->acc ) ); + this->acc[ sizeof ( this->acc ) - 1 ] = '\0'; + strncpy ( this->channel, pChannelName, sizeof ( this->channel ) ); + this->channel[ sizeof ( this->channel ) - 1 ] = '\0'; +} + +msgForMultiplyDefinedPV::~msgForMultiplyDefinedPV () +{ + this->dnsTransaction.release (); +} + +void msgForMultiplyDefinedPV::transactionComplete ( const char * pHostNameRej ) +{ + this->cb.pvMultiplyDefinedNotify ( *this, this->channel, this->acc, pHostNameRej ); + // !! dont touch this pointer after this point because object has been deleted !! +} + +void * msgForMultiplyDefinedPV::operator new ( size_t size, + tsFreeList < class msgForMultiplyDefinedPV, 16 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +void msgForMultiplyDefinedPV::operator delete ( void *pCadaver, + tsFreeList < class msgForMultiplyDefinedPV, 16 > & freeList ) +{ + freeList.release ( pCadaver, sizeof ( msgForMultiplyDefinedPV ) ); +} +#endif + +void * msgForMultiplyDefinedPV::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void msgForMultiplyDefinedPV::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +callbackForMultiplyDefinedPV::~callbackForMultiplyDefinedPV () +{ +} + + diff --git a/src/ca/msgForMultiplyDefinedPV.h b/src/ca/msgForMultiplyDefinedPV.h new file mode 100644 index 000000000..545adc815 --- /dev/null +++ b/src/ca/msgForMultiplyDefinedPV.h @@ -0,0 +1,77 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef msgForMultiplyDefinedPVh +#define msgForMultiplyDefinedPVh + +#ifdef epicsExportSharedSymbols +# define msgForMultiplyDefinedPVh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "ipAddrToAsciiAsynchronous.h" +#include "tsFreeList.h" +#include "compilerDependencies.h" + +#ifdef msgForMultiplyDefinedPVh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +#endif + +class callbackForMultiplyDefinedPV { // X aCC 655 +public: + virtual ~callbackForMultiplyDefinedPV () = 0; + virtual void pvMultiplyDefinedNotify ( + class msgForMultiplyDefinedPV &, const char * pChannelName, + const char * pAcc, const char * pRej ) = 0; +}; + +class msgForMultiplyDefinedPV : public ipAddrToAsciiCallBack { +public: + msgForMultiplyDefinedPV ( ipAddrToAsciiEngine & engine, + callbackForMultiplyDefinedPV &, const char * pChannelName, + const char * pAcc ); + virtual ~msgForMultiplyDefinedPV (); + void ioInitiate ( const osiSockAddr & rej ); + void * operator new ( size_t size, tsFreeList < class msgForMultiplyDefinedPV, 16 > & ); + epicsPlacementDeleteOperator (( void *, tsFreeList < class msgForMultiplyDefinedPV, 16 > & )) +private: + char acc[64]; + char channel[64]; + ipAddrToAsciiTransaction & dnsTransaction; + callbackForMultiplyDefinedPV & cb; + void transactionComplete ( const char * pHostName ); + msgForMultiplyDefinedPV ( const msgForMultiplyDefinedPV & ); + msgForMultiplyDefinedPV & operator = ( const msgForMultiplyDefinedPV & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +inline void msgForMultiplyDefinedPV::ioInitiate ( const osiSockAddr & rej ) +{ + this->dnsTransaction.ipAddrToAscii ( rej, *this ); +} + +#endif // ifdef msgForMultiplyDefinedPVh + diff --git a/src/ca/nciu.cpp b/src/ca/nciu.cpp new file mode 100644 index 000000000..c5ae941b2 --- /dev/null +++ b/src/ca/nciu.cpp @@ -0,0 +1,628 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + * + */ + +#include +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "epicsAlgorithm.h" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "cac.h" +#include "osiWireFormat.h" +#include "udpiiu.h" +#include "virtualCircuit.h" +#include "cadef.h" +#include "db_access.h" // for INVALID_DB_REQ +#include "noopiiu.h" + +nciu::nciu ( cac & cacIn, netiiu & iiuIn, cacChannelNotify & chanIn, + const char *pNameIn, cacChannel::priLev pri ) : + cacChannel ( chanIn ), + cacCtx ( cacIn ), + piiu ( & iiuIn ), + sid ( UINT_MAX ), + count ( 0 ), + retry ( 0u ), + nameLength ( 0u ), + typeCode ( USHRT_MAX ), + priority ( static_cast ( pri ) ) +{ + size_t nameLengthTmp = strlen ( pNameIn ) + 1; + + // second constraint is imposed by size field in protocol header + if ( nameLengthTmp > MAX_UDP_SEND - sizeof ( caHdr ) || nameLengthTmp > USHRT_MAX ) { + throw cacChannel::badString (); + } + + if ( pri > 0xff ) { + throw cacChannel::badPriority (); + } + + this->nameLength = static_cast ( nameLengthTmp ); + + this->pNameStr = new char [ this->nameLength ]; + strcpy ( this->pNameStr, pNameIn ); +} + +nciu::~nciu () +{ + delete [] this->pNameStr; +} + +// channels are created by the user, and only destroyed by the user +// using this routine +void nciu::destroy ( + epicsGuard < epicsMutex > & guard ) +{ + while ( baseNMIU * pNetIO = this->eventq.first () ) { + bool success = this->cacCtx.destroyIO ( guard, pNetIO->getId (), *this ); + assert ( success ); + } + + // if the claim reply has not returned yet then we will issue + // the clear channel request to the server when the claim reply + // arrives and there is no matching nciu in the client + if ( this->channelNode::isInstalledInServer ( guard ) ) { + this->getPIIU(guard)->clearChannelRequest ( + guard, this->sid, this->id ); + } + this->piiu->uninstallChan ( guard, *this ); + this->cacCtx.destroyChannel ( guard, *this ); +} + +void * nciu::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void nciu::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void nciu::initiateConnect ( + epicsGuard < epicsMutex > & guard ) +{ + this->cacCtx.initiateConnect ( guard, *this, this->piiu ); +} + +void nciu::connect ( unsigned nativeType, + unsigned nativeCount, unsigned sidIn, + epicsGuard < epicsMutex > & /* cbGuard */, + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + if ( ! dbf_type_is_valid ( nativeType ) ) { + throw std::logic_error ( "Ignored conn resp with bad native data type" ); + } + + this->typeCode = static_cast < unsigned short > ( nativeType ); + this->count = nativeCount; + this->sid = sidIn; + + /* + * if less than v4.1 then the server will never + * send access rights and there will always be access + */ + bool v41Ok = this->piiu->ca_v41_ok ( guard ); + if ( ! v41Ok ) { + this->accessRightState.setReadPermit(); + this->accessRightState.setWritePermit(); + } + + /* + * if less than v4.1 then the server will never + * send access rights and we know that there + * will always be access and also need to call + * their call back here + */ + if ( ! v41Ok ) { + this->notify().accessRightsNotify ( + guard, this->accessRightState ); + } + + // channel uninstal routine grabs the callback lock so + // a channel will not be deleted while a call back is + // in progress + // + // the callback lock is also taken when a channel + // disconnects to prevent a race condition with the + // code below - ie we hold the callback lock here + // so a chanel cant be destroyed out from under us. + this->notify().connectNotify ( guard ); +} + +void nciu::unresponsiveCircuitNotify ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + ioid tmpId = this->getId (); + cac & caRefTmp = this->cacCtx; + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + this->cacCtx.disconnectAllIO ( cbGuard, guard, + *this, this->eventq ); + this->notify().disconnectNotify ( guard ); + // if they destroy the channel in their disconnect + // handler then we have to be very careful to not + // touch this object if it has been destroyed + nciu * pChan = caRefTmp.lookupChannel ( guard, tmpId ); + if ( pChan ) { + caAccessRights noRights; + pChan->notify().accessRightsNotify ( guard, noRights ); + // likewise, they might destroy the channel in their access rights + // handler so we have to be very careful to not touch this + // object from here on down + } +} + +void nciu::setServerAddressUnknown ( netiiu & newiiu, + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + this->piiu = & newiiu; + this->retry = 0; + this->typeCode = USHRT_MAX; + this->count = 0u; + this->sid = UINT_MAX; + this->accessRightState.clrReadPermit(); + this->accessRightState.clrWritePermit(); +} + +void nciu::accessRightsStateChange ( + const caAccessRights & arIn, epicsGuard < epicsMutex > & /* cbGuard */, + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + this->accessRightState = arIn; + + // + // the channel delete routine takes the call back lock so + // that this will not be called when the channel is being + // deleted. + // + this->notify().accessRightsNotify ( guard, this->accessRightState ); +} + +/* + * nciu::searchMsg () + */ +bool nciu::searchMsg ( epicsGuard < epicsMutex > & guard ) +{ + bool success = this->piiu->searchMsg ( + guard, this->getId (), this->pNameStr, this->nameLength ); + if ( success ) { + if ( this->retry < UINT_MAX ) { + this->retry++; + } + } + return success; +} + +const char *nciu::pName ( + epicsGuard < epicsMutex > & guard ) const throw () +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + return this->pNameStr; +} + +unsigned nciu::getName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw () +{ + if ( bufLen == 0u ) { + return 0u; + } + if ( this->nameLength < bufLen ) { + strcpy ( pBuf, this->pNameStr ); + return this->nameLength; + } + else { + unsigned reducedSize = bufLen - 1u; + strncpy ( pBuf, this->pNameStr, bufLen ); + pBuf[reducedSize] = '\0'; + return reducedSize; + } +} + +unsigned nciu::nameLen ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + return this->nameLength; +} + +unsigned nciu::requestMessageBytesPending ( + epicsGuard < epicsMutex > & guard ) +{ + return piiu->requestMessageBytesPending ( guard ); +} + +void nciu::flush ( + epicsGuard < epicsMutex > & guard ) +{ + piiu->flush ( guard ); +} + +cacChannel::ioStatus nciu::read ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount countIn, + cacReadNotify ¬ify, ioid *pId ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + + if ( ! this->connected ( guard ) ) { + throw cacChannel::notConnected (); + } + if ( ! this->accessRightState.readPermit () ) { + throw cacChannel::noReadAccess (); + } + if ( countIn > this->count ) { + throw cacChannel::outOfBounds (); + } + + // + // fail out if their arguments are invalid + // + if ( INVALID_DB_REQ ( type ) ) { + throw cacChannel::badType (); + } + + netReadNotifyIO & io = this->cacCtx.readNotifyRequest ( + guard, *this, *this, type, countIn, notify ); + if ( pId ) { + *pId = io.getId (); + } + this->eventq.add ( io ); + return cacChannel::iosAsynch; +} + +void nciu::stringVerify ( const char *pStr, const unsigned count ) +{ + for ( unsigned i = 0; i < count; i++ ) { + unsigned int strsize = 0; + while ( pStr[strsize++] != '\0' ) { + if ( strsize >= MAX_STRING_SIZE ) { + throw badString(); + } + } + pStr += MAX_STRING_SIZE; + } +} + +void nciu::write ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount countIn, const void * pValue ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + + // make sure that they get this and not "no write access" + // if disconnected + if ( ! this->connected ( guard ) ) { + throw cacChannel::notConnected(); + } + if ( ! this->accessRightState.writePermit() ) { + throw cacChannel::noWriteAccess(); + } + if ( countIn > this->count || countIn == 0 ) { + throw cacChannel::outOfBounds(); + } + if ( type == DBR_STRING ) { + nciu::stringVerify ( (char *) pValue, countIn ); + } + this->piiu->writeRequest ( guard, *this, type, countIn, pValue ); +} + +cacChannel::ioStatus nciu::write ( + epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount countIn, + const void * pValue, cacWriteNotify & notify, ioid * pId ) +{ + // make sure that they get this and not "no write access" + // if disconnected + if ( ! this->connected ( guard ) ) { + throw cacChannel::notConnected(); + } + if ( ! this->accessRightState.writePermit() ) { + throw cacChannel::noWriteAccess(); + } + if ( countIn > this->count || countIn == 0 ) { + throw cacChannel::outOfBounds(); + } + if ( type == DBR_STRING ) { + nciu::stringVerify ( (char *) pValue, countIn ); + } + + netWriteNotifyIO & io = this->cacCtx.writeNotifyRequest ( + guard, *this, *this, type, countIn, pValue, notify ); + if ( pId ) { + *pId = io.getId (); + } + this->eventq.add ( io ); + return cacChannel::iosAsynch; +} + +void nciu::subscribe ( + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount nElem, unsigned mask, + cacStateNotify & notify, ioid *pId ) +{ + netSubscription & io = this->cacCtx.subscriptionRequest ( + guard, *this, *this, type, nElem, mask, notify, + this->channelNode::isInstalledInServer ( guard ) ); + this->eventq.add ( io ); + if ( pId ) { + *pId = io.getId (); + } +} + +void nciu::ioCancel ( + epicsGuard < epicsMutex > & guard, const ioid & idIn ) +{ + this->cacCtx.destroyIO ( guard, idIn, *this ); +} + +void nciu::ioShow ( + epicsGuard < epicsMutex > & guard, + const ioid &idIn, unsigned level ) const +{ + this->cacCtx.ioShow ( guard, idIn, level ); +} + +unsigned nciu::getHostName ( + epicsGuard < epicsMutex > & guard, + char *pBuf, unsigned bufLength ) const throw () +{ + return this->piiu->getHostName ( + guard, pBuf, bufLength ); +} + +const char * nciu::pHostName ( + epicsGuard < epicsMutex > & guard ) const throw () +{ + return this->piiu->pHostName ( guard ); +} + +bool nciu::ca_v42_ok ( + epicsGuard < epicsMutex > & guard ) const +{ + return this->piiu->ca_v42_ok ( guard ); +} + +short nciu::nativeType ( + epicsGuard < epicsMutex > & guard ) const +{ + short type = TYPENOTCONN; + if ( this->connected ( guard ) ) { + if ( this->typeCode < SHRT_MAX ) { + type = static_cast ( this->typeCode ); + } + } + return type; +} + +arrayElementCount nciu::nativeElementCount ( + epicsGuard < epicsMutex > & guard ) const +{ + arrayElementCount countOut = 0ul; + if ( this->connected ( guard ) ) { + countOut = this->count; + } + return countOut; +} + +caAccessRights nciu::accessRights ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + return this->accessRightState; +} + +unsigned nciu::searchAttempts ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + return this->retry; +} + +double nciu::beaconPeriod ( + epicsGuard < epicsMutex > & guard ) const +{ + return this->cacCtx.beaconPeriod ( guard, *this ); +} + +double nciu::receiveWatchdogDelay ( + epicsGuard < epicsMutex > & guard ) const +{ + return this->piiu->receiveWatchdogDelay ( guard ); +} + +bool nciu::connected ( epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + return this->channelNode::isConnected ( guard ); +} + +void nciu::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->cacCtx.mutexRef() ); + this->show ( guard, level ); +} + +void nciu::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + if ( this->connected ( guard ) ) { + char hostNameTmp [256]; + this->getHostName ( guard, hostNameTmp, sizeof ( hostNameTmp ) ); + ::printf ( "Channel \"%s\", connected to server %s", + this->pNameStr, hostNameTmp ); + if ( level > 1u ) { + int tmpTypeCode = static_cast < int > ( this->typeCode ); + ::printf ( ", native type %s, native element count %u", + dbf_type_to_text ( tmpTypeCode ), this->count ); + ::printf ( ", %sread access, %swrite access", + this->accessRightState.readPermit() ? "" : "no ", + this->accessRightState.writePermit() ? "" : "no "); + } + ::printf ( "\n" ); + } + else { + ::printf ( "Channel \"%s\" is disconnected\n", this->pNameStr ); + } + + if ( level > 2u ) { + ::printf ( "\tnetwork IO pointer = %p\n", + static_cast ( this->piiu ) ); + ::printf ( "\tserver identifier %u\n", this->sid ); + ::printf ( "\tsearch retry number=%u\n", this->retry ); + ::printf ( "\tname length=%u\n", this->nameLength ); + } +} + +void nciu::ioCompletionNotify ( + epicsGuard < epicsMutex > &, class baseNMIU & io ) +{ + this->eventq.remove ( io ); +} + +void nciu::resubscribe ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + tsDLIter < baseNMIU > pNetIO = this->eventq.firstIter (); + while ( pNetIO.valid () ) { + tsDLIter < baseNMIU > next = pNetIO; + next++; + class netSubscription * pSubscr = pNetIO->isSubscription (); + // Its normal for other types of IO to exist after the channel connects, + // but before all of the resubscription requests go out. We must ignore + // them here. + if ( pSubscr ) { + try { + pSubscr->subscribeIfRequired ( guard, *this ); + } + catch ( ... ) { + errlogPrintf ( "CAC: failed to send subscription request " + "during channel connect\n" ); + } + } + pNetIO = next; + } +} + +void nciu::sendSubscriptionUpdateRequests ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + tsDLIter < baseNMIU > pNetIO = this->eventq.firstIter (); + while ( pNetIO.valid () ) { + tsDLIter < baseNMIU > next = pNetIO; + next++; + try { + pNetIO->forceSubscriptionUpdate ( guard, *this ); + } + catch ( ... ) { + errlogPrintf ( + "CAC: failed to send subscription update request " + "during channel connect\n" ); + } + pNetIO = next; + } +} + +void nciu::disconnectAllIO ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + this->cacCtx.disconnectAllIO ( cbGuard, guard, + *this, this->eventq ); +} + +void nciu::serviceShutdownNotify ( + epicsGuard < epicsMutex > & callbackControlGuard, + epicsGuard < epicsMutex > & mutualExclusionGuard ) +{ + this->setServerAddressUnknown ( noopIIU, mutualExclusionGuard ); + this->notify().serviceShutdownNotify ( mutualExclusionGuard ); +} + +void channelNode::setRespPendingState ( + epicsGuard < epicsMutex > &, unsigned index ) +{ + this->listMember = + static_cast < channelNode::channelState > + ( channelNode::cs_searchRespPending0 + index ); + if ( this->listMember > cs_searchRespPending17 ) { + throw std::runtime_error ( + "resp search timer index out of bounds" ); + } +} + +void channelNode::setReqPendingState ( + epicsGuard < epicsMutex > &, unsigned index ) +{ + this->listMember = + static_cast < channelNode::channelState > + ( channelNode::cs_searchReqPending0 + index ); + if ( this->listMember > cs_searchReqPending17 ) { + throw std::runtime_error ( + "req search timer index out of bounds" ); + } +} + +unsigned channelNode::getMaxSearchTimerCount () +{ + return epicsMin ( + cs_searchReqPending17 - cs_searchReqPending0, + cs_searchRespPending17 - cs_searchRespPending0 ) + 1u; +} + +unsigned channelNode::getSearchTimerIndex ( + epicsGuard < epicsMutex > & ) +{ + channelNode::channelState chanState = this->listMember; + unsigned index = 0u; + if ( chanState >= cs_searchReqPending0 && + chanState <= cs_searchReqPending17 ) { + index = chanState - cs_searchReqPending0; + } + else if ( chanState >= cs_searchRespPending0 && + chanState <= cs_searchRespPending17 ) { + index = chanState - cs_searchRespPending0; + } + else { + throw std::runtime_error ( + "channel was expected to be in a search timer, but wasnt" );; + } + return index; +} + diff --git a/src/ca/nciu.h b/src/ca/nciu.h new file mode 100644 index 000000000..de070dcea --- /dev/null +++ b/src/ca/nciu.h @@ -0,0 +1,376 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef nciuh +#define nciuh + +#ifdef epicsExportSharedSymbols +# define nciuh_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "resourceLib.h" +#include "tsDLList.h" +#include "tsFreeList.h" +#include "epicsMutex.h" +#include "compilerDependencies.h" + +#ifdef nciuh_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#define CA_MINOR_PROTOCOL_REVISION 13 +#include "caProto.h" + +#include "cacIO.h" + +class cac; +class netiiu; + +// The node and the state which tracks the list membership +// are in the channel, but belong to the circuit. +// Protected by the callback mutex +class channelNode : public tsDLNode < class nciu > +{ +protected: + channelNode (); + bool isInstalledInServer ( epicsGuard < epicsMutex > & ) const; + bool isConnected ( epicsGuard < epicsMutex > & ) const; + static unsigned getMaxSearchTimerCount (); +private: + enum channelState { + cs_none, + cs_disconnGov, + // note: indexing is used here + // so these must be contiguous + cs_searchReqPending0, + cs_searchReqPending1, + cs_searchReqPending2, + cs_searchReqPending3, + cs_searchReqPending4, + cs_searchReqPending5, + cs_searchReqPending6, + cs_searchReqPending7, + cs_searchReqPending8, + cs_searchReqPending9, + cs_searchReqPending10, + cs_searchReqPending11, + cs_searchReqPending12, + cs_searchReqPending13, + cs_searchReqPending14, + cs_searchReqPending15, + cs_searchReqPending16, + cs_searchReqPending17, + // note: indexing is used here + // so these must be contiguous + cs_searchRespPending0, + cs_searchRespPending1, + cs_searchRespPending2, + cs_searchRespPending3, + cs_searchRespPending4, + cs_searchRespPending5, + cs_searchRespPending6, + cs_searchRespPending7, + cs_searchRespPending8, + cs_searchRespPending9, + cs_searchRespPending10, + cs_searchRespPending11, + cs_searchRespPending12, + cs_searchRespPending13, + cs_searchRespPending14, + cs_searchRespPending15, + cs_searchRespPending16, + cs_searchRespPending17, + cs_createReqPend, + cs_createRespPend, + cs_v42ConnCallbackPend, + cs_subscripReqPend, + cs_connected, + cs_unrespCircuit, + cs_subscripUpdateReqPend + } listMember; + void setRespPendingState ( epicsGuard < epicsMutex > &, unsigned index ); + void setReqPendingState ( epicsGuard < epicsMutex > &, unsigned index ); + unsigned getSearchTimerIndex ( epicsGuard < epicsMutex > & ); + friend class tcpiiu; + friend class udpiiu; + friend class tcpSendThread; + friend class searchTimer; + friend class disconnectGovernorTimer; +}; + +class privateInterfaceForIO { // X aCC 655 +public: + virtual void ioCompletionNotify ( + epicsGuard < epicsMutex > &, class baseNMIU & ) = 0; + virtual arrayElementCount nativeElementCount ( + epicsGuard < epicsMutex > & ) const = 0; + virtual bool connected ( epicsGuard < epicsMutex > & ) const = 0; +protected: + virtual ~privateInterfaceForIO() {} +}; + +class nciu : + public cacChannel, + public chronIntIdRes < nciu >, + public channelNode, + private privateInterfaceForIO { +public: + nciu ( cac &, netiiu &, cacChannelNotify &, + const char * pNameIn, cacChannel::priLev ); + ~nciu (); + void connect ( unsigned nativeType, + unsigned nativeCount, unsigned sid, + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void connect ( epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void unresponsiveCircuitNotify ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void circuitHangupNotify ( class udpiiu &, + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void setServerAddressUnknown ( + netiiu & newiiu, epicsGuard < epicsMutex > & guard ); + bool searchMsg ( + epicsGuard < epicsMutex > & ); + void serviceShutdownNotify ( + epicsGuard < epicsMutex > & callbackControlGuard, + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void accessRightsStateChange ( const caAccessRights &, + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + ca_uint32_t getSID ( + epicsGuard < epicsMutex > & ) const; + ca_uint32_t getCID ( + epicsGuard < epicsMutex > & ) const; + netiiu * getPIIU ( + epicsGuard < epicsMutex > & ); + const netiiu * getConstPIIU ( + epicsGuard < epicsMutex > & ) const; + cac & getClient (); + void searchReplySetUp ( netiiu &iiu, unsigned sidIn, + ca_uint16_t typeIn, arrayElementCount countIn, + epicsGuard < epicsMutex > & ); + void show ( + unsigned level ) const; + void show ( + epicsGuard < epicsMutex > &, + unsigned level ) const; + unsigned getName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw (); + const char * pName ( + epicsGuard < epicsMutex > & ) const throw (); + unsigned nameLen ( + epicsGuard < epicsMutex > & ) const; + unsigned getHostName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw (); + void writeException ( + epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > &, + int status, const char *pContext, unsigned type, arrayElementCount count ); + cacChannel::priLev getPriority ( + epicsGuard < epicsMutex > & ) const; + void * operator new ( + size_t size, tsFreeList < class nciu, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator ( + ( void *, tsFreeList < class nciu, 1024, epicsMutexNOOP > & )) + //arrayElementCount nativeElementCount ( epicsGuard < epicsMutex > & ) const; + void resubscribe ( epicsGuard < epicsMutex > & ); + void sendSubscriptionUpdateRequests ( epicsGuard < epicsMutex > & ); + void disconnectAllIO ( + epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > & ); + bool connected ( epicsGuard < epicsMutex > & ) const; + unsigned getcount() const { return count; } + +private: + tsDLList < class baseNMIU > eventq; + caAccessRights accessRightState; + cac & cacCtx; + char * pNameStr; + netiiu * piiu; + ca_uint32_t sid; // server id + unsigned count; + unsigned retry; // search retry number + unsigned short nameLength; // channel name length + ca_uint16_t typeCode; + ca_uint8_t priority; + virtual void destroy ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void initiateConnect ( + epicsGuard < epicsMutex > & ); + unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void flush ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + ioStatus read ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + cacReadNotify &, ioid * ); + void write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + const void *pValue ); + ioStatus write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + const void *pValue, cacWriteNotify &, ioid * ); + void subscribe ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount nElem, + unsigned mask, cacStateNotify ¬ify, ioid * ); + virtual void ioCancel ( + epicsGuard < epicsMutex > & mutualExclusionGuard, + const ioid & ); + void ioShow ( + epicsGuard < epicsMutex > &, + const ioid &, unsigned level ) const; + short nativeType ( + epicsGuard < epicsMutex > & ) const; + caAccessRights accessRights ( + epicsGuard < epicsMutex > & ) const; + unsigned searchAttempts ( + epicsGuard < epicsMutex > & ) const; + double beaconPeriod ( + epicsGuard < epicsMutex > & ) const; + double receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const; + bool ca_v42_ok ( + epicsGuard < epicsMutex > & ) const; + arrayElementCount nativeElementCount ( + epicsGuard < epicsMutex > & ) const; + static void stringVerify ( const char *pStr, const unsigned count ); + void ioCompletionNotify ( + epicsGuard < epicsMutex > &, class baseNMIU & ); + const char * pHostName ( + epicsGuard < epicsMutex > & guard ) const throw (); + nciu ( const nciu & ); + nciu & operator = ( const nciu & ); + void * operator new ( size_t ); + void operator delete ( void * ); +}; + +inline void * nciu::operator new ( size_t size, + tsFreeList < class nciu, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void nciu::operator delete ( void * pCadaver, + tsFreeList < class nciu, 1024, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver, sizeof ( nciu ) ); +} +#endif + +inline ca_uint32_t nciu::getSID ( + epicsGuard < epicsMutex > & ) const +{ + return this->sid; +} + +inline ca_uint32_t nciu::getCID ( + epicsGuard < epicsMutex > & ) const +{ + return this->id; +} + +// this is to only be used by early protocol revisions +inline void nciu::connect ( epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + this->connect ( this->typeCode, this->count, + this->sid, cbGuard, guard ); +} + +inline void nciu::searchReplySetUp ( netiiu &iiu, unsigned sidIn, + ca_uint16_t typeIn, arrayElementCount countIn, + epicsGuard < epicsMutex > & ) +{ + this->piiu = & iiu; + this->typeCode = typeIn; + this->count = countIn; + this->sid = sidIn; +} + +inline netiiu * nciu::getPIIU ( + epicsGuard < epicsMutex > & ) +{ + return this->piiu; +} + +inline void nciu::writeException ( + epicsGuard < epicsMutex > & /* cbGuard */, + epicsGuard < epicsMutex > & guard, + int status, const char * pContext, + unsigned typeIn, arrayElementCount countIn ) +{ + this->notify().writeException ( guard, + status, pContext, typeIn, countIn ); +} + +inline const netiiu * nciu::getConstPIIU ( + epicsGuard < epicsMutex > & ) const +{ + return this->piiu; +} + +inline cac & nciu::getClient () +{ + return this->cacCtx; +} + +inline cacChannel::priLev nciu::getPriority ( + epicsGuard < epicsMutex > & ) const +{ + return this->priority; +} + +inline channelNode::channelNode () : + listMember ( cs_none ) +{ +} + +inline bool channelNode::isConnected ( epicsGuard < epicsMutex > & ) const +{ + return + this->listMember == cs_connected || + this->listMember == cs_subscripReqPend || + this->listMember == cs_subscripUpdateReqPend; +} + +inline bool channelNode::isInstalledInServer ( epicsGuard < epicsMutex > & ) const +{ + return + this->listMember == cs_connected || + this->listMember == cs_subscripReqPend || + this->listMember == cs_unrespCircuit || + this->listMember == cs_subscripUpdateReqPend; +} + +#endif // ifdef nciuh diff --git a/src/ca/netIO.h b/src/ca/netIO.h new file mode 100644 index 000000000..57273427a --- /dev/null +++ b/src/ca/netIO.h @@ -0,0 +1,309 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef netIOh +#define netIOh + +#include "nciu.h" +#include "compilerDependencies.h" + +// SUN PRO generates multiply defined symbols if the baseNMIU +// destructor is virtual (therefore it is protected). +// I assume that SUNPRO will fix this in future versions. +// With other compilers we get warnings (and +// potential problems) if we dont make the baseNMIU +// destructor virtual. +#if defined ( __SUNPRO_CC ) && ( __SUNPRO_CC <= 0x540 ) +# define NETIO_VIRTUAL_DESTRUCTOR +#else +# define NETIO_VIRTUAL_DESTRUCTOR virtual +#endif + +class privateInterfaceForIO; + +class baseNMIU : public tsDLNode < baseNMIU >, // X aCC 655 + public chronIntIdRes < baseNMIU > { +public: + virtual void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ) = 0; // only called by cac + virtual void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ) = 0; + virtual void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, unsigned type, + arrayElementCount count ) = 0; + virtual void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, + const void * pData ) = 0; + virtual void forceSubscriptionUpdate ( + epicsGuard < epicsMutex > & guard, nciu & chan ) = 0; + virtual class netSubscription * isSubscription () = 0; + virtual void show ( + unsigned level ) const = 0; + virtual void show ( + epicsGuard < epicsMutex > &, + unsigned level ) const = 0; +protected: + NETIO_VIRTUAL_DESTRUCTOR ~baseNMIU (); +}; + +class netSubscription : public baseNMIU { +public: + static netSubscription * factory ( + tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &, + class privateInterfaceForIO &, unsigned type, arrayElementCount count, + unsigned mask, cacStateNotify & ); + void show ( + unsigned level ) const; + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; + arrayElementCount getCount ( + epicsGuard < epicsMutex > &, bool allow_zero ) const; + unsigned getType ( + epicsGuard < epicsMutex > & ) const; + unsigned getMask ( + epicsGuard < epicsMutex > & ) const; + void subscribeIfRequired ( + epicsGuard < epicsMutex > & guard, nciu & chan ); + void unsubscribeIfRequired ( + epicsGuard < epicsMutex > & guard, nciu & chan ); +protected: + netSubscription ( + class privateInterfaceForIO &, unsigned type, + arrayElementCount count, + unsigned mask, cacStateNotify & ); + ~netSubscription (); +private: + const arrayElementCount count; + class privateInterfaceForIO & privateChanForIO; + cacStateNotify & notify; + const unsigned type; + const unsigned mask; + bool subscribed; + class netSubscription * isSubscription (); + void * operator new ( size_t ); + void operator delete ( void * ); + void * operator new ( size_t, + tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & )) + void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, const void * pData ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, unsigned type, + arrayElementCount count ); + void forceSubscriptionUpdate ( + epicsGuard < epicsMutex > & guard, nciu & chan ); + netSubscription ( const netSubscription & ); + netSubscription & operator = ( const netSubscription & ); +}; + +class netReadNotifyIO : public baseNMIU { +public: + static netReadNotifyIO * factory ( + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > &, + privateInterfaceForIO &, cacReadNotify & ); + void show ( + unsigned level ) const; + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; +protected: + netReadNotifyIO ( privateInterfaceForIO &, cacReadNotify & ); + ~netReadNotifyIO (); +private: + cacReadNotify & notify; + class privateInterfaceForIO & privateChanForIO; + void * operator new ( size_t ); + void operator delete ( void * ); + void * operator new ( size_t, + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & )) + void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, + const void * pData ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, + unsigned type, arrayElementCount count ); + class netSubscription * isSubscription (); + void forceSubscriptionUpdate ( + epicsGuard < epicsMutex > & guard, nciu & chan ); + netReadNotifyIO ( const netReadNotifyIO & ); + netReadNotifyIO & operator = ( const netReadNotifyIO & ); +}; + +class netWriteNotifyIO : public baseNMIU { +public: + static netWriteNotifyIO * factory ( + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > &, + privateInterfaceForIO &, cacWriteNotify & ); + void show ( + unsigned level ) const; + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; +protected: + netWriteNotifyIO ( privateInterfaceForIO &, cacWriteNotify & ); + ~netWriteNotifyIO (); +private: + cacWriteNotify & notify; + privateInterfaceForIO & privateChanForIO; + void * operator new ( size_t ); + void operator delete ( void * ); + void * operator new ( size_t, + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & )) + class netSubscription * isSubscription (); + void destroy ( + epicsGuard < epicsMutex > &, class cacRecycle & ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle & ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext ); + void completion ( + epicsGuard < epicsMutex > &, cacRecycle &, + unsigned type, arrayElementCount count, + const void * pData ); + void exception ( + epicsGuard < epicsMutex > &, cacRecycle &, + int status, const char * pContext, unsigned type, + arrayElementCount count ); + void forceSubscriptionUpdate ( + epicsGuard < epicsMutex > & guard, nciu & chan ); + netWriteNotifyIO ( const netWriteNotifyIO & ); + netWriteNotifyIO & operator = ( const netWriteNotifyIO & ); +}; + +inline void * netSubscription::operator new ( size_t size, + tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &freeList ) +{ + return freeList.allocate ( size ); +} + +#if defined ( CXX_PLACEMENT_DELETE ) + inline void netSubscription::operator delete ( void *pCadaver, + tsFreeList < class netSubscription, 1024, epicsMutexNOOP > &freeList ) + { + freeList.release ( pCadaver ); + } +#endif + +inline netSubscription * netSubscription::factory ( + tsFreeList < class netSubscription, 1024, epicsMutexNOOP > & freeList, + class privateInterfaceForIO & chan, unsigned type, arrayElementCount count, + unsigned mask, cacStateNotify ¬ify ) +{ + return new ( freeList ) netSubscription ( chan, type, // X aCC 930 + count, mask, notify ); +} + +inline arrayElementCount netSubscription::getCount ( + epicsGuard < epicsMutex > & guard, bool allow_zero ) const // X aCC 361 +{ + //guard.assertIdenticalMutex ( this->mutex ); + arrayElementCount nativeCount = this->privateChanForIO.nativeElementCount ( guard ); + if ( (this->count == 0u && !allow_zero) || this->count > nativeCount ) { + return nativeCount; + } + else { + return this->count; + } +} + +inline unsigned netSubscription::getType ( epicsGuard < epicsMutex > & ) const +{ + return this->type; +} + +inline unsigned netSubscription::getMask ( epicsGuard < epicsMutex > & ) const +{ + return this->mask; +} + +inline netReadNotifyIO * netReadNotifyIO::factory ( + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList, + privateInterfaceForIO & ioComplNotifIntf, cacReadNotify & notify ) +{ + return new ( freeList ) netReadNotifyIO ( ioComplNotifIntf, notify ); // X aCC 930 +} + +inline void * netReadNotifyIO::operator new ( size_t size, + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#if defined ( CXX_PLACEMENT_DELETE ) + inline void netReadNotifyIO::operator delete ( void *pCadaver, + tsFreeList < class netReadNotifyIO, 1024, epicsMutexNOOP > & freeList ) + { + freeList.release ( pCadaver ); + } +#endif + +inline netWriteNotifyIO * netWriteNotifyIO::factory ( + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList, + privateInterfaceForIO & ioComplNotifyIntf, cacWriteNotify & notify ) +{ + return new ( freeList ) netWriteNotifyIO ( ioComplNotifyIntf, notify ); // X aCC 930 +} + +inline void * netWriteNotifyIO::operator new ( size_t size, + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#if defined ( CXX_PLACEMENT_DELETE ) + inline void netWriteNotifyIO::operator delete ( void *pCadaver, + tsFreeList < class netWriteNotifyIO, 1024, epicsMutexNOOP > & freeList ) + { + freeList.release ( pCadaver ); + } +#endif + +#endif // ifdef netIOh diff --git a/src/ca/netReadNotifyIO.cpp b/src/ca/netReadNotifyIO.cpp new file mode 100644 index 000000000..446b7175d --- /dev/null +++ b/src/ca/netReadNotifyIO.cpp @@ -0,0 +1,141 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include +#include + +#include "errlog.h" + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "iocinf.h" +#include "nciu.h" +#include "cac.h" + +netReadNotifyIO::netReadNotifyIO ( + privateInterfaceForIO & ioComplIntfIn, + cacReadNotify & notify ) : + notify ( notify ), privateChanForIO ( ioComplIntfIn ) +{ +} + +netReadNotifyIO::~netReadNotifyIO () +{ +} + +void netReadNotifyIO::show ( unsigned /* level */ ) const +{ + ::printf ( "netReadNotifyIO at %p\n", + static_cast < const void * > ( this ) ); +} + +void netReadNotifyIO::show ( + epicsGuard < epicsMutex > &, unsigned level ) const +{ + this->show ( level ); +} + +void netReadNotifyIO::destroy ( + epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) +{ + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); +} + +void netReadNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, unsigned type, + arrayElementCount count, const void * pData ) +{ + //guard.assertIdenticalMutex ( this->mutex ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.completion ( guard, type, count, pData ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); +} + +void netReadNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle ) +{ + //guard.assertIdenticalMutex ( this->mutex ); + //this->chan.getClient().printf ( "Read response w/o data ?\n" ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); +} + +void netReadNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char *pContext ) +{ + //guard.assertIdenticalMutex ( this->mutex ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0u ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); +} + +void netReadNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char *pContext, + unsigned type, arrayElementCount count ) +{ + //guard.assertIdenticalMutex ( this->mutex ) + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.exception ( + guard, status, pContext, type, count ); + this->~netReadNotifyIO (); + recycle.recycleReadNotifyIO ( guard, *this ); +} + +class netSubscription * netReadNotifyIO::isSubscription () +{ + return 0; +} + +void netReadNotifyIO::forceSubscriptionUpdate ( + epicsGuard < epicsMutex > &, nciu & ) +{ +} + +void * netReadNotifyIO::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void netReadNotifyIO::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + + + diff --git a/src/ca/netSubscription.cpp b/src/ca/netSubscription.cpp new file mode 100644 index 000000000..9a29ac2b6 --- /dev/null +++ b/src/ca/netSubscription.cpp @@ -0,0 +1,196 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include +#include + +#include "errlog.h" + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "nciu.h" +#include "cac.h" +#include "db_access.h" // for dbf_type_to_text +#include "caerr.h" + +netSubscription::netSubscription ( + privateInterfaceForIO & chanIn, + unsigned typeIn, arrayElementCount countIn, + unsigned maskIn, cacStateNotify & notifyIn ) : + count ( countIn ), privateChanForIO ( chanIn ), + notify ( notifyIn ), type ( typeIn ), mask ( maskIn ), + subscribed ( false ) +{ + if ( ! dbr_type_is_valid ( typeIn ) ) { + throw cacChannel::badType (); + } + if ( this->mask == 0u ) { + throw cacChannel::badEventSelection (); + } +} + +netSubscription::~netSubscription () +{ +} + +void netSubscription::destroy ( + epicsGuard < epicsMutex > & guard, cacRecycle & recycle ) +{ + this->~netSubscription (); + recycle.recycleSubscription ( guard, *this ); +} + +class netSubscription * netSubscription::isSubscription () +{ + return this; +} + +void netSubscription::show ( unsigned /* level */ ) const +{ + ::printf ( "event subscription IO at %p, type %s, element count %lu, mask %u\n", + static_cast < const void * > ( this ), + dbf_type_to_text ( static_cast < int > ( this->type ) ), + this->count, this->mask ); +} + +void netSubscription::show ( + epicsGuard < epicsMutex > &, unsigned level ) const +{ + this->show ( level ); +} + +void netSubscription::completion ( + epicsGuard < epicsMutex > &, cacRecycle & ) +{ + errlogPrintf ( "subscription update w/o data ?\n" ); +} + +void netSubscription::exception ( + epicsGuard < epicsMutex > & guard, cacRecycle & recycle, + int status, const char * pContext ) +{ + if ( status == ECA_DISCONN ) { + this->subscribed = false; + } + if ( status == ECA_CHANDESTROY ) { + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0 ); + this->~netSubscription (); + recycle.recycleSubscription ( guard, *this ); + } + else { + // guard.assertIdenticalMutex ( this->mutex ); + if ( this->privateChanForIO.connected ( guard ) ) { + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0 ); + } + } +} + +void netSubscription::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, int status, const char * pContext, + unsigned typeIn, arrayElementCount countIn ) +{ + if ( status == ECA_DISCONN ) { + this->subscribed = false; + } + if ( status == ECA_CHANDESTROY ) { + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0 ); + this->~netSubscription (); + recycle.recycleSubscription ( guard, *this ); + } + else { + //guard.assertIdenticalMutex ( this->mutex ); + if ( this->privateChanForIO.connected ( guard ) ) { + this->notify.exception ( + guard, status, pContext, typeIn, countIn ); + } + } +} + +void netSubscription::completion ( + epicsGuard < epicsMutex > & guard, cacRecycle &, + unsigned typeIn, arrayElementCount countIn, + const void * pDataIn ) +{ + // guard.assertIdenticalMutex ( this->mutex ); + if ( this->privateChanForIO.connected ( guard ) ) { + this->notify.current ( + guard, typeIn, countIn, pDataIn ); + } +} + +void netSubscription::subscribeIfRequired ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + if ( ! this->subscribed ) { + chan.getPIIU(guard)->subscriptionRequest ( + guard, chan, *this ); + this->subscribed = true; + } +} + +void netSubscription::unsubscribeIfRequired ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + if ( this->subscribed ) { + chan.getPIIU(guard)->subscriptionCancelRequest ( + guard, chan, *this ); + this->subscribed = false; + } +} + +void netSubscription::forceSubscriptionUpdate ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + chan.getPIIU(guard)->subscriptionUpdateRequest ( + guard, chan, *this ); +} + +void * netSubscription::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void netSubscription::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + + + + + + + diff --git a/src/ca/netWriteNotifyIO.cpp b/src/ca/netWriteNotifyIO.cpp new file mode 100644 index 000000000..68d4992e7 --- /dev/null +++ b/src/ca/netWriteNotifyIO.cpp @@ -0,0 +1,138 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include +#include + +#include "errlog.h" + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "iocinf.h" +#include "nciu.h" +#include "cac.h" + +netWriteNotifyIO::netWriteNotifyIO ( + privateInterfaceForIO & ioComplIntf, cacWriteNotify & notifyIn ) : + notify ( notifyIn ), privateChanForIO ( ioComplIntf ) +{ +} + +netWriteNotifyIO::~netWriteNotifyIO () +{ +} + +void netWriteNotifyIO::show ( unsigned /* level */ ) const +{ + ::printf ( "read write notify IO at %p\n", + static_cast < const void * > ( this ) ); +} + +void netWriteNotifyIO::show ( + epicsGuard < epicsMutex > &, + unsigned level ) const +{ + this->show ( level ); +} + +void netWriteNotifyIO::destroy ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle ) +{ + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); +} + +void netWriteNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle ) +{ + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.completion ( guard ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); +} + +void netWriteNotifyIO::completion ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + unsigned /* type */, arrayElementCount /* count */, + const void * /* pData */ ) +{ + //this->chan.getClient().printf ( "Write response with data ?\n" ); + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); +} + +void netWriteNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char * pContext ) +{ + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.exception ( + guard, status, pContext, UINT_MAX, 0u ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); +} + +void netWriteNotifyIO::exception ( + epicsGuard < epicsMutex > & guard, + cacRecycle & recycle, + int status, const char *pContext, + unsigned type, arrayElementCount count ) +{ + this->privateChanForIO.ioCompletionNotify ( guard, *this ); + this->notify.exception ( + guard, status, pContext, type, count ); + this->~netWriteNotifyIO (); + recycle.recycleWriteNotifyIO ( guard, *this ); +} + +class netSubscription * netWriteNotifyIO::isSubscription () +{ + return 0; +} + +void netWriteNotifyIO::forceSubscriptionUpdate ( + epicsGuard < epicsMutex > &, nciu & ) +{ +} + +void * netWriteNotifyIO::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( + "why is the compiler calling private operator new" ); +} + +void netWriteNotifyIO::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + diff --git a/src/ca/net_convert.h b/src/ca/net_convert.h new file mode 100644 index 000000000..bee51c0c4 --- /dev/null +++ b/src/ca/net_convert.h @@ -0,0 +1,36 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * Author: J. Hill + * + */ + +#ifndef _NET_CONVERT_H +#define _NET_CONVERT_H + +#include "db_access.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long arrayElementCount; + +epicsShareFunc int caNetConvert ( + unsigned type, const void *pSrc, void *pDest, + int hton, arrayElementCount count ); + +#ifdef __cplusplus +} +#endif + +#endif /* define _NET_CONVERT_H */ diff --git a/src/ca/netiiu.cpp b/src/ca/netiiu.cpp new file mode 100644 index 000000000..a81b15533 --- /dev/null +++ b/src/ca/netiiu.cpp @@ -0,0 +1,174 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include +#include // vxWorks 6.0 requires this include + +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "iocinf.h" +#include "cac.h" +#include "netiiu.h" + +netiiu::~netiiu () +{ +} + +bool netiiu::ca_v42_ok ( + epicsGuard < epicsMutex > & ) const +{ + return false; +} + +bool netiiu::ca_v41_ok ( + epicsGuard < epicsMutex > & ) const +{ + return false; +} + +void netiiu::writeRequest ( + epicsGuard < epicsMutex > &, nciu &, + unsigned, arrayElementCount, const void * ) +{ + throw cacChannel::notConnected(); +} + +void netiiu::writeNotifyRequest ( + epicsGuard < epicsMutex > &, + nciu &, netWriteNotifyIO &, unsigned, + arrayElementCount, const void * ) +{ + throw cacChannel::notConnected(); +} + +void netiiu::readNotifyRequest ( + epicsGuard < epicsMutex > &, + nciu &, netReadNotifyIO &, unsigned, arrayElementCount ) +{ + throw cacChannel::notConnected(); +} + +void netiiu::clearChannelRequest ( + epicsGuard < epicsMutex > &, ca_uint32_t, ca_uint32_t ) +{ +} + +void netiiu::subscriptionRequest ( + epicsGuard < epicsMutex > &, nciu &, netSubscription & ) +{ +} + +void netiiu::subscriptionCancelRequest ( + epicsGuard < epicsMutex > &, nciu &, netSubscription & ) +{ +} + +void netiiu::subscriptionUpdateRequest ( + epicsGuard < epicsMutex > &, nciu &, netSubscription & ) +{ +} + +static const char * const pHostNameNetIIU = ""; + +unsigned netiiu::getHostName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw () +{ + if ( bufLen ) { + unsigned len = strlen ( pHostNameNetIIU ); + strncpy ( pBuf, pHostNameNetIIU, bufLen ); + if ( len < bufLen ) { + return len; + } + else { + unsigned reducedSize = bufLen - 1u; + pBuf[reducedSize] = '\0'; + return reducedSize; + } + } + return 0u; +} + +const char * netiiu::pHostName ( + epicsGuard < epicsMutex > & ) const throw () +{ + return pHostNameNetIIU; +} + +osiSockAddr netiiu::getNetworkAddress ( + epicsGuard < epicsMutex > & ) const +{ + osiSockAddr addr; + addr.sa.sa_family = AF_UNSPEC; + return addr; +} + +void netiiu::flushRequest ( + epicsGuard < epicsMutex > & ) +{ +} + +unsigned netiiu::requestMessageBytesPending ( + epicsGuard < epicsMutex > & ) +{ + return 0u; +} + +void netiiu::flush ( + epicsGuard < epicsMutex > & ) +{ +} + +void netiiu::requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & ) +{ +} + +void netiiu::uninstallChan ( + epicsGuard < epicsMutex > &, nciu & ) +{ + throw cacChannel::notConnected(); +} + +double netiiu::receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const +{ + return - DBL_MAX; +} + +void netiiu::uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > &, nciu &, const epicsTime & ) +{ + throw std::runtime_error ( + "search response occured when not attached to udpiiu?" ); +} + +bool netiiu::searchMsg ( + epicsGuard < epicsMutex > &, ca_uint32_t /* id */, + const char * /* pName */, unsigned /* nameLength */ ) +{ + return false; +} + + diff --git a/src/ca/netiiu.h b/src/ca/netiiu.h new file mode 100644 index 000000000..99ea128f1 --- /dev/null +++ b/src/ca/netiiu.h @@ -0,0 +1,97 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef netiiuh +#define netiiuh + +#include "cacIO.h" +#include "caProto.h" + +class netWriteNotifyIO; +class netReadNotifyIO; +class netSubscription; +union osiSockAddr; +class cac; +class nciu; + +class netiiu { // X aCC 655 +public: + virtual ~netiiu () = 0; + virtual unsigned getHostName ( + epicsGuard < epicsMutex > &, char * pBuf, + unsigned bufLength ) const throw () = 0; + virtual const char * pHostName ( + epicsGuard < epicsMutex > & ) const throw () = 0; + virtual bool ca_v41_ok ( + epicsGuard < epicsMutex > & ) const = 0; + virtual bool ca_v42_ok ( + epicsGuard < epicsMutex > & ) const = 0; + virtual unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; + virtual void flush ( + epicsGuard < epicsMutex > & mutualExclusionGuard ) = 0; + virtual void writeRequest ( + epicsGuard < epicsMutex > &, nciu &, + unsigned type, arrayElementCount nElem, + const void *pValue ) = 0; + virtual void writeNotifyRequest ( + epicsGuard < epicsMutex > &, + nciu &, netWriteNotifyIO &, + unsigned type, arrayElementCount nElem, + const void *pValue ) = 0; + virtual void readNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, + netReadNotifyIO &, unsigned type, + arrayElementCount nElem ) = 0; + virtual void clearChannelRequest ( + epicsGuard < epicsMutex > &, + ca_uint32_t sid, ca_uint32_t cid ) = 0; + virtual void subscriptionRequest ( + epicsGuard < epicsMutex > &, + nciu &, netSubscription & ) = 0; + virtual void subscriptionUpdateRequest ( + epicsGuard < epicsMutex > &, + nciu &, netSubscription & ) = 0; + virtual void subscriptionCancelRequest ( + epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ) = 0; + virtual void flushRequest ( + epicsGuard < epicsMutex > & ) = 0; + virtual void requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & ) = 0; + virtual osiSockAddr getNetworkAddress ( + epicsGuard < epicsMutex > & ) const = 0; + virtual void uninstallChan ( + epicsGuard < epicsMutex > &, nciu & ) = 0; + virtual void uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > &, nciu &, + const class epicsTime & currentTime ) = 0; + virtual double receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const = 0; + virtual bool searchMsg ( + epicsGuard < epicsMutex > &, ca_uint32_t id, + const char * pName, unsigned nameLength ) = 0; +}; + +#endif // netiiuh diff --git a/src/ca/noopiiu.cpp b/src/ca/noopiiu.cpp new file mode 100644 index 000000000..a3d20b59b --- /dev/null +++ b/src/ca/noopiiu.cpp @@ -0,0 +1,170 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "osiSock.h" + +#define epicsExportSharedSymbols +#include "noopiiu.h" + +noopiiu noopIIU; + +noopiiu::~noopiiu () +{ +} + +unsigned noopiiu::getHostName ( + epicsGuard < epicsMutex > & cacGuard, + char * pBuf, unsigned bufLength ) const throw () +{ + return netiiu::getHostName ( cacGuard, pBuf, bufLength ); +} + +const char * noopiiu::pHostName ( + epicsGuard < epicsMutex > & cacGuard ) const throw () +{ + return netiiu::pHostName ( cacGuard ); +} + +bool noopiiu::ca_v42_ok ( + epicsGuard < epicsMutex > & cacGuard ) const +{ + return netiiu::ca_v42_ok ( cacGuard ); +} + +bool noopiiu::ca_v41_ok ( + epicsGuard < epicsMutex > & cacGuard ) const +{ + return netiiu::ca_v41_ok ( cacGuard ); +} + +void noopiiu::writeRequest ( + epicsGuard < epicsMutex > & guard, + nciu & chan, unsigned type, + arrayElementCount nElem, const void * pValue ) +{ + netiiu::writeRequest ( guard, chan, type, nElem, pValue ); +} + +void noopiiu::writeNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netWriteNotifyIO & io, unsigned type, + arrayElementCount nElem, const void *pValue ) +{ + netiiu::writeNotifyRequest ( guard, chan, io, type, nElem, pValue ); +} + +void noopiiu::readNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netReadNotifyIO & io, unsigned type, arrayElementCount nElem ) +{ + netiiu::readNotifyRequest ( guard, chan, io, type, nElem ); +} + +void noopiiu::clearChannelRequest ( + epicsGuard < epicsMutex > & guard, + ca_uint32_t sid, ca_uint32_t cid ) +{ + netiiu::clearChannelRequest ( guard, sid, cid ); +} + +void noopiiu::subscriptionRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netSubscription & subscr ) +{ + netiiu::subscriptionRequest ( guard, chan, subscr ); +} + +void noopiiu::subscriptionUpdateRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netSubscription & subscr ) +{ + netiiu::subscriptionUpdateRequest ( + guard, chan, subscr ); +} + +void noopiiu::subscriptionCancelRequest ( + epicsGuard < epicsMutex > & guard, + nciu & chan, netSubscription & subscr ) +{ + netiiu::subscriptionCancelRequest ( guard, chan, subscr ); +} + +void noopiiu::flushRequest ( + epicsGuard < epicsMutex > & guard ) +{ + netiiu::flushRequest ( guard ); +} + +unsigned noopiiu::requestMessageBytesPending ( + epicsGuard < epicsMutex > & guard ) +{ + return netiiu::requestMessageBytesPending ( guard ); +} + +void noopiiu::flush ( + epicsGuard < epicsMutex > & guard ) +{ + netiiu::flush ( guard ); +} + +void noopiiu::requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & guard ) +{ + netiiu::requestRecvProcessPostponedFlush ( guard ); +} + +osiSockAddr noopiiu::getNetworkAddress ( + epicsGuard < epicsMutex > & guard ) const +{ + return netiiu::getNetworkAddress ( guard ); +} + +double noopiiu::receiveWatchdogDelay ( + epicsGuard < epicsMutex > & guard ) const +{ + return netiiu::receiveWatchdogDelay ( guard ); +} + +void noopiiu::uninstallChan ( + epicsGuard < epicsMutex > &, nciu & ) +{ + // intentionally does not call default in netiiu +} + +void noopiiu::uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > & guard, nciu & chan, + const class epicsTime & currentTime ) +{ + netiiu::uninstallChanDueToSuccessfulSearchResponse ( + guard, chan, currentTime ); +} + +bool noopiiu::searchMsg ( + epicsGuard < epicsMutex > & guard, ca_uint32_t id, + const char * pName, unsigned nameLength ) +{ + return netiiu::searchMsg ( + guard, id, pName, nameLength ); +} + diff --git a/src/ca/noopiiu.h b/src/ca/noopiiu.h new file mode 100644 index 000000000..f373edec8 --- /dev/null +++ b/src/ca/noopiiu.h @@ -0,0 +1,92 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef noopiiuh +#define noopiiuh + +#include "netiiu.h" + +class noopiiu : public netiiu { +public: + ~noopiiu (); + unsigned getHostName ( + epicsGuard < epicsMutex > &, char * pBuf, + unsigned bufLength ) const throw (); + const char * pHostName ( + epicsGuard < epicsMutex > & ) const throw (); + bool ca_v41_ok ( + epicsGuard < epicsMutex > & ) const; + bool ca_v42_ok ( + epicsGuard < epicsMutex > & ) const; + unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void flush ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void writeRequest ( + epicsGuard < epicsMutex > &, nciu &, + unsigned type, arrayElementCount nElem, + const void *pValue ); + void writeNotifyRequest ( + epicsGuard < epicsMutex > &, + nciu &, netWriteNotifyIO &, + unsigned type, arrayElementCount nElem, + const void *pValue ); + void readNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, + netReadNotifyIO &, unsigned type, + arrayElementCount nElem ); + void clearChannelRequest ( + epicsGuard < epicsMutex > &, + ca_uint32_t sid, ca_uint32_t cid ); + void subscriptionRequest ( + epicsGuard < epicsMutex > &, + nciu &, netSubscription & ); + void subscriptionUpdateRequest ( + epicsGuard < epicsMutex > &, + nciu &, netSubscription & ); + void subscriptionCancelRequest ( + epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ); + void flushRequest ( + epicsGuard < epicsMutex > & ); + void requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & ); + osiSockAddr getNetworkAddress ( + epicsGuard < epicsMutex > & ) const; + void uninstallChan ( + epicsGuard < epicsMutex > & mutex, + nciu & ); + void uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > &, nciu &, + const class epicsTime & currentTime ); + double receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const; + bool searchMsg ( + epicsGuard < epicsMutex > &, ca_uint32_t id, + const char * pName, unsigned nameLength ); +}; + +extern noopiiu noopIIU; + +#endif // ifndef noopiiuh diff --git a/src/ca/oldAccess.h b/src/ca/oldAccess.h new file mode 100644 index 000000000..d18a4fe1b --- /dev/null +++ b/src/ca/oldAccess.h @@ -0,0 +1,564 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef oldAccessh +#define oldAccessh + +#ifdef epicsExportSharedSymbols +# define oldAccessh_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "tsFreeList.h" +#include "epicsMemory.h" +#include "compilerDependencies.h" +#include "osiSock.h" + +#ifdef oldAccessh_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "caProto.h" +#include "cacIO.h" +#include "cadef.h" +#include "syncGroup.h" + +struct oldChannelNotify : private cacChannelNotify { +public: + oldChannelNotify ( + epicsGuard < epicsMutex > &, struct ca_client_context &, + const char * pName, caCh * pConnCallBackIn, + void * pPrivateIn, capri priority ); + void destructor ( + epicsGuard < epicsMutex > & guard ); + + // legacy C API + friend unsigned epicsShareAPI ca_get_host_name ( + chid pChan, char * pBuf, unsigned bufLength ); + friend const char * epicsShareAPI ca_host_name ( + chid pChan ); + friend const char * epicsShareAPI ca_name ( + chid pChan ); + friend void epicsShareAPI ca_set_puser ( + chid pChan, void * puser ); + friend void * epicsShareAPI ca_puser ( + chid pChan ); + friend int epicsShareAPI ca_change_connection_event ( + chid pChan, caCh * pfunc ); + friend int epicsShareAPI ca_replace_access_rights_event ( + chid pChan, caArh *pfunc ); + friend int epicsShareAPI ca_array_get ( chtype type, + arrayElementCount count, chid pChan, void * pValue ); + friend int epicsShareAPI ca_array_get_callback ( chtype type, + arrayElementCount count, chid pChan, + caEventCallBackFunc *pfunc, void *arg ); + friend int epicsShareAPI ca_array_put ( + chtype type, arrayElementCount count, + chid pChan, const void * pValue ); + friend int epicsShareAPI ca_array_put_callback ( + chtype type, arrayElementCount count, + chid pChan, const void *pValue, + caEventCallBackFunc *pfunc, void *usrarg ); + friend double epicsShareAPI ca_beacon_period ( + chid pChan ); + friend unsigned epicsShareAPI ca_search_attempts ( + chid pChan ); + friend unsigned epicsShareAPI ca_write_access ( + chid pChan ); + friend unsigned epicsShareAPI ca_read_access ( + chid pChan ); + friend short epicsShareAPI ca_field_type ( + chid pChan ); + friend arrayElementCount epicsShareAPI ca_element_count ( + chid pChan ); + friend int epicsShareAPI ca_v42_ok ( + chid pChan ); + friend int epicsShareAPI ca_create_subscription ( + chtype type, arrayElementCount count, chid pChan, + long mask, caEventCallBackFunc * pCallBack, + void * pCallBackArg, evid * monixptr ); + friend enum channel_state epicsShareAPI ca_state ( + chid pChan ); + friend double epicsShareAPI ca_receive_watchdog_delay ( + chid pChan ); + + unsigned getName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw (); + void show ( + epicsGuard < epicsMutex > &, + unsigned level ) const; + void initiateConnect ( + epicsGuard < epicsMutex > & ); + void read ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, + cacReadNotify ¬ify, cacChannel::ioid *pId = 0 ); + void write ( + epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count, const void *pValue, + cacWriteNotify &, cacChannel::ioid *pId = 0 ); + void ioCancel ( + epicsGuard < epicsMutex > & mutualExclusionGuard, + const cacChannel::ioid & ); + void ioShow ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid &, unsigned level ) const; + ca_client_context & getClientCtx (); + void eliminateExcessiveSendBacklog ( + epicsGuard < epicsMutex > & ); + + void * operator new ( size_t size, + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void * , + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & )) +protected: + ~oldChannelNotify (); +private: + ca_client_context & cacCtx; + cacChannel & io; + caCh * pConnCallBack; + void * pPrivate; + caArh * pAccessRightsFunc; + unsigned ioSeqNo; + bool currentlyConnected; + bool prevConnected; + void connectNotify ( epicsGuard < epicsMutex > & ); + void disconnectNotify ( epicsGuard < epicsMutex > & ); + void serviceShutdownNotify ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void accessRightsNotify ( + epicsGuard < epicsMutex > &, const caAccessRights & ); + void exception ( epicsGuard < epicsMutex > &, + int status, const char * pContext ); + void readException ( epicsGuard < epicsMutex > &, + int status, const char * pContext, + unsigned type, arrayElementCount count, void *pValue ); + void writeException ( epicsGuard < epicsMutex > &, + int status, const char * pContext, + unsigned type, arrayElementCount count ); + oldChannelNotify ( const oldChannelNotify & ); + oldChannelNotify & operator = ( const oldChannelNotify & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +class getCopy : public cacReadNotify { +public: + getCopy ( + epicsGuard < epicsMutex > & guard, + ca_client_context & cacCtx, + oldChannelNotify &, unsigned type, + arrayElementCount count, void *pValue ); + ~getCopy (); + void show ( unsigned level ) const; + void cancel (); + void * operator new ( size_t size, + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & )) +private: + arrayElementCount count; + ca_client_context & cacCtx; + oldChannelNotify & chan; + void * pValue; + unsigned ioSeqNo; + unsigned type; + void completion ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void *pData ); + void exception ( + epicsGuard < epicsMutex > &, int status, + const char *pContext, unsigned type, arrayElementCount count ); + getCopy ( const getCopy & ); + getCopy & operator = ( const getCopy & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +class getCallback : public cacReadNotify { +public: + getCallback ( + oldChannelNotify & chanIn, + caEventCallBackFunc *pFunc, void *pPrivate ); + ~getCallback (); + void * operator new ( size_t size, + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & )) +private: + oldChannelNotify & chan; + caEventCallBackFunc * pFunc; + void * pPrivate; + void completion ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void *pData); + void exception ( + epicsGuard < epicsMutex > &, int status, + const char * pContext, unsigned type, arrayElementCount count ); + getCallback ( const getCallback & ); + getCallback & operator = ( const getCallback & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +class putCallback : public cacWriteNotify { +public: + putCallback ( + oldChannelNotify &, + caEventCallBackFunc *pFunc, void *pPrivate ); + ~putCallback (); + void * operator new ( size_t size, + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & )) +private: + oldChannelNotify & chan; + caEventCallBackFunc * pFunc; + void *pPrivate; + void completion ( epicsGuard < epicsMutex > & ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext, + unsigned type, arrayElementCount count ); + putCallback ( const putCallback & ); + putCallback & operator = ( const putCallback & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +struct oldSubscription : private cacStateNotify { +public: + oldSubscription ( + epicsGuard < epicsMutex > & guard, + oldChannelNotify & chanIn, cacChannel & io, + unsigned type, arrayElementCount nElem, unsigned mask, + caEventCallBackFunc * pFuncIn, void * pPrivateIn, + evid * ); + ~oldSubscription (); + oldChannelNotify & channel () const; + void cancel ( + epicsGuard < epicsMutex > & guard ); + void * operator new ( size_t size, + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & )) +private: + oldChannelNotify & chan; + cacChannel::ioid id; + caEventCallBackFunc * pFunc; + void * pPrivate; + void current ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void *pData ); + void exception ( + epicsGuard < epicsMutex > &, int status, + const char *pContext, unsigned type, arrayElementCount count ); + oldSubscription ( const oldSubscription & ); + oldSubscription & operator = ( const oldSubscription & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +extern "C" void cacOnceFunc ( void * ); +extern "C" void cacExitHandler ( void *); + +struct ca_client_context : public cacContextNotify +{ +public: + ca_client_context ( bool enablePreemptiveCallback = false ); + virtual ~ca_client_context (); + void changeExceptionEvent ( + caExceptionHandler * pfunc, void * arg ); + void registerForFileDescriptorCallBack ( + CAFDHANDLER * pFunc, void * pArg ); + void replaceErrLogHandler ( caPrintfFunc * ca_printf_func ); + cacChannel & createChannel ( + epicsGuard < epicsMutex > &, const char * pChannelName, + cacChannelNotify &, cacChannel::priLev pri ); + void flush ( epicsGuard < epicsMutex > & ); + void eliminateExcessiveSendBacklog ( + epicsGuard < epicsMutex > &, cacChannel & ); + int pendIO ( const double & timeout ); + int pendEvent ( const double & timeout ); + bool ioComplete () const; + void show ( unsigned level ) const; + unsigned circuitCount () const; + unsigned sequenceNumberOfOutstandingIO ( + epicsGuard < epicsMutex > & ) const; + unsigned beaconAnomaliesSinceProgramStart () const; + void incrementOutstandingIO ( + epicsGuard < epicsMutex > &, unsigned ioSeqNo ); + void decrementOutstandingIO ( + epicsGuard < epicsMutex > &, unsigned ioSeqNo ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, + unsigned type, arrayElementCount count, unsigned op ); + void blockForEventAndEnableCallbacks ( + epicsEvent & event, const double & timeout ); + CASG * lookupCASG ( epicsGuard < epicsMutex > &, unsigned id ); + static void installDefaultService ( cacService & ); + void installCASG ( epicsGuard < epicsMutex > &, CASG & ); + void uninstallCASG ( epicsGuard < epicsMutex > &, CASG & ); + void selfTest () const; +// perhaps these should be eliminated in deference to the exception mechanism + int printFormated ( const char * pformat, ... ) const; + int varArgsPrintFormated ( const char * pformat, va_list args ) const; + void signal ( int ca_status, const char * pfilenm, + int lineno, const char * pFormat, ... ); + void vSignal ( int ca_status, const char * pfilenm, + int lineno, const char *pFormat, va_list args ); + bool preemptiveCallbakIsEnabled () const; + void destroyGetCopy ( epicsGuard < epicsMutex > &, getCopy & ); + void destroyGetCallback ( epicsGuard < epicsMutex > &, getCallback & ); + void destroyPutCallback ( epicsGuard < epicsMutex > &, putCallback & ); + void destroySubscription ( epicsGuard < epicsMutex > &, oldSubscription & ); + epicsMutex & mutexRef () const; + + + // legacy C API + friend int epicsShareAPI ca_create_channel ( + const char * name_str, caCh * conn_func, void * puser, + capri priority, chid * chanptr ); + friend int epicsShareAPI ca_clear_channel ( chid pChan ); + friend int epicsShareAPI ca_array_get ( chtype type, + arrayElementCount count, chid pChan, void * pValue ); + friend int epicsShareAPI ca_array_get_callback ( chtype type, + arrayElementCount count, chid pChan, + caEventCallBackFunc *pfunc, void *arg ); + friend int epicsShareAPI ca_array_put ( chtype type, + arrayElementCount count, chid pChan, const void * pValue ); + friend int epicsShareAPI ca_array_put_callback ( chtype type, + arrayElementCount count, chid pChan, const void * pValue, + caEventCallBackFunc *pfunc, void *usrarg ); + friend int epicsShareAPI ca_create_subscription ( + chtype type, arrayElementCount count, chid pChan, + long mask, caEventCallBackFunc * pCallBack, void * pCallBackArg, + evid *monixptr ); + friend int epicsShareAPI ca_flush_io (); + friend int epicsShareAPI ca_clear_subscription ( evid pMon ); + friend int epicsShareAPI ca_sg_create ( CA_SYNC_GID * pgid ); + friend int epicsShareAPI ca_sg_delete ( const CA_SYNC_GID gid ); + friend int epicsShareAPI ca_sg_block ( const CA_SYNC_GID gid, ca_real timeout ); + friend int epicsShareAPI ca_sg_reset ( const CA_SYNC_GID gid ); + friend int epicsShareAPI ca_sg_test ( const CA_SYNC_GID gid ); + + // exceptions + class noSocket {}; +private: + chronIntIdResTable < CASG > sgTable; + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > oldChannelNotifyFreeList; + tsFreeList < class getCopy, 1024, epicsMutexNOOP > getCopyFreeList; + tsFreeList < class getCallback, 1024, epicsMutexNOOP > getCallbackFreeList; + tsFreeList < class putCallback, 1024, epicsMutexNOOP > putCallbackFreeList; + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > subscriptionFreeList; + tsFreeList < struct CASG, 128, epicsMutexNOOP > casgFreeList; + mutable epicsMutex mutex; + mutable epicsMutex cbMutex; + epicsEvent ioDone; + epicsEvent callbackThreadActivityComplete; + epicsThreadId createdByThread; + epics_auto_ptr < epicsGuard < epicsMutex > > pCallbackGuard; + epics_auto_ptr < cacContext > pServiceContext; + caExceptionHandler * ca_exception_func; + void * ca_exception_arg; + caPrintfFunc * pVPrintfFunc; + CAFDHANDLER * fdRegFunc; + void * fdRegArg; + SOCKET sock; + unsigned pndRecvCnt; + unsigned ioSeqNo; + unsigned callbackThreadsPending; + ca_uint16_t localPort; + bool fdRegFuncNeedsToBeCalled; + bool noWakeupSincePend; + + void attachToClientCtx (); + void callbackProcessingInitiateNotify (); + void callbackProcessingCompleteNotify (); + cacContext & createNetworkContext ( + epicsMutex & mutualExclusion, epicsMutex & callbackControl ); + void _sendWakeupMsg (); + + ca_client_context ( const ca_client_context & ); + ca_client_context & operator = ( const ca_client_context & ); + + friend void cacOnceFunc ( void * ); + friend void cacExitHandler ( void *); + static cacService * pDefaultService; + static epicsMutex * pDefaultServiceInstallMutex; + static const unsigned flushBlockThreshold; +}; + +int fetchClientContext ( ca_client_context * * ppcac ); + +inline ca_client_context & oldChannelNotify::getClientCtx () +{ + return this->cacCtx; +} + +inline unsigned oldChannelNotify::getName ( + epicsGuard < epicsMutex > & guard, + char * pBuf, unsigned bufLen ) const throw () +{ + return this->io.getName ( guard, pBuf, bufLen ); +} + +inline void oldChannelNotify::show ( + epicsGuard < epicsMutex > & guard, + unsigned level ) const +{ + this->io.show ( guard, level ); +} + +inline void oldChannelNotify::initiateConnect ( + epicsGuard < epicsMutex > & guard ) +{ + this->io.initiateConnect ( guard ); +} + +inline void oldChannelNotify::ioCancel ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & id ) +{ + this->io.ioCancel ( guard, id ); +} + +inline void oldChannelNotify::ioShow ( + epicsGuard < epicsMutex > & guard, + const cacChannel::ioid & id, unsigned level ) const +{ + this->io.ioShow ( guard, id, level ); +} + +inline void oldChannelNotify::eliminateExcessiveSendBacklog ( + epicsGuard < epicsMutex > & guard ) +{ + this->cacCtx.eliminateExcessiveSendBacklog ( guard, this->io ); +} + +inline void * oldChannelNotify::operator new ( size_t size, + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void oldChannelNotify::operator delete ( void *pCadaver, + tsFreeList < struct oldChannelNotify, 1024, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void * oldSubscription::operator new ( size_t size, + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void oldSubscription::operator delete ( void *pCadaver, + tsFreeList < struct oldSubscription, 1024, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void oldSubscription::cancel ( + epicsGuard < epicsMutex > & guard ) +{ + this->chan.ioCancel ( guard, this->id ); +} + +inline oldChannelNotify & oldSubscription::channel () const +{ + return this->chan; +} + +inline void * getCopy::operator new ( size_t size, + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void getCopy::operator delete ( void *pCadaver, + tsFreeList < class getCopy, 1024, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void * putCallback::operator new ( size_t size, + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void putCallback::operator delete ( void * pCadaver, + tsFreeList < class putCallback, 1024, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void * getCallback::operator new ( size_t size, + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void getCallback::operator delete ( void * pCadaver, + tsFreeList < class getCallback, 1024, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline bool ca_client_context::preemptiveCallbakIsEnabled () const +{ + return this->pCallbackGuard.get () == 0; +} + +inline bool ca_client_context::ioComplete () const +{ + return ( this->pndRecvCnt == 0u ); +} + +inline unsigned ca_client_context::sequenceNumberOfOutstandingIO ( + epicsGuard < epicsMutex > & ) const +{ + // perhaps on SMP systems THERE should be lock/unlock around this + return this->ioSeqNo; +} + +#endif // ifndef oldAccessh diff --git a/src/ca/oldChannelNotify.cpp b/src/ca/oldChannelNotify.cpp new file mode 100644 index 000000000..8865f8d63 --- /dev/null +++ b/src/ca/oldChannelNotify.cpp @@ -0,0 +1,709 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#ifdef _MSC_VER +# pragma warning(disable:4355) +#endif + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" +#include "cac.h" +#include "autoPtrFreeList.h" + +extern "C" void cacNoopAccesRightsHandler ( struct access_rights_handler_args ) +{ +} + +oldChannelNotify::oldChannelNotify ( + epicsGuard < epicsMutex > & guard, ca_client_context & cacIn, + const char *pName, caCh * pConnCallBackIn, + void * pPrivateIn, capri priority ) : + cacCtx ( cacIn ), + io ( cacIn.createChannel ( guard, pName, *this, priority ) ), + pConnCallBack ( pConnCallBackIn ), + pPrivate ( pPrivateIn ), pAccessRightsFunc ( cacNoopAccesRightsHandler ), + ioSeqNo ( 0 ), currentlyConnected ( false ), prevConnected ( false ) +{ + guard.assertIdenticalMutex ( cacIn.mutexRef () ); + this->ioSeqNo = cacIn.sequenceNumberOfOutstandingIO ( guard ); + if ( pConnCallBackIn == 0 ) { + cacIn.incrementOutstandingIO ( guard, this->ioSeqNo ); + } +} + +oldChannelNotify::~oldChannelNotify () +{ +} + +void oldChannelNotify::destructor ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->cacCtx.mutexRef () ); + this->io.destroy ( guard ); + // no need to worry about a connect preempting here because + // the io (the nciu) has been destroyed above + if ( this->pConnCallBack == 0 && ! this->currentlyConnected ) { + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); + } + this->~oldChannelNotify (); +} + +void oldChannelNotify::connectNotify ( + epicsGuard < epicsMutex > & guard ) +{ + this->currentlyConnected = true; + this->prevConnected = true; + if ( this->pConnCallBack ) { + struct connection_handler_args args; + args.chid = this; + args.op = CA_OP_CONN_UP; + caCh * pFunc = this->pConnCallBack; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFunc ) ( args ); + } + } + else { + this->cacCtx.decrementOutstandingIO ( guard, this->ioSeqNo ); + } +} + +void oldChannelNotify::disconnectNotify ( + epicsGuard < epicsMutex > & guard ) +{ + this->currentlyConnected = false; + if ( this->pConnCallBack ) { + struct connection_handler_args args; + args.chid = this; + args.op = CA_OP_CONN_DOWN; + caCh * pFunc = this->pConnCallBack; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFunc ) ( args ); + } + } + else { + this->cacCtx.incrementOutstandingIO ( + guard, this->ioSeqNo ); + } +} + +void oldChannelNotify::serviceShutdownNotify ( + epicsGuard < epicsMutex > & guard ) +{ + this->disconnectNotify ( guard ); +} + +void oldChannelNotify::accessRightsNotify ( + epicsGuard < epicsMutex > & guard, const caAccessRights & ar ) +{ + struct access_rights_handler_args args; + args.chid = this; + args.ar.read_access = ar.readPermit(); + args.ar.write_access = ar.writePermit(); + caArh * pFunc = this->pAccessRightsFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFunc ) ( args ); + } +} + +void oldChannelNotify::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * pContext ) +{ + this->cacCtx.exception ( guard, status, pContext, __FILE__, __LINE__ ); +} + +void oldChannelNotify::readException ( + epicsGuard < epicsMutex > & guard, int status, const char *pContext, + unsigned type, arrayElementCount count, void * /* pValue */ ) +{ + this->cacCtx.exception ( guard, status, pContext, + __FILE__, __LINE__, *this, type, count, CA_OP_GET ); +} + +void oldChannelNotify::writeException ( + epicsGuard < epicsMutex > & guard, int status, const char *pContext, + unsigned type, arrayElementCount count ) +{ + this->cacCtx.exception ( guard, status, pContext, + __FILE__, __LINE__, *this, type, count, CA_OP_PUT ); +} + +void * oldChannelNotify::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void oldChannelNotify::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +/* + * ca_get_host_name () + */ +unsigned epicsShareAPI ca_get_host_name ( + chid pChan, char * pBuf, unsigned bufLength ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef() ); + return pChan->io.getHostName ( guard, pBuf, bufLength ); +} + +/* + * ca_host_name () + * + * !!!! not thread safe !!!! + * + */ +const char * epicsShareAPI ca_host_name ( + chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.pHostName ( guard ); +} + +/* + * ca_set_puser () + */ +void epicsShareAPI ca_set_puser ( + chid pChan, void * puser ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + pChan->pPrivate = puser; +} + +/* + * ca_get_puser () + */ +void * epicsShareAPI ca_puser ( + chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->pPrivate; +} + +/* + * Specify an event subroutine to be run for connection events + */ +int epicsShareAPI ca_change_connection_event ( chid pChan, caCh * pfunc ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + if ( ! pChan->currentlyConnected ) { + if ( pfunc ) { + if ( ! pChan->pConnCallBack ) { + pChan->cacCtx.decrementOutstandingIO ( guard, pChan->ioSeqNo ); + } + } + else { + if ( pChan->pConnCallBack ) { + pChan->cacCtx.incrementOutstandingIO ( guard, pChan->ioSeqNo ); + } + } + } + pChan->pConnCallBack = pfunc; + return ECA_NORMAL; +} + +/* + * ca_replace_access_rights_event + */ +int epicsShareAPI ca_replace_access_rights_event ( + chid pChan, caArh *pfunc ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + + // The order of the following is significant to guarantee that the + // access rights handler is always gets called even if the channel connects + // while this is running. There is some very small chance that the + // handler could be called twice here with the same access rights state, but + // that will not upset the application. + pChan->pAccessRightsFunc = pfunc ? pfunc : cacNoopAccesRightsHandler; + caAccessRights tmp = pChan->io.accessRights ( guard ); + + if ( pChan->currentlyConnected ) { + struct access_rights_handler_args args; + args.chid = pChan; + args.ar.read_access = tmp.readPermit (); + args.ar.write_access = tmp.writePermit (); + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pChan->pAccessRightsFunc ) ( args ); + } + return ECA_NORMAL; +} + +/* + * ca_array_get () + */ +int epicsShareAPI ca_array_get ( chtype type, + arrayElementCount count, chid pChan, void *pValue ) +{ + int caStatus; + try { + if ( type < 0 ) { + return ECA_BADTYPE; + } + if ( count == 0 ) + return ECA_BADCOUNT; + + unsigned tmpType = static_cast < unsigned > ( type ); + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + pChan->eliminateExcessiveSendBacklog ( guard ); + autoPtrFreeList < getCopy, 0x400, epicsMutexNOOP > pNotify + ( pChan->getClientCtx().getCopyFreeList, + new ( pChan->getClientCtx().getCopyFreeList ) + getCopy ( guard, pChan->getClientCtx(), *pChan, + tmpType, count, pValue ) ); + pChan->io.read ( guard, type, count, *pNotify, 0 ); + pNotify.release (); + caStatus = ECA_NORMAL; + } + catch ( cacChannel::badString & ) + { + caStatus = ECA_BADSTR; + } + catch ( cacChannel::badType & ) + { + caStatus = ECA_BADTYPE; + } + catch ( cacChannel::outOfBounds & ) + { + caStatus = ECA_BADCOUNT; + } + catch ( cacChannel::noReadAccess & ) + { + caStatus = ECA_NORDACCESS; + } + catch ( cacChannel::notConnected & ) + { + caStatus = ECA_DISCONN; + } + catch ( cacChannel::unsupportedByService & ) + { + caStatus = ECA_UNAVAILINSERV; + } + catch ( cacChannel::requestTimedOut & ) + { + caStatus = ECA_TIMEOUT; + } + catch ( std::bad_alloc & ) + { + caStatus = ECA_ALLOCMEM; + } + catch ( cacChannel::msgBodyCacheTooSmall & ) { + caStatus = ECA_TOLARGE; + } + catch ( ... ) + { + caStatus = ECA_GETFAIL; + } + return caStatus; +} + +/* + * ca_array_get_callback () + */ +int epicsShareAPI ca_array_get_callback ( chtype type, + arrayElementCount count, chid pChan, + caEventCallBackFunc *pfunc, void *arg ) +{ + int caStatus; + try { + if ( type < 0 ) { + return ECA_BADTYPE; + } + unsigned tmpType = static_cast < unsigned > ( type ); + + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + pChan->eliminateExcessiveSendBacklog ( guard ); + autoPtrFreeList < getCallback, 0x400, epicsMutexNOOP > pNotify + ( pChan->getClientCtx().getCallbackFreeList, + new ( pChan->getClientCtx().getCallbackFreeList ) + getCallback ( *pChan, pfunc, arg ) ); + pChan->io.read ( guard, tmpType, count, *pNotify, 0 ); + pNotify.release (); + caStatus = ECA_NORMAL; + } + catch ( cacChannel::badString & ) + { + caStatus = ECA_BADSTR; + } + catch ( cacChannel::badType & ) + { + caStatus = ECA_BADTYPE; + } + catch ( cacChannel::outOfBounds & ) + { + caStatus = ECA_BADCOUNT; + } + catch ( cacChannel::noReadAccess & ) + { + caStatus = ECA_NORDACCESS; + } + catch ( cacChannel::notConnected & ) + { + caStatus = ECA_DISCONN; + } + catch ( cacChannel::unsupportedByService & ) + { + caStatus = ECA_UNAVAILINSERV; + } + catch ( cacChannel::requestTimedOut & ) + { + caStatus = ECA_TIMEOUT; + } + catch ( std::bad_alloc & ) + { + caStatus = ECA_ALLOCMEM; + } + catch ( cacChannel::msgBodyCacheTooSmall ) { + caStatus = ECA_TOLARGE; + } + catch ( ... ) + { + caStatus = ECA_GETFAIL; + } + return caStatus; +} + +void oldChannelNotify::read ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount count, + cacReadNotify & notify, cacChannel::ioid * pId ) +{ + this->io.read ( guard, type, count, notify, pId ); +} + +/* + * ca_array_put_callback () + */ +int epicsShareAPI ca_array_put_callback ( chtype type, arrayElementCount count, + chid pChan, const void *pValue, caEventCallBackFunc *pfunc, void *usrarg ) +{ + int caStatus; + try { + if ( type < 0 ) { + return ECA_BADTYPE; + } + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + pChan->eliminateExcessiveSendBacklog ( guard ); + unsigned tmpType = static_cast < unsigned > ( type ); + autoPtrFreeList < putCallback, 0x400, epicsMutexNOOP > pNotify + ( pChan->getClientCtx().putCallbackFreeList, + new ( pChan->getClientCtx().putCallbackFreeList ) + putCallback ( *pChan, pfunc, usrarg ) ); + pChan->io.write ( guard, tmpType, count, pValue, *pNotify, 0 ); + pNotify.release (); + caStatus = ECA_NORMAL; + } + catch ( cacChannel::badString & ) + { + caStatus = ECA_BADSTR; + } + catch ( cacChannel::badType & ) + { + caStatus = ECA_BADTYPE; + } + catch ( cacChannel::outOfBounds & ) + { + caStatus = ECA_BADCOUNT; + } + catch ( cacChannel::noWriteAccess & ) + { + caStatus = ECA_NOWTACCESS; + } + catch ( cacChannel::notConnected & ) + { + caStatus = ECA_DISCONN; + } + catch ( cacChannel::unsupportedByService & ) + { + caStatus = ECA_UNAVAILINSERV; + } + catch ( cacChannel::requestTimedOut & ) + { + caStatus = ECA_TIMEOUT; + } + catch ( std::bad_alloc & ) + { + caStatus = ECA_ALLOCMEM; + } + catch ( ... ) + { + caStatus = ECA_PUTFAIL; + } + return caStatus; +} + +/* + * ca_array_put () + */ +int epicsShareAPI ca_array_put ( chtype type, arrayElementCount count, + chid pChan, const void * pValue ) +{ + if ( type < 0 ) { + return ECA_BADTYPE; + } + unsigned tmpType = static_cast < unsigned > ( type ); + + int caStatus; + try { + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + pChan->eliminateExcessiveSendBacklog ( guard ); + pChan->io.write ( guard, tmpType, count, pValue ); + caStatus = ECA_NORMAL; + } + catch ( cacChannel::badString & ) + { + caStatus = ECA_BADSTR; + } + catch ( cacChannel::badType & ) + { + caStatus = ECA_BADTYPE; + } + catch ( cacChannel::outOfBounds & ) + { + caStatus = ECA_BADCOUNT; + } + catch ( cacChannel::noWriteAccess & ) + { + caStatus = ECA_NOWTACCESS; + } + catch ( cacChannel::notConnected & ) + { + caStatus = ECA_DISCONN; + } + catch ( cacChannel::unsupportedByService & ) + { + caStatus = ECA_UNAVAILINSERV; + } + catch ( cacChannel::requestTimedOut & ) + { + caStatus = ECA_TIMEOUT; + } + catch ( std::bad_alloc & ) + { + caStatus = ECA_ALLOCMEM; + } + catch ( ... ) + { + caStatus = ECA_PUTFAIL; + } + return caStatus; +} + +int epicsShareAPI ca_create_subscription ( + chtype type, arrayElementCount count, chid pChan, + long mask, caEventCallBackFunc * pCallBack, void * pCallBackArg, + evid * monixptr ) +{ + if ( type < 0 ) { + return ECA_BADTYPE; + } + unsigned tmpType = static_cast < unsigned > ( type ); + + if ( INVALID_DB_REQ (type) ) { + return ECA_BADTYPE; + } + + if ( pCallBack == NULL ) { + return ECA_BADFUNCPTR; + } + + static const long maskMask = 0xffff; + if ( ( mask & maskMask ) == 0) { + return ECA_BADMASK; + } + + if ( mask & ~maskMask ) { + return ECA_BADMASK; + } + + try { + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + try { + // if this stalls out on a live circuit then an exception + // can be forthcoming which we must ignore (this is a + // special case preserving legacy ca_create_subscription + // behavior) + pChan->eliminateExcessiveSendBacklog ( guard ); + } + catch ( cacChannel::notConnected & ) { + // intentionally ignored (its ok to subscribe when not connected) + } + new ( pChan->getClientCtx().subscriptionFreeList ) + oldSubscription ( + guard, *pChan, pChan->io, tmpType, count, mask, + pCallBack, pCallBackArg, monixptr ); + // dont touch object created after above new because + // the first callback might have canceled, and therefore + // destroyed, it + return ECA_NORMAL; + } + catch ( cacChannel::badType & ) + { + return ECA_BADTYPE; + } + catch ( cacChannel::outOfBounds & ) + { + return ECA_BADCOUNT; + } + catch ( cacChannel::badEventSelection & ) + { + return ECA_BADMASK; + } + catch ( cacChannel::noReadAccess & ) + { + return ECA_NORDACCESS; + } + catch ( cacChannel::unsupportedByService & ) + { + return ECA_UNAVAILINSERV; + } + catch ( std::bad_alloc & ) + { + return ECA_ALLOCMEM; + } + catch ( cacChannel::msgBodyCacheTooSmall & ) { + return ECA_TOLARGE; + } + catch ( ... ) + { + return ECA_INTERNAL; + } +} + +void oldChannelNotify::write ( + epicsGuard < epicsMutex > & guard, unsigned type, arrayElementCount count, + const void * pValue, cacWriteNotify & notify, cacChannel::ioid * pId ) +{ + this->io.write ( guard, type, count, pValue, notify, pId ); +} + +/* + * ca_field_type() + */ +short epicsShareAPI ca_field_type ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.nativeType ( guard ); +} + +/* + * ca_element_count () + */ +arrayElementCount epicsShareAPI ca_element_count ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.nativeElementCount ( guard ); +} + +/* + * ca_state () + */ +enum channel_state epicsShareAPI ca_state ( chid pChan ) // X aCC 361 +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + if ( pChan->io.connected ( guard ) ) { + return cs_conn; + } + else if ( pChan->prevConnected ){ + return cs_prev_conn; + } + else { + return cs_never_conn; + } +} + +/* + * ca_read_access () + */ +unsigned epicsShareAPI ca_read_access ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.accessRights(guard).readPermit(); +} + +/* + * ca_write_access () + */ +unsigned epicsShareAPI ca_write_access ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.accessRights(guard).writePermit(); +} + +/* + * ca_name () + */ +const char * epicsShareAPI ca_name ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.pName ( guard ); +} + +unsigned epicsShareAPI ca_search_attempts ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.searchAttempts ( guard ); +} + +double epicsShareAPI ca_beacon_period ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.beaconPeriod ( guard ); +} + +double epicsShareAPI ca_receive_watchdog_delay ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.receiveWatchdogDelay ( guard ); +} + +/* + * ca_v42_ok(chid chan) + */ +int epicsShareAPI ca_v42_ok ( chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.ca_v42_ok ( guard ); +} + + diff --git a/src/ca/oldSubscription.cpp b/src/ca/oldSubscription.cpp new file mode 100644 index 000000000..34b58a48d --- /dev/null +++ b/src/ca/oldSubscription.cpp @@ -0,0 +1,107 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" + +oldSubscription::oldSubscription ( + epicsGuard < epicsMutex > & guard, + oldChannelNotify & chanIn, cacChannel & io, + unsigned type, arrayElementCount nElem, unsigned mask, + caEventCallBackFunc * pFuncIn, void * pPrivateIn, + evid * pEventId ) : + chan ( chanIn ), id ( UINT_MAX ), pFunc ( pFuncIn ), + pPrivate ( pPrivateIn ) +{ + // The users event id *must* be set prior to potentially + // calling his callback from within subscribe. + if ( pEventId ) { + *pEventId = this; + } + io.subscribe ( guard, type, nElem, mask, *this, &this->id ); + // Dont touch this pointer after this point because the + // 1st update callback might cancel the subscription and + // thereby destroy this object. +} + +oldSubscription::~oldSubscription () +{ +} + +void oldSubscription::current ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount count, const void * pData ) +{ + struct event_handler_args args; + args.usr = this->pPrivate; + args.chid = & this->chan; + args.type = static_cast < long > ( type ); + args.count = static_cast < long > ( count ); + args.status = ECA_NORMAL; + args.dbr = pData; + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } +} + +void oldSubscription::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char * /* pContext */, + unsigned type, arrayElementCount count ) +{ + if ( status == ECA_CHANDESTROY ) { + ca_client_context & cac = this->chan.getClientCtx (); + cac.destroySubscription ( guard, *this ); + } + else if ( status != ECA_DISCONN ) { + struct event_handler_args args; + args.usr = this->pPrivate; + args.chid = & this->chan; + args.type = type; + args.count = count; + args.status = status; + args.dbr = 0; + caEventCallBackFunc * pFuncTmp = this->pFunc; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } + } +} + +void oldSubscription::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + diff --git a/src/ca/putCallback.cpp b/src/ca/putCallback.cpp new file mode 100644 index 000000000..bff1dd8a4 --- /dev/null +++ b/src/ca/putCallback.cpp @@ -0,0 +1,110 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" + +putCallback::putCallback ( + oldChannelNotify & chanIn, caEventCallBackFunc * pFuncIn, + void * pPrivateIn ) : + chan ( chanIn ), pFunc ( pFuncIn ), pPrivate ( pPrivateIn ) +{ +} + +putCallback::~putCallback () +{ +} + +void putCallback::completion ( epicsGuard < epicsMutex > & guard ) +{ + struct event_handler_args args; + + args.usr = this->pPrivate; + args.chid = & this->chan; + args.type = TYPENOTCONN; + args.count = 0; + args.status = ECA_NORMAL; + args.dbr = 0; + caEventCallBackFunc * pFuncTmp = this->pFunc; + // fetch client context and destroy prior to releasing + // the lock and calling cb in case they destroy channel there + this->chan.getClientCtx().destroyPutCallback ( guard, *this ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } +} + +void putCallback::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char * /* pContext */, + unsigned type, arrayElementCount count ) +{ + if ( status != ECA_CHANDESTROY ) { + struct event_handler_args args; + args.usr = this->pPrivate; + args.chid = & this->chan; + args.type = type; + args.count = count; + args.status = status; + args.dbr = 0; + caEventCallBackFunc * pFuncTmp = this->pFunc; + // fetch client context and destroy prior to releasing + // the lock and calling cb in case they destroy channel there + this->chan.getClientCtx().destroyPutCallback ( guard, *this ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + ( *pFuncTmp ) ( args ); + } + } + else { + this->chan.getClientCtx().destroyPutCallback ( guard, *this ); + } +} + +void * putCallback::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void putCallback::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + diff --git a/src/ca/repeater.cpp b/src/ca/repeater.cpp new file mode 100644 index 000000000..64016aed6 --- /dev/null +++ b/src/ca/repeater.cpp @@ -0,0 +1,607 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * REPEATER.cpp + * + * CA broadcast repeater + * + * Author: Jeff Hill + * Date: 3-27-90 + * + * Control System Software for the GTA Project + * + * Copyright 1988, 1989, the Regents of the University of California. + * + * This software was produced under a U.S. Government contract + * (W-7405-ENG-36) at the Los Alamos National Laboratory, which is + * operated by the University of California for the U.S. Department + * of Energy. + * + * Developed by the Controls and Automation Group (AT-8) + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Direct inqueries to: + * Jeff HIll, AT-8, Mail Stop H820 + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * Phone: (505) 665-1831 + * E-mail: johill@lanl.gov + * + * PURPOSE: + * Broadcasts fan out over the LAN, but old IP kernels do not allow + * two processes on the same machine to get the same broadcast + * (and modern IP kernels do not allow two processes on the same machine + * to receive the same unicast). + * + * This code fans out UDP messages sent to the CA repeater port + * to all CA client processes that have subscribed. + * + */ + +/* + * It would be preferable to avoid using the repeater on multicast enhanced + * IP kernels, but this is not going to work in all situations because + * (according to Steven's TCP/IP illustrated volume I) if a broadcast is + * received it goes to all sockets on the same port, but if a unicast is + * received it goes to only one of the sockets on the same port (we can only + * guess at which one it will be). + * + * I have observed this behavior under winsock II: + * o only one of the sockets on the same port receives the message if we + * send to the loopback address + * o both of the sockets on the same port receives the message if we send + * to the broadcast address + */ + +/* verifyClients() Mechanism + * + * This is required because Solaris and HPUX have half baked versions + * of sockets. + * + * As written, the repeater should be robust against situations where the + * IP kernel doesn't implement UDP disconnect on receiving ICMP port + * unreachable errors from the destination process. As I recall, this + * change was required in the repeater code when we ported from sunos4 to + * Solaris. To avoid unreasonable overhead, I decided at the time to check + * the validity of all existing connections only when a new client + * registers with the repeater (and not when fanning out each beacon + * received). -- Jeff + */ + +#include +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "tsDLList.h" +#include "envDefs.h" +#include "tsFreeList.h" +#include "osiWireFormat.h" +#include "taskwd.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "caProto.h" +#include "udpiiu.h" +#include "repeaterClient.h" + + +/* + * these can be external since there is only one instance + * per machine so we dont care about reentrancy + */ +static tsDLList < repeaterClient > client_list; + +static const unsigned short PORT_ANY = 0u; + +/* + * makeSocket() + */ +static bool makeSocket ( unsigned short port, bool reuseAddr, SOCKET * pSock ) +{ + int status; + union { + struct sockaddr_in ia; + struct sockaddr sa; + } bd; + + SOCKET sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, 0 ); + if ( sock == INVALID_SOCKET ) { + return false; + } + + /* + * no need to bind if unconstrained + */ + if ( port != PORT_ANY ) { + + memset ( (char *) &bd, 0, sizeof (bd) ); + bd.ia.sin_family = AF_INET; + bd.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); + bd.ia.sin_port = htons ( port ); + status = bind ( sock, &bd.sa, (int) sizeof(bd) ); + if ( status < 0 ) { + epicsSocketDestroy ( sock ); + return false; + } + if ( reuseAddr ) { + epicsSocketEnableAddressReuseDuringTimeWaitState ( sock ); + } + } + *pSock = sock; + return true; +} + +repeaterClient::repeaterClient ( const osiSockAddr &fromIn ) : + from ( fromIn ), sock ( INVALID_SOCKET ) +{ +#ifdef DEBUG + unsigned port = ntohs ( from.ia.sin_port ); + debugPrintf ( ( "new client %u\n", port ) ); +#endif +} + +bool repeaterClient::connect () +{ + int status; + + if ( ! makeSocket ( PORT_ANY, false, & this->sock ) ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "%s: no client sock because \"%s\"\n", + __FILE__, sockErrBuf ); + return false; + } + + status = ::connect ( this->sock, &this->from.sa, sizeof ( this->from.sa ) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "%s: unable to connect client sock because \"%s\"\n", + __FILE__, sockErrBuf ); + return false; + } + + return true; +} + +bool repeaterClient::sendConfirm () // X aCC 361 +{ + int status; + + caHdr confirm; + memset ( (char *) &confirm, '\0', sizeof (confirm) ); + AlignedWireRef < epicsUInt16 > ( confirm.m_cmmd ) = REPEATER_CONFIRM; + confirm.m_available = this->from.ia.sin_addr.s_addr; + status = send ( this->sock, (char *) &confirm, + sizeof (confirm), 0 ); + if ( status >= 0 ) { + assert ( status == sizeof ( confirm ) ); + return true; + } + else if ( SOCKERRNO == SOCK_ECONNREFUSED ) { + return false; + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + debugPrintf ( ( "CA Repeater: confirm req err was \"%s\"\n", sockErrBuf) ); + return false; + } +} + +bool repeaterClient::sendMessage ( const void *pBuf, unsigned bufSize ) // X aCC 361 +{ + int status; + + status = send ( this->sock, (char *) pBuf, bufSize, 0 ); + if ( status >= 0 ) { + assert ( static_cast ( status ) == bufSize ); +#ifdef DEBUG + epicsUInt16 port = ntohs ( this->from.ia.sin_port ); + debugPrintf ( ("Sent to %u\n", port ) ); +#endif + return true; + } + else { + int errnoCpy = SOCKERRNO; + if ( errnoCpy == SOCK_ECONNREFUSED ) { +#ifdef DEBUG + epicsUInt16 port = ntohs ( this->from.ia.sin_port ); + debugPrintf ( ("Client refused message %u\n", port ) ); +#endif + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + debugPrintf ( ( "CA Repeater: UDP send err was \"%s\"\n", sockErrBuf) ); + } + return false; + } +} + +repeaterClient::~repeaterClient () +{ + if ( this->sock != INVALID_SOCKET ) { + epicsSocketDestroy ( this->sock ); + } +#ifdef DEBUG + epicsUInt16 port = ntohs ( this->from.ia.sin_port ); + debugPrintf ( ( "Deleted client %u\n", port ) ); +#endif +} + +void * repeaterClient::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void repeaterClient::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void * repeaterClient::operator new ( size_t size, + tsFreeList < repeaterClient, 0x20 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +void repeaterClient::operator delete ( void *pCadaver, + tsFreeList < repeaterClient, 0x20 > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline unsigned short repeaterClient::port () const +{ + return ntohs ( this->from.ia.sin_port ); +} + +inline bool repeaterClient::identicalAddress ( const osiSockAddr &fromIn ) +{ + if ( fromIn.sa.sa_family == this->from.sa.sa_family ) { + if ( fromIn.ia.sin_port == this->from.ia.sin_port) { + if ( fromIn.ia.sin_addr.s_addr == this->from.ia.sin_addr.s_addr ) { + return true; + } + } + } + return false; +} + +inline bool repeaterClient::identicalPort ( const osiSockAddr &fromIn ) +{ + if ( fromIn.sa.sa_family == this->from.sa.sa_family ) { + if ( fromIn.ia.sin_port == this->from.ia.sin_port) { + return true; + } + } + return false; +} + +bool repeaterClient::verify () // X aCC 361 +{ + SOCKET tmpSock; + bool success = makeSocket ( this->port (), false, & tmpSock ); + if ( success ) { + epicsSocketDestroy ( tmpSock ); + } + else { + if ( SOCKERRNO != SOCK_EADDRINUSE ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "CA Repeater: bind test err was \"%s\"\n", + sockErrBuf ); + } + } + return ! success; +} + + +/* + * verifyClients() + */ +static void verifyClients ( tsFreeList < repeaterClient, 0x20 > & freeList ) +{ + static tsDLList < repeaterClient > theClients; + repeaterClient *pclient; + + while ( ( pclient = client_list.get () ) ) { + if ( pclient->verify () ) { + theClients.add ( *pclient ); + } + else { + pclient->~repeaterClient (); + freeList.release ( pclient ); + } + } + client_list.add ( theClients ); +} + +/* + * fanOut() + */ +static void fanOut ( const osiSockAddr & from, const void * pMsg, + unsigned msgSize, tsFreeList < repeaterClient, 0x20 > & freeList ) +{ + static tsDLList < repeaterClient > theClients; + repeaterClient *pclient; + + while ( ( pclient = client_list.get () ) ) { + theClients.add ( *pclient ); + /* Dont reflect back to sender */ + if ( pclient->identicalAddress ( from ) ) { + continue; + } + + if ( ! pclient->sendMessage ( pMsg, msgSize ) ) { + if ( ! pclient->verify () ) { + theClients.remove ( *pclient ); + pclient->~repeaterClient (); + freeList.release ( pclient ); + } + } + } + + client_list.add ( theClients ); +} + +/* + * register_new_client() + */ +static void register_new_client ( osiSockAddr & from, + tsFreeList < repeaterClient, 0x20 > & freeList ) +{ + bool newClient = false; + int status; + + if ( from.sa.sa_family != AF_INET ) { + return; + } + + /* + * the repeater and its clients must be on the same host + */ + if ( INADDR_LOOPBACK != ntohl ( from.ia.sin_addr.s_addr ) ) { + static SOCKET testSock = INVALID_SOCKET; + static bool init = false; + + if ( ! init ) { + SOCKET sock; + if ( ! makeSocket ( PORT_ANY, true, & sock ) ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "%s: Unable to create repeater bind test socket because \"%s\"\n", + __FILE__, sockErrBuf ); + } + else { + testSock = sock; + } + init = true; + } + + /* + * Unfortunately on 3.13 beta 11 and before the + * repeater would not always allow the loopback address + * as a local client address so current clients alternate + * between the address of the first non-loopback interface + * found and the loopback addresss when subscribing with + * the CA repeater until all CA repeaters have been updated + * to current code. + */ + if ( testSock != INVALID_SOCKET ) { + osiSockAddr addr; + + addr = from; + addr.ia.sin_port = PORT_ANY; + + /* we can only bind to a local address */ + status = bind ( testSock, &addr.sa, sizeof ( addr ) ); + if ( status ) { + return; + } + } + else { + return; + } + } + + tsDLIter < repeaterClient > pclient = client_list.firstIter (); + while ( pclient.valid () ) { + if ( pclient->identicalPort ( from ) ) { + break; + } + pclient++; + } + + repeaterClient *pNewClient; + if ( pclient.valid () ) { + pNewClient = pclient.pointer (); + } + else { + pNewClient = new ( freeList ) repeaterClient ( from ); + if ( ! pNewClient ) { + fprintf ( stderr, "%s: no memory for new client\n", __FILE__ ); + return; + } + if ( ! pNewClient->connect () ) { + pNewClient->~repeaterClient (); + freeList.release ( pNewClient ); + return; + } + client_list.add ( *pNewClient ); + newClient = true; + } + + if ( ! pNewClient->sendConfirm () ) { + client_list.remove ( *pNewClient ); + pNewClient->~repeaterClient (); + freeList.release ( pNewClient ); +# ifdef DEBUG + epicsUInt16 port = ntohs ( from.ia.sin_port ); + debugPrintf ( ( "Deleted repeater client=%u (error while sending ack)\n", + port ) ); +# endif + } + + /* + * send a noop message to all other clients so that we dont + * accumulate sockets when there are no beacons + */ + caHdr noop; + memset ( (char *) &noop, '\0', sizeof ( noop ) ); + AlignedWireRef < epicsUInt16 > ( noop.m_cmmd ) = CA_PROTO_VERSION; + fanOut ( from, &noop, sizeof ( noop ), freeList ); + + if ( newClient ) { + /* + * For HPUX and Solaris we need to verify that the clients + * have not gone away - because an ICMP error return does not + * get through to send(), which returns no error code. + * + * This is done each time that a new client is created. + * See also the note in the file header. + * + * This is done here in order to avoid deleting a client + * prior to sending its confirm message. + */ + verifyClients ( freeList ); + } +} + + +/* + * ca_repeater () + */ +void ca_repeater () +{ + tsFreeList < repeaterClient, 0x20 > freeList; + int size; + SOCKET sock; + osiSockAddr from; + unsigned short port; + char * pBuf; + + pBuf = new char [MAX_UDP_RECV]; + + { + bool success = osiSockAttach(); + assert ( success ); + } + + port = envGetInetPortConfigParam ( & EPICS_CA_REPEATER_PORT, + static_cast (CA_REPEATER_PORT) ); + if ( ! makeSocket ( port, true, & sock ) ) { + /* + * test for server was already started + */ + if ( SOCKERRNO == SOCK_EADDRINUSE ) { + osiSockRelease (); + debugPrintf ( ( "CA Repeater: exiting because a repeater is already running\n" ) ); + return; + } + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "%s: Unable to create repeater socket because \"%s\" - fatal\n", + __FILE__, sockErrBuf ); + osiSockRelease (); + delete [] pBuf; + return; + } + + debugPrintf ( ( "CA Repeater: Attached and initialized\n" ) ); + + while ( true ) { + osiSocklen_t from_size = sizeof ( from ); + size = recvfrom ( sock, pBuf, MAX_UDP_RECV, 0, + &from.sa, &from_size ); + if ( size < 0 ) { + int errnoCpy = SOCKERRNO; + // Avoid spurious ECONNREFUSED bug in linux + if ( errnoCpy == SOCK_ECONNREFUSED ) { + continue; + } + // Avoid ECONNRESET from connected socket in windows + if ( errnoCpy == SOCK_ECONNRESET ) { + continue; + } + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "CA Repeater: unexpected UDP recv err: %s\n", + sockErrBuf ); + continue; + } + + caHdr * pMsg = ( caHdr * ) pBuf; + + /* + * both zero length message and a registration message + * will register a new client + */ + if ( ( (size_t) size) >= sizeof (*pMsg) ) { + if ( AlignedWireRef < epicsUInt16 > ( pMsg->m_cmmd ) == REPEATER_REGISTER ) { + register_new_client ( from, freeList ); + + /* + * strip register client message + */ + pMsg++; + size -= sizeof ( *pMsg ); + if ( size==0 ) { + continue; + } + } + else if ( AlignedWireRef < epicsUInt16 > ( pMsg->m_cmmd ) == CA_PROTO_RSRV_IS_UP ) { + if ( pMsg->m_available == 0u ) { + pMsg->m_available = from.ia.sin_addr.s_addr; + } + } + } + else if ( size == 0 ) { + register_new_client ( from, freeList ); + continue; + } + + fanOut ( from, pMsg, size, freeList ); + } +} + +/* + * caRepeaterThread () + */ +extern "C" void caRepeaterThread ( void * /* pDummy */ ) +{ + taskwdInsert ( epicsThreadGetIdSelf(), NULL, NULL ); + ca_repeater (); +} + + diff --git a/src/ca/repeaterClient.h b/src/ca/repeaterClient.h new file mode 100644 index 000000000..c57aeaefe --- /dev/null +++ b/src/ca/repeaterClient.h @@ -0,0 +1,73 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef repeaterClienth +#define repeaterClienth + +#ifdef epicsExportSharedSymbols +# define repeaterClienth_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "tsDLList.h" +#include "tsFreeList.h" +#include "compilerDependencies.h" + +#ifdef repeaterClienth_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +union osiSockAddr; + +/* + * one socket per client so we will get the ECONNREFUSED + * error code (and then delete the client) + */ +class repeaterClient : public tsDLNode < repeaterClient > { +public: + repeaterClient ( const osiSockAddr & from ); + ~repeaterClient (); + bool connect (); + bool sendConfirm (); + bool sendMessage ( const void *pBuf, unsigned bufSize ); + bool verify (); + bool identicalAddress ( const osiSockAddr &from ); + bool identicalPort ( const osiSockAddr &from ); + void * operator new ( size_t size, + tsFreeList < repeaterClient, 0x20 > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < repeaterClient, 0x20 > & )) +private: + osiSockAddr from; + SOCKET sock; + unsigned short port () const; + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +#endif // repeaterClienth + + diff --git a/src/ca/repeaterSubscribeTimer.cpp b/src/ca/repeaterSubscribeTimer.cpp new file mode 100644 index 000000000..337ff00ba --- /dev/null +++ b/src/ca/repeaterSubscribeTimer.cpp @@ -0,0 +1,104 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + * + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "cac.h" +#include "iocinf.h" +#include "repeaterSubscribeTimer.h" + +#define epicsExportSharedSymbols +#include "udpiiu.h" +#undef epicsExportSharedSymbols + +static const double repeaterSubscribeTimerInitialPeriod = 10.0; // sec +static const double repeaterSubscribeTimerPeriod = 1.0; // sec + +repeaterSubscribeTimer::repeaterSubscribeTimer ( + repeaterTimerNotify & iiuIn, epicsTimerQueue & queueIn, + epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn ) : + timer ( queueIn.createTimer () ), iiu ( iiuIn ), + cbMutex ( cbMutexIn ),ctxNotify ( ctxNotifyIn ), + attempts ( 0 ), registered ( false ), once ( false ) +{ +} + +repeaterSubscribeTimer::~repeaterSubscribeTimer () +{ + this->timer.destroy (); +} + +void repeaterSubscribeTimer::start () +{ + this->timer.start ( + *this, repeaterSubscribeTimerInitialPeriod ); +} + +void repeaterSubscribeTimer::shutdown ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); + this->timer.cancel (); + } +} + +epicsTimerNotify::expireStatus repeaterSubscribeTimer:: + expire ( const epicsTime & /* currentTime */ ) // X aCC 361 +{ + static const unsigned nTriesToMsg = 50; + if ( this->attempts > nTriesToMsg && ! this->once ) { + callbackManager mgr ( this->ctxNotify, this->cbMutex ); + this->iiu.printFormated ( mgr.cbGuard, + "CA client library is unable to contact CA repeater after %u tries.\n", + nTriesToMsg ); + this->iiu.printFormated ( mgr.cbGuard, + "Silence this message by starting a CA repeater daemon\n") ; + this->iiu.printFormated ( mgr.cbGuard, + "or by calling ca_pend_event() and or ca_poll() more often.\n" ); + this->once = true; + } + + this->iiu.repeaterRegistrationMessage ( this->attempts ); + this->attempts++; + + if ( this->registered ) { + return noRestart; + } + else { + return expireStatus ( restart, repeaterSubscribeTimerPeriod ); + } +} + +void repeaterSubscribeTimer::show ( unsigned /* level */ ) const +{ + ::printf ( "repeater subscribe timer: attempts=%u registered=%u once=%u\n", + this->attempts, this->registered, this->once ); +} + +void repeaterSubscribeTimer::confirmNotify () +{ + this->registered = true; +} + +repeaterTimerNotify::~repeaterTimerNotify () {} diff --git a/src/ca/repeaterSubscribeTimer.h b/src/ca/repeaterSubscribeTimer.h new file mode 100644 index 000000000..0a23d16d3 --- /dev/null +++ b/src/ca/repeaterSubscribeTimer.h @@ -0,0 +1,81 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef repeaterSubscribeTimerh +#define repeaterSubscribeTimerh + +#include "epicsTimer.h" + +#ifdef epicsExportSharedSymbols +# define repeaterSubscribeTimerh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsTimer.h" + +#ifdef repeaterSubscribeTimerh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class epicsMutex; +class cacContextNotify; + +class repeaterTimerNotify { // X aCC 655 +public: + virtual ~repeaterTimerNotify () = 0; + virtual void repeaterRegistrationMessage ( + unsigned attemptNumber ) = 0; + virtual int printFormated ( + epicsGuard < epicsMutex > & callbackControl, + const char * pformat, ... ) = 0; +}; + +class repeaterSubscribeTimer : private epicsTimerNotify { +public: + repeaterSubscribeTimer ( + repeaterTimerNotify &, epicsTimerQueue &, + epicsMutex & cbMutex, cacContextNotify & ctxNotify ); + virtual ~repeaterSubscribeTimer (); + void start (); + void shutdown ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void confirmNotify (); + void show ( unsigned level ) const; +private: + epicsTimer & timer; + repeaterTimerNotify & iiu; + epicsMutex & cbMutex; + cacContextNotify & ctxNotify; + unsigned attempts; + bool registered; + bool once; + expireStatus expire ( const epicsTime & currentTime ); + repeaterSubscribeTimer ( const repeaterSubscribeTimer & ); + repeaterSubscribeTimer & operator = ( const repeaterSubscribeTimer & ); +}; + +#endif // ifdef repeaterSubscribeTimerh diff --git a/src/ca/searchTimer.cpp b/src/ca/searchTimer.cpp new file mode 100644 index 000000000..c7aa980ae --- /dev/null +++ b/src/ca/searchTimer.cpp @@ -0,0 +1,400 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// +// L O S A L A M O S +// Los Alamos National Laboratory +// Los Alamos, New Mexico 87545 +// +// Copyright, 1986, The Regents of the University of California. +// +// Author: Jeff Hill +// + +#include +#include // vxWorks 6.0 requires this include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "tsMinMax.h" +#include "envDefs.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "udpiiu.h" +#include "nciu.h" + +static const unsigned initialTriesPerFrame = 1u; // initial UDP frames per search try +static const unsigned maxTriesPerFrame = 64u; // max UDP frames per search try + +// +// searchTimer::searchTimer () +// +searchTimer::searchTimer ( + searchTimerNotify & iiuIn, + epicsTimerQueue & queueIn, + const unsigned indexIn, + epicsMutex & mutexIn, + bool boostPossibleIn ) : + timeAtLastSend ( epicsTime::getCurrent () ), + timer ( queueIn.createTimer () ), + iiu ( iiuIn ), + mutex ( mutexIn ), + framesPerTry ( initialTriesPerFrame ), + framesPerTryCongestThresh ( DBL_MAX ), + retry ( 0 ), + searchAttempts ( 0u ), + searchResponses ( 0u ), + index ( indexIn ), + dgSeqNoAtTimerExpireBegin ( 0u ), + dgSeqNoAtTimerExpireEnd ( 0u ), + boostPossible ( boostPossibleIn ), + stopped ( false ) +{ +} + +void searchTimer::start ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->timer.start ( *this, this->period ( guard ) ); +} + +searchTimer::~searchTimer () +{ + assert ( this->chanListReqPending.count() == 0 ); + assert ( this->chanListRespPending.count() == 0 ); + this->timer.destroy (); +} + +void searchTimer::shutdown ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + this->stopped = true; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); + this->timer.cancel (); + } + } + + while ( nciu * pChan = this->chanListReqPending.get () ) { + pChan->channelNode::listMember = + channelNode::cs_none; + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + while ( nciu * pChan = this->chanListRespPending.get () ) { + pChan->channelNode::listMember = + channelNode::cs_none; + pChan->serviceShutdownNotify ( cbGuard, guard ); + } +} + +void searchTimer::installChannel ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + this->chanListReqPending.add ( chan ); + chan.channelNode::setReqPendingState ( guard, this->index ); +} + +void searchTimer::moveChannels ( + epicsGuard < epicsMutex > & guard, searchTimer & dest ) +{ + while ( nciu * pChan = this->chanListRespPending.get () ) { + if ( this->searchAttempts > 0 ) { + this->searchAttempts--; + } + dest.installChannel ( guard, *pChan ); + } + while ( nciu * pChan = this->chanListReqPending.get () ) { + dest.installChannel ( guard, *pChan ); + } +} + +// +// searchTimer::expire () +// +epicsTimerNotify::expireStatus searchTimer::expire ( + const epicsTime & currentTime ) // X aCC 361 +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + while ( nciu * pChan = this->chanListRespPending.get () ) { + pChan->channelNode::listMember = + channelNode::cs_none; + this->iiu.noSearchRespNotify ( + guard, *pChan, this->index ); + } + + this->timeAtLastSend = currentTime; + + // boost search period for channels not recently + // searched for if there was some success + if ( this->searchResponses && this->boostPossible ) { + while ( nciu * pChan = this->chanListReqPending.get () ) { + pChan->channelNode::listMember = + channelNode::cs_none; + this->iiu.boostChannel ( guard, *pChan ); + } + } + + if ( this->searchAttempts ) { +#if 0 + // + // dynamically adjust the number of UDP frames per + // try depending how many search requests are not + // replied to + // + // The variable this->framesPerTry + // determines the number of UDP frames to be sent + // each time that expire() is called. + // If this value is too high we will waste some + // network bandwidth. If it is too low we will + // use very little of the incoming UDP message + // buffer associated with the server's port and + // will therefore take longer to connect. We + // initialize this->framesPerTry to a prime number + // so that it is less likely that the + // same channel is in the last UDP frame + // sent every time that this is called (and + // potentially discarded by a CA server with + // a small UDP input queue). + // + // increase frames per try only if we see better than + // a 93.75% success rate for one pass through the list + // + if ( this->searchResponses > + ( this->searchAttempts - (this->searchAttempts/16u) ) ) { + // increase UDP frames per try if we have a good score + if ( this->framesPerTry < maxTriesPerFrame ) { + // a congestion avoidance threshold similar to TCP is now used + if ( this->framesPerTry < this->framesPerTryCongestThresh ) { + this->framesPerTry += this->framesPerTry; + } + else { + this->framesPerTry += (this->framesPerTry/8) + 1; + } + debugPrintf ( ("Increasing frame count to %u t=%u r=%u\n", + this->framesPerTry, this->searchAttempts, this->searchResponses) ); + } + } + // if we detect congestion because we have less than a 87.5% success + // rate then gradually reduce the frames per try + else if ( this->searchResponses < + ( this->searchAttempts - (this->searchAttempts/8u) ) ) { + if ( this->framesPerTry > 1 ) { + this->framesPerTry--; + } + this->framesPerTryCongestThresh = this->framesPerTry/2 + 1; + debugPrintf ( ("Congestion detected - set frames per try to %f t=%u r=%u\n", + this->framesPerTry, this->searchAttempts, this->searchResponses) ); + } +#else + if ( this->searchResponses == this->searchAttempts ) { + // increase UDP frames per try if we have a good score + if ( this->framesPerTry < maxTriesPerFrame ) { + // a congestion avoidance threshold similar to TCP is now used + if ( this->framesPerTry < this->framesPerTryCongestThresh ) { + double doubled = 2 * this->framesPerTry; + if ( doubled > this->framesPerTryCongestThresh ) { + this->framesPerTry = this->framesPerTryCongestThresh; + } + else { + this->framesPerTry = doubled; + } + } + else { + this->framesPerTry += 1.0 / this->framesPerTry; + } + debugPrintf ( ("Increasing frame count to %g t=%u r=%u\n", + this->framesPerTry, this->searchAttempts, this->searchResponses) ); + } + } + else { + this->framesPerTryCongestThresh = this->framesPerTry / 2.0; + this->framesPerTry = 1u; + debugPrintf ( ("Congestion detected - set frames per try to %g t=%u r=%u\n", + this->framesPerTry, this->searchAttempts, this->searchResponses) ); + } +#endif + } + + this->dgSeqNoAtTimerExpireBegin = + this->iiu.datagramSeqNumber ( guard ); + + this->searchAttempts = 0; + this->searchResponses = 0; + + unsigned nFrameSent = 0u; + while ( true ) { + nciu * pChan = this->chanListReqPending.get (); + if ( ! pChan ) { + break; + } + + pChan->channelNode::listMember = + channelNode::cs_none; + + bool success = pChan->searchMsg ( guard ); + if ( ! success ) { + if ( this->iiu.datagramFlush ( guard, currentTime ) ) { + nFrameSent++; + if ( nFrameSent < this->framesPerTry ) { + success = pChan->searchMsg ( guard ); + } + } + if ( ! success ) { + this->chanListReqPending.push ( *pChan ); + pChan->channelNode::setReqPendingState ( + guard, this->index ); + break; + } + } + + this->chanListRespPending.add ( *pChan ); + pChan->channelNode::setRespPendingState ( + guard, this->index ); + + if ( this->searchAttempts < UINT_MAX ) { + this->searchAttempts++; + } + } + + // flush out the search request buffer + if ( this->iiu.datagramFlush ( guard, currentTime ) ) { + nFrameSent++; + } + + this->dgSeqNoAtTimerExpireEnd = + this->iiu.datagramSeqNumber ( guard ) - 1u; + +# ifdef DEBUG + if ( this->searchAttempts ) { + char buf[64]; + currentTime.strftime ( buf, sizeof(buf), "%M:%S.%09f"); + debugPrintf ( ("sent %u delay sec=%f Rts=%s\n", + nFrameSent, this->period(), buf ) ); + } +# endif + + return expireStatus ( restart, this->period ( guard ) ); +} + +void searchTimer :: show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + ::printf ( "searchTimer with period %f\n", this->period ( guard ) ); + if ( level > 0 ) { + ::printf ( "channels with search request pending = %u\n", + this->chanListReqPending.count () ); + if ( level > 1u ) { + tsDLIterConst < nciu > pChan = + this->chanListReqPending.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + ::printf ( "channels with search response pending = %u\n", + this->chanListRespPending.count () ); + if ( level > 1u ) { + tsDLIterConst < nciu > pChan = + this->chanListRespPending.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + } +} + +// +// Reset the delay to the next search request if we get +// at least one response. However, dont reset this delay if we +// get a delayed response to an old search request. +// +void searchTimer::uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > & guard, nciu & chan, + ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, + const epicsTime & currentTime ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->uninstallChan ( guard, chan ); + + if ( this->stopped ) { + return; + } + + bool validResponse = true; + if ( seqNumberIsValid ) { + validResponse = + this->dgSeqNoAtTimerExpireBegin <= respDatagramSeqNo && + this->dgSeqNoAtTimerExpireEnd >= respDatagramSeqNo; + } + + // if we receive a successful response then reset to a + // reasonable timer period + if ( validResponse ) { + double measured = currentTime - this->timeAtLastSend; + this->iiu.updateRTTE ( guard, measured ); + + if ( this->searchResponses < UINT_MAX ) { + this->searchResponses++; + if ( this->searchResponses == this->searchAttempts ) { + if ( this->chanListReqPending.count () ) { + // + // when we get 100% success immediately + // send another search request + // + debugPrintf ( ( "All requests succesful, set timer delay to zero\n" ) ); + this->timer.start ( *this, currentTime ); + } + } + } + } +} + +void searchTimer::uninstallChan ( + epicsGuard < epicsMutex > & cacGuard, nciu & chan ) +{ + cacGuard.assertIdenticalMutex ( this->mutex ); + unsigned ulistmem = + static_cast ( chan.channelNode::listMember ); + unsigned uReqBase = + static_cast ( channelNode::cs_searchReqPending0 ); + if ( ulistmem == this->index + uReqBase ) { + this->chanListReqPending.remove ( chan ); + } + else { + unsigned uRespBase = + static_cast ( + channelNode::cs_searchRespPending0 ); + if ( ulistmem == this->index + uRespBase ) { + this->chanListRespPending.remove ( chan ); + } + else { + throw std::runtime_error ( + "uninstalling channel search timer, but channel " + "state is wrong" ); + } + } + chan.channelNode::listMember = channelNode::cs_none; +} + +double searchTimer::period ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return (1 << this->index ) * this->iiu.getRTTE ( guard ); +} + +searchTimerNotify::~searchTimerNotify () {} diff --git a/src/ca/searchTimer.h b/src/ca/searchTimer.h new file mode 100644 index 000000000..2df980ded --- /dev/null +++ b/src/ca/searchTimer.h @@ -0,0 +1,108 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// +// +// +// L O S A L A M O S +// Los Alamos National Laboratory +// Los Alamos, New Mexico 87545 +// +// Copyright, 1986, The Regents of the University of California. +// +// +// Author Jeffrey O. Hill +// johill@lanl.gov +// 505 665 1831 +// + +#ifndef searchTimerh +#define searchTimerh + +#ifdef epicsExportSharedSymbols +# define searchTimerh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsTimer.h" + +#ifdef searchTimerh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "caProto.h" +#include "netiiu.h" + +class searchTimerNotify { // X aCC 655 +public: + virtual ~searchTimerNotify () = 0; + virtual void boostChannel ( + epicsGuard < epicsMutex > &, nciu & ) = 0; + virtual void noSearchRespNotify ( + epicsGuard < epicsMutex > &, nciu &, unsigned ) = 0; + virtual double getRTTE ( epicsGuard < epicsMutex > & ) const = 0; + virtual void updateRTTE ( epicsGuard < epicsMutex > &, double rtte ) = 0; + virtual bool datagramFlush ( + epicsGuard < epicsMutex > &, + const epicsTime & currentTime ) = 0; + virtual ca_uint32_t datagramSeqNumber ( + epicsGuard < epicsMutex > & ) const = 0; +}; + +class searchTimer : private epicsTimerNotify { +public: + searchTimer ( + class searchTimerNotify &, epicsTimerQueue &, + const unsigned index, epicsMutex &, + bool boostPossible ); + virtual ~searchTimer (); + void start ( epicsGuard < epicsMutex > & ); + void shutdown ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void moveChannels ( + epicsGuard < epicsMutex > &, searchTimer & dest ); + void installChannel ( + epicsGuard < epicsMutex > &, nciu & ); + void uninstallChan ( + epicsGuard < epicsMutex > &, nciu & ); + void uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > &, nciu &, + ca_uint32_t respDatagramSeqNo, bool seqNumberIsValid, + const epicsTime & currentTime ); + void show ( unsigned level ) const; +private: + tsDLList < nciu > chanListReqPending; + tsDLList < nciu > chanListRespPending; + epicsTime timeAtLastSend; + epicsTimer & timer; + searchTimerNotify & iiu; + epicsMutex & mutex; + double framesPerTry; /* # of UDP frames per search try */ + double framesPerTryCongestThresh; /* one half N tries w congest */ + unsigned retry; + unsigned searchAttempts; /* num search tries after last timer experation */ + unsigned searchResponses; /* num search resp after last timer experation */ + const unsigned index; + ca_uint32_t dgSeqNoAtTimerExpireBegin; + ca_uint32_t dgSeqNoAtTimerExpireEnd; + const bool boostPossible; + bool stopped; + + expireStatus expire ( const epicsTime & currentTime ); + double period ( epicsGuard < epicsMutex > & ) const; + searchTimer ( const searchTimer & ); // not implemented + searchTimer & operator = ( const searchTimer & ); // not implemented +}; + +#endif // ifdef searchTimerh diff --git a/src/ca/sgAutoPtr.h b/src/ca/sgAutoPtr.h new file mode 100644 index 000000000..b3753bb44 --- /dev/null +++ b/src/ca/sgAutoPtr.h @@ -0,0 +1,104 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef sgAutoPtrh +#define sgAutoPtrh + +template < class T > +class sgAutoPtr { +public: + sgAutoPtr ( epicsGuard < epicsMutex > &, + struct CASG &, tsDLList < syncGroupNotify > & ); + ~sgAutoPtr (); + sgAutoPtr < T > & operator = ( T * ); + T * operator -> (); + T & operator * (); + T * get (); + T * release (); +private: + tsDLList < syncGroupNotify > & list; + T * pNotify; + struct CASG & sg; + epicsGuard < epicsMutex > & guard; + sgAutoPtr & operator = ( const sgAutoPtr & ); +}; + +template < class T > +inline sgAutoPtr < T > :: sgAutoPtr ( + epicsGuard < epicsMutex > & guardIn, + struct CASG & sgIn, tsDLList < syncGroupNotify > & listIn ) : + list ( listIn ), pNotify ( 0 ), sg ( sgIn ), guard ( guardIn ) +{ +} + +template < class T > +inline sgAutoPtr < T > :: ~sgAutoPtr () +{ + if ( this->pNotify ) { + list.remove ( *this->pNotify ); + pNotify->destroy ( this->guard, this->sg ); + } +} + +template < class T > +inline sgAutoPtr < T > & sgAutoPtr < T > :: operator = ( T * pNotifyIn ) +{ + if ( this->pNotify ) { + list.remove ( *this->pNotify ); + pNotify->destroy ( this->guard, this->sg ); + } + this->pNotify = pNotifyIn; + list.add ( *this->pNotify ); + return *this; +} + +template < class T > +inline T * sgAutoPtr < T > :: operator -> () +{ + return this->pNotify; +} + +template < class T > +inline T & sgAutoPtr < T > :: operator * () +{ + assert ( this->pNotify ); + return * this->pNotify; +} + +template < class T > +inline T * sgAutoPtr < T > :: release () +{ + T * pTmp = this->pNotify; + this->pNotify = 0; + return pTmp; +} + +template < class T > +inline T * sgAutoPtr < T > :: get () +{ + return this->pNotify; +} + +#endif // sgAutoPtrh diff --git a/src/ca/syncGroup.h b/src/ca/syncGroup.h new file mode 100644 index 000000000..c41c9b5b8 --- /dev/null +++ b/src/ca/syncGroup.h @@ -0,0 +1,280 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef syncGrouph +#define syncGrouph + +#ifdef epicsExportSharedSymbols +# define syncGrouph_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "tsDLList.h" +#include "tsFreeList.h" +#include "resourceLib.h" +#include "epicsEvent.h" +#include "compilerDependencies.h" + +#ifdef syncGrouph_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "cadef.h" +#include "cacIO.h" + +static const unsigned CASG_MAGIC = 0xFAB4CAFE; + +// used to control access to CASG's recycle routines which +// should only be indirectly invoked by CASG when its lock +// is applied +class casgRecycle { // X aCC 655 +public: + virtual void recycleSyncGroupWriteNotify ( + epicsGuard < epicsMutex > &, class syncGroupWriteNotify & io ) = 0; + virtual void recycleSyncGroupReadNotify ( + epicsGuard < epicsMutex > &, class syncGroupReadNotify & io ) = 0; +protected: + virtual ~casgRecycle (); +}; + +class syncGroupNotify : public tsDLNode < syncGroupNotify > { +public: + syncGroupNotify (); + virtual void destroy ( + epicsGuard < epicsMutex > & guard, + casgRecycle & ) = 0; + virtual bool ioPending ( + epicsGuard < epicsMutex > & guard ) = 0; + virtual void cancel ( + epicsGuard < epicsMutex > & guard ) = 0; + virtual void show ( + epicsGuard < epicsMutex > &, + unsigned level ) const = 0; +protected: + virtual ~syncGroupNotify (); + syncGroupNotify ( const syncGroupNotify & ); + syncGroupNotify & operator = ( const syncGroupNotify & ); +}; + +class syncGroupReadNotify : public syncGroupNotify, public cacReadNotify { +public: + static syncGroupReadNotify * factory ( + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > &, + struct CASG &, chid, void *pValueIn ); + void destroy ( + epicsGuard < epicsMutex > & guard, + casgRecycle & ); + bool ioPending ( + epicsGuard < epicsMutex > & guard ); + void begin ( epicsGuard < epicsMutex > &, + unsigned type, arrayElementCount count ); + void cancel ( + epicsGuard < epicsMutex > & guard ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; +protected: + syncGroupReadNotify ( struct CASG & sgIn, chid, void * pValueIn ); + virtual ~syncGroupReadNotify (); +private: + chid chan; + struct CASG & sg; + void * pValue; + const unsigned magic; + cacChannel::ioid id; + bool idIsValid; + bool ioComplete; + void * operator new ( size_t ); + void operator delete ( void * ); + void * operator new ( size_t, + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & )) + void completion ( + epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pData ); + void exception ( + epicsGuard < epicsMutex > &, int status, + const char * pContext, unsigned type, arrayElementCount count ); + syncGroupReadNotify ( const syncGroupReadNotify & ); + syncGroupReadNotify & operator = ( const syncGroupReadNotify & ); +}; + +class syncGroupWriteNotify : public syncGroupNotify, public cacWriteNotify { +public: + static syncGroupWriteNotify * factory ( + tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &, + struct CASG &, chid ); + void destroy ( + epicsGuard < epicsMutex > & guard, + casgRecycle & ); + bool ioPending ( + epicsGuard < epicsMutex > & guard ); + void begin ( epicsGuard < epicsMutex > &, unsigned type, + arrayElementCount count, const void * pValueIn ); + void cancel ( + epicsGuard < epicsMutex > & guard ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; +protected: + syncGroupWriteNotify ( struct CASG &, chid ); + virtual ~syncGroupWriteNotify (); // allocate only from pool +private: + chid chan; + struct CASG & sg; + const unsigned magic; + cacChannel::ioid id; + bool idIsValid; + bool ioComplete; + void * operator new ( size_t ); + void operator delete ( void * ); + void * operator new ( size_t, + tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & )) + void completion ( epicsGuard < epicsMutex > & ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char *pContext, + unsigned type, arrayElementCount count ); + syncGroupWriteNotify ( const syncGroupWriteNotify & ); + syncGroupWriteNotify & operator = ( const syncGroupWriteNotify & ); +}; + +struct ca_client_context; + +template < class T > class sgAutoPtr; + +struct CASG : public chronIntIdRes < CASG >, private casgRecycle { +public: + CASG ( epicsGuard < epicsMutex > &, ca_client_context & cacIn ); + void destructor ( + epicsGuard < epicsMutex > & guard ); + bool ioComplete ( + epicsGuard < epicsMutex > & guard ); + bool verify ( epicsGuard < epicsMutex > & ) const; + int block ( epicsGuard < epicsMutex > * pcbGuard, + epicsGuard < epicsMutex > & guard, double timeout ); + void reset ( epicsGuard < epicsMutex > & guard ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; + void show ( unsigned level ) const; + void get ( epicsGuard < epicsMutex > &, chid pChan, + unsigned type, arrayElementCount count, void * pValue ); + void put ( epicsGuard < epicsMutex > &, chid pChan, + unsigned type, arrayElementCount count, const void * pValue ); + void completionNotify ( + epicsGuard < epicsMutex > &, syncGroupNotify & ); + int printFormated ( const char * pFormat, ... ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo ); + void exception ( + epicsGuard < epicsMutex > &, int status, const char * pContext, + const char * pFileName, unsigned lineNo, oldChannelNotify & chan, + unsigned type, arrayElementCount count, unsigned op ); + void * operator new ( size_t size, + tsFreeList < struct CASG, 128, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < struct CASG, 128, epicsMutexNOOP > & )) +private: + tsDLList < syncGroupNotify > ioPendingList; + tsDLList < syncGroupNotify > ioCompletedList; + epicsEvent sem; + ca_client_context & client; + unsigned magic; + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > freeListReadOP; + tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > freeListWriteOP; + void recycleSyncGroupWriteNotify ( + epicsGuard < epicsMutex > &, syncGroupWriteNotify & io ); + void recycleSyncGroupReadNotify ( + epicsGuard < epicsMutex > &, syncGroupReadNotify & io ); + + void destroyPendingIO ( + epicsGuard < epicsMutex > & guard ); + void destroyCompletedIO ( + epicsGuard < epicsMutex > & guard ); + + CASG ( const CASG & ); + CASG & operator = ( const CASG & ); + + void * operator new ( size_t size ); + void operator delete ( void * ); + + ~CASG (); + + friend class sgAutoPtr < syncGroupWriteNotify >; + friend class sgAutoPtr < syncGroupReadNotify >; +}; + +class boolFlagManager { +public: + boolFlagManager ( bool & flag ); + ~boolFlagManager (); + void release (); +private: + bool * pBool; +}; + +inline boolFlagManager::boolFlagManager ( bool & flagIn ) : + pBool ( & flagIn ) +{ + *this->pBool = true; +} + +inline boolFlagManager::~boolFlagManager () +{ + if ( this->pBool ) { + *this->pBool = false; + } +} + +inline void boolFlagManager::release () +{ + this->pBool = 0; +} + +inline void * CASG::operator new ( size_t size, + tsFreeList < struct CASG, 128, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#if defined ( CXX_PLACEMENT_DELETE ) +inline void CASG::operator delete ( void * pCadaver, + tsFreeList < struct CASG, 128, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline bool syncGroupWriteNotify::ioPending ( + epicsGuard < epicsMutex > & /* guard */ ) +{ + return ! this->ioComplete; +} + +inline bool syncGroupReadNotify::ioPending ( + epicsGuard < epicsMutex > & /* guard */ ) +{ + return ! this->ioComplete; +} + +#endif // ifdef syncGrouph diff --git a/src/ca/syncGroupNotify.cpp b/src/ca/syncGroupNotify.cpp new file mode 100644 index 000000000..2780fbe89 --- /dev/null +++ b/src/ca/syncGroupNotify.cpp @@ -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. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "syncGroup.h" +#include "oldAccess.h" + +syncGroupNotify::syncGroupNotify () +{ +} + +syncGroupNotify::~syncGroupNotify () +{ +} + + + + diff --git a/src/ca/syncGroupReadNotify.cpp b/src/ca/syncGroupReadNotify.cpp new file mode 100644 index 000000000..11f90fe57 --- /dev/null +++ b/src/ca/syncGroupReadNotify.cpp @@ -0,0 +1,155 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + */ + +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "syncGroup.h" +#include "oldAccess.h" + +syncGroupReadNotify::syncGroupReadNotify ( + CASG & sgIn, chid pChan, void * pValueIn ) : + chan ( pChan ), sg ( sgIn ), pValue ( pValueIn ), + magic ( CASG_MAGIC ), id ( 0u ), + idIsValid ( false ), ioComplete ( false ) +{ +} + +void syncGroupReadNotify::begin ( + epicsGuard < epicsMutex > & guard, + unsigned type, arrayElementCount count ) +{ + this->chan->eliminateExcessiveSendBacklog ( guard ); + this->ioComplete = false; + boolFlagManager mgr ( this->idIsValid ); + this->chan->read ( guard, type, count, *this, &this->id ); + mgr.release (); +} + +void syncGroupReadNotify::cancel ( + epicsGuard < epicsMutex > & guard ) +{ + if ( this->idIsValid ) { + this->chan->ioCancel ( guard, this->id ); + this->idIsValid = false; + } +} + +syncGroupReadNotify * syncGroupReadNotify::factory ( + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & freeList, + struct CASG & sg, chid chan, void * pValueIn ) +{ + return new ( freeList ) // X aCC 930 + syncGroupReadNotify ( sg, chan, pValueIn ); +} + +void syncGroupReadNotify::destroy ( + epicsGuard < epicsMutex > & guard, casgRecycle & recycle ) +{ + this->~syncGroupReadNotify (); + recycle.recycleSyncGroupReadNotify ( guard, *this ); +} + +syncGroupReadNotify::~syncGroupReadNotify () +{ + assert ( ! this->idIsValid ); +} + +void syncGroupReadNotify::completion ( + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount count, const void * pData ) +{ + if ( this->magic != CASG_MAGIC ) { + this->sg.printFormated ( + "cac: sync group io_complete(): bad sync grp op magic number?\n" ); + return; + } + + if ( this->pValue ) { + size_t size = dbr_size_n ( type, count ); + memcpy ( this->pValue, pData, size ); + } + this->sg.completionNotify ( guard, *this ); + this->idIsValid = false; + this->ioComplete = true; +} + +void syncGroupReadNotify::exception ( + epicsGuard < epicsMutex > & guard, int status, const char * pContext, + unsigned type, arrayElementCount count ) +{ + if ( this->magic != CASG_MAGIC ) { + this->sg.printFormated ( + "cac: sync group io_complete(): bad sync grp op magic number?\n" ); + return; + } + this->idIsValid = false; + this->sg.exception ( guard, status, pContext, + __FILE__, __LINE__, *this->chan, type, count, CA_OP_GET ); + // + // This notify is left installed at this point as a place holder indicating that + // all requests have not been completed. This notify is not uninstalled until + // CASG::block() times out or unit they call CASG::reset(). + // +} + +void syncGroupReadNotify::show ( + epicsGuard < epicsMutex > &, unsigned level ) const +{ + ::printf ( "pending sg read op: pVal=%p\n", this->pValue ); + if ( level > 0u ) { + ::printf ( "pending sg op: magic=%u sg=%p\n", + this->magic, static_cast < void * > ( & this->sg ) ); + } +} + +void * syncGroupReadNotify::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void syncGroupReadNotify::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void * syncGroupReadNotify::operator new ( size_t size, + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#if defined ( CXX_PLACEMENT_DELETE ) +void syncGroupReadNotify::operator delete ( void *pCadaver, + tsFreeList < class syncGroupReadNotify, 128, epicsMutexNOOP > &freeList ) +{ + freeList.release ( pCadaver ); +} +#endif diff --git a/src/ca/syncGroupWriteNotify.cpp b/src/ca/syncGroupWriteNotify.cpp new file mode 100644 index 000000000..244d9d3bd --- /dev/null +++ b/src/ca/syncGroupWriteNotify.cpp @@ -0,0 +1,146 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + */ + +#include +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "syncGroup.h" +#include "oldAccess.h" + +syncGroupWriteNotify::syncGroupWriteNotify ( CASG & sgIn, chid pChan ) : + chan ( pChan ), sg ( sgIn ), magic ( CASG_MAGIC ), + id ( 0u ), idIsValid ( false ), ioComplete ( false ) +{ +} + +void syncGroupWriteNotify::begin ( + epicsGuard < epicsMutex > & guard, unsigned type, + arrayElementCount count, const void * pValueIn ) +{ + this->chan->eliminateExcessiveSendBacklog ( guard ); + this->ioComplete = false; + boolFlagManager mgr ( this->idIsValid ); + this->chan->write ( guard, type, count, + pValueIn, *this, &this->id ); + mgr.release (); +} + +void syncGroupWriteNotify::cancel ( + epicsGuard < epicsMutex > & guard ) +{ + if ( this->idIsValid ) { + this->chan->ioCancel ( guard, this->id ); + this->idIsValid = false; + } +} + +syncGroupWriteNotify * syncGroupWriteNotify::factory ( + tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &freeList, + struct CASG & sg, chid chan ) +{ + return new ( freeList ) syncGroupWriteNotify ( sg, chan ); +} + +void syncGroupWriteNotify::destroy ( + epicsGuard < epicsMutex > & guard, casgRecycle & recycle ) +{ + this->~syncGroupWriteNotify (); + recycle.recycleSyncGroupWriteNotify ( guard, *this ); +} + +syncGroupWriteNotify::~syncGroupWriteNotify () +{ + assert ( ! this->idIsValid ); +} + +void syncGroupWriteNotify::completion ( + epicsGuard < epicsMutex > & guard ) +{ + if ( this->magic != CASG_MAGIC ) { + this->sg.printFormated ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); + return; + } + this->sg.completionNotify ( guard, *this ); + this->idIsValid = false; + this->ioComplete = true; +} + +void syncGroupWriteNotify::exception ( + epicsGuard < epicsMutex > & guard, + int status, const char *pContext, unsigned type, arrayElementCount count ) +{ + if ( this->magic != CASG_MAGIC ) { + this->sg.printFormated ( "cac: sync group io_complete(): bad sync grp op magic number?\n" ); + return; + } + this->sg.exception ( guard, status, pContext, + __FILE__, __LINE__, *this->chan, type, count, CA_OP_PUT ); + this->idIsValid = false; + // + // This notify is left installed at this point as a place holder indicating that + // all requests have not been completed. This notify is not uninstalled until + // CASG::block() times out or unit they call CASG::reset(). + // +} + +void syncGroupWriteNotify::show ( + epicsGuard < epicsMutex > &, unsigned level ) const +{ + ::printf ( "pending write sg op\n" ); + if ( level > 0u ) { + ::printf ( "pending sg op: magic=%u sg=%p\n", + this->magic, static_cast < void * > ( &this->sg ) ); + } +} + +void * syncGroupWriteNotify::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void syncGroupWriteNotify::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void * syncGroupWriteNotify::operator new ( size_t size, + tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#if defined ( CXX_PLACEMENT_DELETE ) +void syncGroupWriteNotify::operator delete ( void *pCadaver, + tsFreeList < class syncGroupWriteNotify, 128, epicsMutexNOOP > &freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + diff --git a/src/ca/syncgrp.cpp b/src/ca/syncgrp.cpp new file mode 100644 index 000000000..c76f6a194 --- /dev/null +++ b/src/ca/syncgrp.cpp @@ -0,0 +1,292 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#define epicsExportSharedSymbols +#include "iocinf.h" +#include "oldAccess.h" +#include "syncGroup.h" + +/* + * ca_sg_create() + */ +extern "C" int epicsShareAPI ca_sg_create ( CA_SYNC_GID * pgid ) // X aCC 361 +{ + ca_client_context * pcac; + int caStatus; + CASG * pcasg; + + caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + try { + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + pcasg = new ( pcac->casgFreeList ) CASG ( guard, *pcac ); + *pgid = pcasg->getId (); + return ECA_NORMAL; + } + catch ( std::bad_alloc & ) { + return ECA_ALLOCMEM; + } + catch ( ... ) { + return ECA_INTERNAL; + } +} + +/* + * ca_sg_delete() + */ +extern "C" int epicsShareAPI ca_sg_delete ( const CA_SYNC_GID gid ) +{ + ca_client_context * pcac; + int caStatus = fetchClientContext ( & pcac ); + if ( caStatus == ECA_NORMAL ) { + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + CASG * pcasg = pcac->lookupCASG ( guard, gid ); + if ( pcasg ) { + pcasg->destructor ( guard ); + pcac->casgFreeList.release ( pcasg ); + } + else { + caStatus = ECA_BADSYNCGRP; + } + } + return caStatus; +} + +// +// ca_sg_block () +// +// !!!! This routine is only visible in the old interface - or in a new ST interface. +// !!!! In the old interface we restrict thread attach so that calls from threads +// !!!! other than the initializing thread are not allowed if preemptive callback +// !!!! is disabled. This prevents the preemptive callback lock from being released +// !!!! by other threads than the one that locked it. +// +extern "C" int epicsShareAPI ca_sg_block ( + const CA_SYNC_GID gid, ca_real timeout ) +{ + ca_client_context *pcac; + int status = fetchClientContext ( &pcac ); + if ( status == ECA_NORMAL ) { + epicsGuard < epicsMutex > guard ( pcac->mutex ); + CASG * pcasg = pcac->lookupCASG ( guard, gid ); + if ( ! pcasg ) { + status = ECA_BADSYNCGRP; + } + else { + status = pcasg->block ( + pcac->pCallbackGuard.get (), guard, timeout ); + } + } + return status; +} + +/* + * ca_sg_reset + */ +extern "C" int epicsShareAPI ca_sg_reset ( const CA_SYNC_GID gid ) +{ + ca_client_context *pcac; + int caStatus = fetchClientContext (&pcac); + if ( caStatus == ECA_NORMAL ) { + epicsGuard < epicsMutex > guard ( pcac->mutex ); + CASG * pcasg = pcac->lookupCASG ( guard, gid ); + if ( pcasg ) { + pcasg->reset ( guard ); + } + else { + caStatus = ECA_BADSYNCGRP; + } + } + return caStatus; +} + +/* + * ca_sg_stat + */ +extern "C" int epicsShareAPI ca_sg_stat ( const CA_SYNC_GID gid ) +{ + ca_client_context * pcac; + int caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + CASG * pcasg = pcac->lookupCASG ( guard, gid ); + if ( ! pcasg ) { + ::printf ( "Bad Sync Group Id\n"); + return ECA_BADSYNCGRP; + } + pcasg->show ( guard, 1000u ); + + return ECA_NORMAL; +} + +/* + * ca_sg_test + */ +extern "C" int epicsShareAPI ca_sg_test ( const CA_SYNC_GID gid ) // X aCC 361 +{ + ca_client_context * pcac; + int caStatus = fetchClientContext ( &pcac ); + if ( caStatus == ECA_NORMAL ) { + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + CASG * pcasg = pcac->lookupCASG ( guard, gid ); + if ( pcasg ) { + if ( pcasg->ioComplete ( guard ) ) { + caStatus = ECA_IODONE; + } + else{ + caStatus = ECA_IOINPROGRESS; + } + } + else { + caStatus = ECA_BADSYNCGRP; + } + } + return caStatus; +} + +/* + * ca_sg_array_put() + */ +extern "C" int epicsShareAPI ca_sg_array_put ( const CA_SYNC_GID gid, chtype type, + arrayElementCount count, chid pChan, const void *pValue ) +{ + ca_client_context *pcac; + CASG *pcasg; + int caStatus; + + caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + pcasg = pcac->lookupCASG ( guard, gid ); + if ( ! pcasg ) { + return ECA_BADSYNCGRP; + } + + try { + pcasg->put ( guard, pChan, type, + static_cast < unsigned > ( count ), pValue ); + return ECA_NORMAL; + } + catch ( cacChannel::badString & ) + { + return ECA_BADSTR; + } + catch ( cacChannel::badType & ) + { + return ECA_BADTYPE; + } + catch ( cacChannel::outOfBounds & ) + { + return ECA_BADCOUNT; + } + catch ( cacChannel::noWriteAccess & ) + { + return ECA_NOWTACCESS; + } + catch ( cacChannel::notConnected & ) + { + return ECA_DISCONN; + } + catch ( cacChannel::unsupportedByService & ) + { + return ECA_UNAVAILINSERV; + } + catch ( cacChannel::requestTimedOut & ) + { + return ECA_TIMEOUT; + } + catch ( std::bad_alloc & ) + { + return ECA_ALLOCMEM; + } + catch ( ... ) + { + return ECA_INTERNAL; + } +} + +/* + * ca_sg_array_get() + */ +extern "C" int epicsShareAPI ca_sg_array_get ( const CA_SYNC_GID gid, chtype type, + arrayElementCount count, chid pChan, void *pValue ) +{ + ca_client_context *pcac; + CASG *pcasg; + int caStatus; + + caStatus = fetchClientContext ( &pcac ); + if ( caStatus != ECA_NORMAL ) { + return caStatus; + } + + epicsGuard < epicsMutex > guard ( pcac->mutexRef() ); + + pcasg = pcac->lookupCASG ( guard, gid ); + if ( ! pcasg ) { + return ECA_BADSYNCGRP; + } + + try { + pcasg->get ( guard, pChan, type, + static_cast < unsigned > ( count ), pValue ); + return ECA_NORMAL; + } + catch ( cacChannel::badString & ) + { + return ECA_BADSTR; + } + catch ( cacChannel::badType & ) + { + return ECA_BADTYPE; + } + catch ( cacChannel::outOfBounds & ) + { + return ECA_BADCOUNT; + } + catch ( cacChannel::noReadAccess & ) + { + return ECA_NORDACCESS; + } + catch ( cacChannel::notConnected & ) + { + return ECA_DISCONN; + } + catch ( cacChannel::unsupportedByService & ) + { + return ECA_UNAVAILINSERV; + } + catch ( std::bad_alloc & ) + { + return ECA_ALLOCMEM; + } + catch ( ... ) + { + return ECA_INTERNAL; + } +} diff --git a/src/ca/tcpRecvThread.cpp b/src/ca/tcpRecvThread.cpp new file mode 100644 index 000000000..34bf8ca83 --- /dev/null +++ b/src/ca/tcpRecvThread.cpp @@ -0,0 +1,11 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + diff --git a/src/ca/tcpRecvWatchdog.cpp b/src/ca/tcpRecvWatchdog.cpp new file mode 100644 index 000000000..35e509a7c --- /dev/null +++ b/src/ca/tcpRecvWatchdog.cpp @@ -0,0 +1,251 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "iocinf.h" +#include "cac.h" +#include "virtualCircuit.h" + +// +// the recv watchdog timer is active when this object is created +// +tcpRecvWatchdog::tcpRecvWatchdog + ( epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn, + epicsMutex & mutexIn, tcpiiu & iiuIn, + double periodIn, epicsTimerQueue & queueIn ) : + period ( periodIn ), timer ( queueIn.createTimer () ), + cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ), + mutex ( mutexIn ), iiu ( iiuIn ), + probeResponsePending ( false ), beaconAnomaly ( true ), + probeTimeoutDetected ( false ), shuttingDown ( false ) +{ +} + +tcpRecvWatchdog::~tcpRecvWatchdog () +{ + this->timer.destroy (); +} + +epicsTimerNotify::expireStatus +tcpRecvWatchdog::expire ( const epicsTime & /* currentTime */ ) // X aCC 361 +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->shuttingDown ) { + return noRestart; + } + if ( this->probeResponsePending ) { + if ( this->iiu.receiveThreadIsBusy ( guard ) ) { + return expireStatus ( restart, CA_ECHO_TIMEOUT ); + } + + { +# ifdef DEBUG + char hostName[128]; + this->iiu.getHostName ( guard, hostName, sizeof (hostName) ); + debugPrintf ( ( "CA server \"%s\" unresponsive after %g inactive sec" + "- disconnecting.\n", + hostName, this->period ) ); +# endif + // to get the callback lock safely we must reorder + // the lock hierarchy + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + // callback lock is required because channel disconnect + // state change is initiated from this thread, and + // this can cause their disconnect notify callback + // to be invoked. + callbackManager mgr ( this->ctxNotify, this->cbMutex ); + epicsGuard < epicsMutex > tmpGuard ( this->mutex ); + this->iiu.receiveTimeoutNotify ( mgr, tmpGuard ); + this->probeTimeoutDetected = true; + } + } + return noRestart; + } + else { + if ( this->iiu.receiveThreadIsBusy ( guard ) ) { + return expireStatus ( restart, this->period ); + } + this->probeTimeoutDetected = false; + this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); + debugPrintf ( ("circuit timed out - sending echo request\n") ); + return expireStatus ( restart, CA_ECHO_TIMEOUT ); + } +} + +void tcpRecvWatchdog::beaconArrivalNotify ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( ! ( this->shuttingDown || this->beaconAnomaly || this->probeResponsePending ) ) { + this->timer.start ( *this, this->period ); + debugPrintf ( ("saw a normal beacon - reseting circuit receive watchdog\n") ); + } +} + +// +// be careful about using beacons to reset the connection +// time out watchdog until we have received a ping response +// from the IOC (this makes the software detect reconnects +// faster when the server is rebooted twice in rapid +// succession before a 1st or 2nd beacon has been received) +// +void tcpRecvWatchdog::beaconAnomalyNotify ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->beaconAnomaly = true; + debugPrintf ( ("Saw an abnormal beacon\n") ); +} + +void tcpRecvWatchdog::messageArrivalNotify ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( ! ( this->shuttingDown || this->probeResponsePending ) ) { + this->beaconAnomaly = false; + this->timer.start ( *this, this->period ); + debugPrintf ( ("received a message - reseting circuit recv watchdog\n") ); + } +} + +void tcpRecvWatchdog::probeResponseNotify ( + epicsGuard < epicsMutex > & cbGuard ) +{ + bool restartNeeded = false; + double restartDelay = DBL_MAX; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->probeResponsePending && ! this->shuttingDown ) { + restartNeeded = true; + if ( this->probeTimeoutDetected ) { + this->probeTimeoutDetected = false; + this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); + restartDelay = CA_ECHO_TIMEOUT; + debugPrintf ( ("late probe response - sending another probe request\n") ); + } + else { + this->probeResponsePending = false; + restartDelay = this->period; + this->iiu.responsiveCircuitNotify ( cbGuard, guard ); + debugPrintf ( ("probe response on time - circuit was tagged reponsive if unresponsive\n") ); + } + } + } + if ( restartNeeded ) { + this->timer.start ( *this, restartDelay ); + debugPrintf ( ("recv wd restarted with delay %f\n", restartDelay) ); + } +} + +// +// The thread for outgoing requests in the client runs +// at a higher priority than the thread in the client +// that receives responses. Therefore, there could +// be considerable large array write send backlog that +// is delaying departure of an echo request and also +// interrupting delivery of an echo response. +// We must be careful not to timeout the echo response as +// long as we see indication of regular departures of +// message buffers from the client in a situation where +// we know that the TCP send queueing has been exceeded. +// The send watchdog will be responsible for detecting +// dead connections in this case. +// +void tcpRecvWatchdog::sendBacklogProgressNotify ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + // We dont set "beaconAnomaly" to be false here because, after we see a + // beacon anomaly (which could be transiently detecting a reboot) we will + // not trust the beacon as an indicator of a healthy server until we + // receive at least one message from the server. + if ( this->probeResponsePending && ! this->shuttingDown ) { + this->timer.start ( *this, CA_ECHO_TIMEOUT ); + debugPrintf ( ("saw heavy send backlog - reseting circuit recv watchdog\n") ); + } +} + +void tcpRecvWatchdog::connectNotify ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->shuttingDown ) { + return; + } + this->timer.start ( *this, this->period ); + debugPrintf ( ("connected to the server - initiating circuit recv watchdog\n") ); +} + +void tcpRecvWatchdog::sendTimeoutNotify ( + epicsGuard < epicsMutex > & /* cbGuard */, + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + bool restartNeeded = false; + if ( ! ( this->probeResponsePending || this->shuttingDown ) ) { + this->probeResponsePending = this->iiu.setEchoRequestPending ( guard ); + restartNeeded = true; + } + if ( restartNeeded ) { + this->timer.start ( *this, CA_ECHO_TIMEOUT ); + } + debugPrintf ( ("TCP send timed out - sending echo request\n") ); +} + +void tcpRecvWatchdog::cancel () +{ + this->timer.cancel (); + debugPrintf ( ("canceling TCP recv watchdog\n") ); +} + +void tcpRecvWatchdog::shutdown () +{ + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->shuttingDown = true; + } + this->timer.cancel (); + debugPrintf ( ("canceling TCP recv watchdog\n") ); +} + +double tcpRecvWatchdog::delay () const +{ + return this->timer.getExpireDelay (); +} + +void tcpRecvWatchdog::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + ::printf ( "Receive virtual circuit watchdog at %p, period %f\n", + static_cast ( this ), this->period ); + if ( level > 0u ) { + ::printf ( "\t%s %s %s\n", + this->probeResponsePending ? "probe-response-pending" : "", + this->beaconAnomaly ? "beacon-anomaly-detected" : "", + this->probeTimeoutDetected ? "probe-response-timeout" : "" ); + } +} + diff --git a/src/ca/tcpRecvWatchdog.h b/src/ca/tcpRecvWatchdog.h new file mode 100644 index 000000000..0b15e22d8 --- /dev/null +++ b/src/ca/tcpRecvWatchdog.h @@ -0,0 +1,85 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef tcpRecvWatchdogh +#define tcpRecvWatchdogh + +#ifdef epicsExportSharedSymbols +# define tcpRecvWatchdogh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsTimer.h" + +#ifdef tcpRecvWatchdogh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class tcpiiu; + +class tcpRecvWatchdog : private epicsTimerNotify { +public: + tcpRecvWatchdog ( epicsMutex & cbMutex, + cacContextNotify & ctxNotify, + epicsMutex & mutex, tcpiiu &, + double periodIn, epicsTimerQueue & ); + virtual ~tcpRecvWatchdog (); + void sendBacklogProgressNotify ( + epicsGuard < epicsMutex > & ); + void messageArrivalNotify ( + epicsGuard < epicsMutex > & guard ); + void probeResponseNotify ( + epicsGuard < epicsMutex > & ); + void beaconArrivalNotify ( + epicsGuard < epicsMutex > & ); + void beaconAnomalyNotify ( epicsGuard < epicsMutex > & ); + void connectNotify ( + epicsGuard < epicsMutex > & ); + void sendTimeoutNotify ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void cancel (); + void shutdown (); + void show ( unsigned level ) const; + double delay () const; +private: + const double period; + epicsTimer & timer; + epicsMutex & cbMutex; + cacContextNotify & ctxNotify; + epicsMutex & mutex; + tcpiiu & iiu; + bool probeResponsePending; + bool beaconAnomaly; + bool probeTimeoutDetected; + bool shuttingDown; + expireStatus expire ( const epicsTime & currentTime ); + tcpRecvWatchdog ( const tcpRecvWatchdog & ); + tcpRecvWatchdog & operator = ( const tcpRecvWatchdog & ); +}; + +#endif // #ifndef tcpRecvWatchdogh + diff --git a/src/ca/tcpSendWatchdog.cpp b/src/ca/tcpSendWatchdog.cpp new file mode 100644 index 000000000..6834b39e7 --- /dev/null +++ b/src/ca/tcpSendWatchdog.cpp @@ -0,0 +1,76 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "iocinf.h" +#include "cac.h" +#include "virtualCircuit.h" + +tcpSendWatchdog::tcpSendWatchdog ( + epicsMutex & cbMutexIn, cacContextNotify & ctxNotifyIn, + epicsMutex & mutexIn, tcpiiu & iiuIn, + double periodIn, epicsTimerQueue & queueIn ) : + period ( periodIn ), timer ( queueIn.createTimer () ), + cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ), + mutex ( mutexIn ), iiu ( iiuIn ) +{ +} + +tcpSendWatchdog::~tcpSendWatchdog () +{ + this->timer.destroy (); +} + +epicsTimerNotify::expireStatus tcpSendWatchdog::expire ( + const epicsTime & /* currentTime */ ) +{ + { + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->iiu.receiveThreadIsBusy ( guard ) ) { + return expireStatus ( restart, this->period ); + } + } + { + callbackManager mgr ( this->ctxNotify, this->cbMutex ); + epicsGuard < epicsMutex > guard ( this->mutex ); +# ifdef DEBUG + char hostName[128]; + this->iiu.getHostName ( guard, hostName, sizeof ( hostName ) ); + debugPrintf ( ( "Request not accepted by CA server %s for %g sec. Disconnecting.\n", + hostName, this->period ) ); +# endif + this->iiu.sendTimeoutNotify ( mgr, guard ); + } + return noRestart; +} + +void tcpSendWatchdog::start ( const epicsTime & /* currentTime */ ) +{ + this->timer.start ( *this, this->period ); +} + +void tcpSendWatchdog::cancel () +{ + this->timer.cancel (); +} + + diff --git a/src/ca/tcpSendWatchdog.h b/src/ca/tcpSendWatchdog.h new file mode 100644 index 000000000..05a2dfe75 --- /dev/null +++ b/src/ca/tcpSendWatchdog.h @@ -0,0 +1,63 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef tcpSendWatchdogh +#define tcpSendWatchdogh + + +#ifdef epicsExportSharedSymbols +# define tcpSendWatchdogh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsTimer.h" + +#ifdef tcpSendWatchdogh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class tcpSendWatchdog : private epicsTimerNotify { +public: + tcpSendWatchdog ( + epicsMutex & cbMutex, cacContextNotify & ctxNotify, + epicsMutex & mutex, tcpiiu &, + double periodIn, epicsTimerQueue & queueIn ); + virtual ~tcpSendWatchdog (); + void start ( const epicsTime & ); + void cancel (); +private: + const double period; + epicsTimer & timer; + epicsMutex & cbMutex; + cacContextNotify & ctxNotify; + epicsMutex & mutex; + tcpiiu & iiu; + expireStatus expire ( const epicsTime & currentTime ); + tcpSendWatchdog ( const tcpSendWatchdog & ); + tcpSendWatchdog & operator = ( const tcpSendWatchdog & ); +}; + +#endif // #ifndef tcpSendWatchdog diff --git a/src/ca/tcpiiu.cpp b/src/ca/tcpiiu.cpp new file mode 100644 index 000000000..a38ee7ff3 --- /dev/null +++ b/src/ca/tcpiiu.cpp @@ -0,0 +1,2190 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + * + */ + +#ifdef _MSC_VER +# pragma warning(disable:4355) +#endif + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include +#include +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "localHostName.h" +#include "iocinf.h" +#include "virtualCircuit.h" +#include "inetAddrID.h" +#include "cac.h" +#include "netiiu.h" +#include "hostNameCache.h" +#include "net_convert.h" +#include "bhe.h" +#include "epicsSignal.h" +#include "caerr.h" +#include "udpiiu.h" + +using namespace std; + +const unsigned mSecPerSec = 1000u; +const unsigned uSecPerSec = 1000u * mSecPerSec; + +tcpSendThread::tcpSendThread ( + class tcpiiu & iiuIn, const char * pName, + unsigned stackSize, unsigned priority ) : + thread ( *this, pName, stackSize, priority ), iiu ( iiuIn ) +{ +} + +tcpSendThread::~tcpSendThread () +{ +} + +void tcpSendThread::start () +{ + this->thread.start (); +} + +void tcpSendThread::show ( unsigned /* level */ ) const +{ +} + +void tcpSendThread::exitWait () +{ + this->thread.exitWait (); +} + +void tcpSendThread::run () +{ + try { + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + + bool laborPending = false; + + while ( true ) { + + // dont wait if there is still labor to be done below + if ( ! laborPending ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->iiu.sendThreadFlushEvent.wait (); + } + + if ( this->iiu.state != tcpiiu::iiucs_connected ) { + break; + } + + laborPending = false; + bool flowControlLaborNeeded = + this->iiu.busyStateDetected != this->iiu.flowControlActive; + bool echoLaborNeeded = this->iiu.echoRequestPending; + this->iiu.echoRequestPending = false; + + if ( flowControlLaborNeeded ) { + if ( this->iiu.flowControlActive ) { + this->iiu.disableFlowControlRequest ( guard ); + this->iiu.flowControlActive = false; + debugPrintf ( ( "fc off\n" ) ); + } + else { + this->iiu.enableFlowControlRequest ( guard ); + this->iiu.flowControlActive = true; + debugPrintf ( ( "fc on\n" ) ); + } + } + + if ( echoLaborNeeded ) { + this->iiu.echoRequest ( guard ); + } + + while ( nciu * pChan = this->iiu.createReqPend.get () ) { + this->iiu.createChannelRequest ( *pChan, guard ); + + if ( CA_V42 ( this->iiu.minorProtocolVersion ) ) { + this->iiu.createRespPend.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_createRespPend; + } + else { + // This wakes up the resp thread so that it can call + // the connect callback. This isnt maximally efficent + // but it has the excellent side effect of not requiring + // that the UDP thread take the callback lock. There are + // almost no V42 servers left at this point. + this->iiu.v42ConnCallbackPend.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_v42ConnCallbackPend; + this->iiu.echoRequestPending = true; + laborPending = true; + } + + if ( this->iiu.sendQue.flushBlockThreshold () ) { + laborPending = true; + break; + } + } + + while ( nciu * pChan = this->iiu.subscripReqPend.get () ) { + // this installs any subscriptions as needed + pChan->resubscribe ( guard ); + this->iiu.connectedList.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_connected; + if ( this->iiu.sendQue.flushBlockThreshold () ) { + laborPending = true; + break; + } + } + + while ( nciu * pChan = this->iiu.subscripUpdateReqPend.get () ) { + // this updates any subscriptions as needed + pChan->sendSubscriptionUpdateRequests ( guard ); + this->iiu.connectedList.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_connected; + if ( this->iiu.sendQue.flushBlockThreshold () ) { + laborPending = true; + break; + } + } + + if ( ! this->iiu.sendThreadFlush ( guard ) ) { + break; + } + } + if ( this->iiu.state == tcpiiu::iiucs_clean_shutdown ) { + this->iiu.sendThreadFlush ( guard ); + // this should cause the server to disconnect from + // the client + int status = ::shutdown ( this->iiu.sock, SHUT_WR ); + if ( status ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("CAC TCP clean socket shutdown error was %s\n", + sockErrBuf ); + } + } + } + catch ( ... ) { + errlogPrintf ( + "cac: tcp send thread received an unexpected exception " + "- disconnecting\n"); + // this should cause the server to disconnect from + // the client + int status = ::shutdown ( this->iiu.sock, SHUT_WR ); + if ( status ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("CAC TCP clean socket shutdown error was %s\n", + sockErrBuf ); + } + } + + this->iiu.sendDog.cancel (); + this->iiu.recvDog.shutdown (); + + while ( ! this->iiu.recvThread.exitWait ( 30.0 ) ) { + // it is possible to get stuck here if the user calls + // ca_context_destroy() when a circuit isnt known to + // be unresponsive, but is. That situation is probably + // rare, and the IP kernel might have a timeout for + // such situations, nevertheless we will attempt to deal + // with it here after waiting a reasonable amount of time + // for a clean shutdown to finish. + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + this->iiu.initiateAbortShutdown ( guard ); + } + + // user threads blocking for send backlog to be reduced + // will abort their attempt to get space if + // the state of the tcpiiu changes from connected to a + // disconnecting state. Nevertheless, we need to wait + // for them to finish prior to destroying the IIU. + { + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + while ( this->iiu.blockingForFlush ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + epicsThreadSleep ( 0.1 ); + } + } + this->iiu.cacRef.destroyIIU ( this->iiu ); +} + +unsigned tcpiiu::sendBytes ( const void *pBuf, + unsigned nBytesInBuf, const epicsTime & currentTime ) +{ + unsigned nBytes = 0u; + assert ( nBytesInBuf <= INT_MAX ); + + this->sendDog.start ( currentTime ); + + while ( true ) { + int status = ::send ( this->sock, + static_cast < const char * > (pBuf), (int) nBytesInBuf, 0 ); + if ( status > 0 ) { + nBytes = static_cast ( status ); + // printf("SEND: %u\n", nBytes ); + break; + } + else { + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->state != iiucs_connected && + this->state != iiucs_clean_shutdown ) { + break; + } + // winsock indicates disconnect by returning zero here + if ( status == 0 ) { + this->disconnectNotify ( guard ); + break; + } + + int localError = SOCKERRNO; + + if ( localError == SOCK_EINTR ) { + continue; + } + + if ( localError == SOCK_ENOBUFS ) { + errlogPrintf ( + "CAC: system low on network buffers " + "- send retry in 15 seconds\n" ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + epicsThreadSleep ( 15.0 ); + } + continue; + } + + if ( + localError != SOCK_EPIPE && + localError != SOCK_ECONNRESET && + localError != SOCK_ETIMEDOUT && + localError != SOCK_ECONNABORTED && + localError != SOCK_SHUTDOWN ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: unexpected TCP send error: %s\n", + sockErrBuf ); + } + + this->disconnectNotify ( guard ); + break; + } + } + + this->sendDog.cancel (); + + return nBytes; +} + +void tcpiiu::recvBytes ( + void * pBuf, unsigned nBytesInBuf, statusWireIO & stat ) +{ + assert ( nBytesInBuf <= INT_MAX ); + + while ( true ) { + int status = ::recv ( this->sock, static_cast ( pBuf ), + static_cast ( nBytesInBuf ), 0 ); + + if ( status > 0 ) { + stat.bytesCopied = static_cast ( status ); + assert ( stat.bytesCopied <= nBytesInBuf ); + stat.circuitState = swioConnected; + return; + } + else { + epicsGuard < epicsMutex > guard ( this->mutex ); + + if ( status == 0 ) { + this->disconnectNotify ( guard ); + stat.bytesCopied = 0u; + stat.circuitState = swioPeerHangup; + return; + } + + // if the circuit was locally aborted then supress + // warning messages about bad file descriptor etc + if ( this->state != iiucs_connected && + this->state != iiucs_clean_shutdown ) { + stat.bytesCopied = 0u; + stat.circuitState = swioLocalAbort; + return; + } + + int localErrno = SOCKERRNO; + + if ( localErrno == SOCK_SHUTDOWN ) { + stat.bytesCopied = 0u; + stat.circuitState = swioPeerHangup; + return; + } + + if ( localErrno == SOCK_EINTR ) { + continue; + } + + if ( localErrno == SOCK_ENOBUFS ) { + errlogPrintf ( + "CAC: system low on network buffers " + "- receive retry in 15 seconds\n" ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + epicsThreadSleep ( 15.0 ); + } + continue; + } + + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + + // the replacable printf handler isnt called here + // because it reqires a callback lock which probably + // isnt appropriate here + char name[64]; + this->hostNameCacheInstance.getName ( + name, sizeof ( name ) ); + errlogPrintf ( + "Unexpected problem with CA circuit to" + " server \"%s\" was \"%s\" - disconnecting\n", + name, sockErrBuf ); + + stat.bytesCopied = 0u; + stat.circuitState = swioPeerAbort; + return; + } + } +} + +tcpRecvThread::tcpRecvThread ( + class tcpiiu & iiuIn, class epicsMutex & cbMutexIn, + cacContextNotify & ctxNotifyIn, const char * pName, + unsigned int stackSize, unsigned int priority ) : + thread ( *this, pName, stackSize, priority ), + iiu ( iiuIn ), cbMutex ( cbMutexIn ), + ctxNotify ( ctxNotifyIn ) {} + +tcpRecvThread::~tcpRecvThread () +{ +} + +void tcpRecvThread::start () +{ + this->thread.start (); +} + +void tcpRecvThread::show ( unsigned /* level */ ) const +{ +} + +bool tcpRecvThread::exitWait ( double delay ) +{ + return this->thread.exitWait ( delay ); +} + +void tcpRecvThread::exitWait () +{ + this->thread.exitWait (); +} + +bool tcpRecvThread::validFillStatus ( + epicsGuard < epicsMutex > & guard, const statusWireIO & stat ) +{ + if ( this->iiu.state != tcpiiu::iiucs_connected && + this->iiu.state != tcpiiu::iiucs_clean_shutdown ) { + return false; + } + if ( stat.circuitState == swioConnected ) { + return true; + } + if ( stat.circuitState == swioPeerHangup || + stat.circuitState == swioPeerAbort ) { + this->iiu.disconnectNotify ( guard ); + } + else if ( stat.circuitState == swioLinkFailure ) { + this->iiu.initiateAbortShutdown ( guard ); + } + else if ( stat.circuitState == swioLocalAbort ) { + // state change already occurred + } + else { + errlogMessage ( "cac: invalid fill status - disconnecting" ); + this->iiu.disconnectNotify ( guard ); + } + return false; +} + +void tcpRecvThread::run () +{ + try { + { + bool connectSuccess = false; + { + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + this->connect ( guard ); + connectSuccess = this->iiu.state == tcpiiu::iiucs_connected; + } + if ( ! connectSuccess ) { + this->iiu.recvDog.shutdown (); + this->iiu.cacRef.destroyIIU ( this->iiu ); + return; + } + if ( this->iiu.isNameService () ) { + this->iiu.pSearchDest->setCircuit ( &this->iiu ); + this->iiu.pSearchDest->enable (); + } + } + + this->iiu.sendThread.start (); + epicsThreadPrivateSet ( caClientCallbackThreadId, &this->iiu ); + this->iiu.cacRef.attachToClientCtx (); + + comBuf * pComBuf = 0; + while ( true ) { + + // + // We leave the bytes pending and fetch them after + // callbacks are enabled when running in the old preemptive + // call back disabled mode so that asynchronous wakeup via + // file manager call backs works correctly. This does not + // appear to impact performance. + // + if ( ! pComBuf ) { + pComBuf = new ( this->iiu.comBufMemMgr ) comBuf; + } + + statusWireIO stat; + pComBuf->fillFromWire ( this->iiu, stat ); + + epicsTime currentTime = epicsTime::getCurrent (); + + { + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + + if ( ! this->validFillStatus ( guard, stat ) ) { + break; + } + if ( stat.bytesCopied == 0u ) { + continue; + } + + this->iiu.recvQue.pushLastComBufReceived ( *pComBuf ); + pComBuf = 0; + + this->iiu._receiveThreadIsBusy = true; + } + + bool sendWakeupNeeded = false; + { + // only one recv thread at a time may call callbacks + // - pendEvent() blocks until threads waiting for + // this lock get a chance to run + callbackManager mgr ( this->ctxNotify, this->cbMutex ); + + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + + // route legacy V42 channel connect through the recv thread - + // the only thread that should be taking the callback lock + while ( nciu * pChan = this->iiu.v42ConnCallbackPend.first () ) { + this->iiu.connectNotify ( guard, *pChan ); + pChan->connect ( mgr.cbGuard, guard ); + } + + this->iiu.unacknowledgedSendBytes = 0u; + + bool protocolOK = false; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + // execute receive labor + protocolOK = this->iiu.processIncoming ( currentTime, mgr ); + } + + if ( ! protocolOK ) { + this->iiu.initiateAbortShutdown ( guard ); + break; + } + this->iiu._receiveThreadIsBusy = false; + // reschedule connection activity watchdog + this->iiu.recvDog.messageArrivalNotify ( guard ); + // + // if this thread has connected channels with subscriptions + // that need to be sent then wakeup the send thread + if ( this->iiu.subscripReqPend.count() ) { + sendWakeupNeeded = true; + } + } + + // + // we dont feel comfortable calling this with a lock applied + // (it might block for longer than we like) + // + // we would prefer to improve efficency by trying, first, a + // recv with the new MSG_DONTWAIT flag set, but there isnt + // universal support + // + bool bytesArePending = this->iiu.bytesArePendingInOS (); + { + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + if ( bytesArePending ) { + if ( ! this->iiu.busyStateDetected ) { + this->iiu.contigRecvMsgCount++; + if ( this->iiu.contigRecvMsgCount >= + this->iiu.cacRef.maxContiguousFrames ( guard ) ) { + this->iiu.busyStateDetected = true; + sendWakeupNeeded = true; + } + } + } + else { + // if no bytes are pending then we must immediately + // switch off flow control w/o waiting for more + // data to arrive + this->iiu.contigRecvMsgCount = 0u; + if ( this->iiu.busyStateDetected ) { + sendWakeupNeeded = true; + this->iiu.busyStateDetected = false; + } + } + } + + if ( sendWakeupNeeded ) { + this->iiu.sendThreadFlushEvent.signal (); + } + } + + if ( pComBuf ) { + pComBuf->~comBuf (); + this->iiu.comBufMemMgr.release ( pComBuf ); + } + } + catch ( std::bad_alloc & ) { + errlogPrintf ( + "CA client library tcp receive thread " + "terminating due to no space in pool " + "C++ exception\n" ); + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + this->iiu.initiateCleanShutdown ( guard ); + } + catch ( std::exception & except ) { + errlogPrintf ( + "CA client library tcp receive thread " + "terminating due to C++ exception \"%s\"\n", + except.what () ); + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + this->iiu.initiateCleanShutdown ( guard ); + } + catch ( ... ) { + errlogPrintf ( + "CA client library tcp receive thread " + "terminating due to a non-standard C++ exception\n" ); + epicsGuard < epicsMutex > guard ( this->iiu.mutex ); + this->iiu.initiateCleanShutdown ( guard ); + } +} + +/* + * tcpRecvThread::connect () + */ +void tcpRecvThread::connect ( + epicsGuard < epicsMutex > & guard ) +{ + // attempt to connect to a CA server + while ( true ) { + int status; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + osiSockAddr tmp = this->iiu.address (); + status = ::connect ( this->iiu.sock, + & tmp.sa, sizeof ( tmp.sa ) ); + } + + if ( this->iiu.state != tcpiiu::iiucs_connecting ) { + break; + } + if ( status >= 0 ) { + // put the iiu into the connected state + this->iiu.state = tcpiiu::iiucs_connected; + this->iiu.recvDog.connectNotify ( guard ); + break; + } + else { + int errnoCpy = SOCKERRNO; + + if ( errnoCpy == SOCK_EINTR ) { + continue; + } + else if ( errnoCpy == SOCK_SHUTDOWN ) { + if ( ! this->iiu.isNameService () ) { + break; + } + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: Unable to connect because \"%s\"\n", + sockErrBuf ); + if ( ! this->iiu.isNameService () ) { + this->iiu.disconnectNotify ( guard ); + break; + } + } + { + double sleepTime = this->iiu.cacRef.connectionTimeout ( guard ); + epicsGuardRelease < epicsMutex > unguard ( guard ); + epicsThreadSleep ( sleepTime ); + } + continue; + } + } + return; +} + +// +// tcpiiu::tcpiiu () +// +tcpiiu::tcpiiu ( + cac & cac, epicsMutex & mutexIn, epicsMutex & cbMutexIn, + cacContextNotify & ctxNotifyIn, double connectionTimeout, + epicsTimerQueue & timerQueue, const osiSockAddr & addrIn, + comBufMemoryManager & comBufMemMgrIn, + unsigned minorVersion, ipAddrToAsciiEngine & engineIn, + const cacChannel::priLev & priorityIn, + SearchDestTCP * pSearchDestIn ) : + caServerID ( addrIn.ia, priorityIn ), + hostNameCacheInstance ( addrIn, engineIn ), + recvThread ( *this, cbMutexIn, ctxNotifyIn, "CAC-TCP-recv", + epicsThreadGetStackSize ( epicsThreadStackBig ), + cac::highestPriorityLevelBelow ( cac.getInitializingThreadsPriority() ) ), + sendThread ( *this, "CAC-TCP-send", + epicsThreadGetStackSize ( epicsThreadStackMedium ), + cac::lowestPriorityLevelAbove ( + cac.getInitializingThreadsPriority() ) ), + recvDog ( cbMutexIn, ctxNotifyIn, mutexIn, + *this, connectionTimeout, timerQueue ), + sendDog ( cbMutexIn, ctxNotifyIn, mutexIn, + *this, connectionTimeout, timerQueue ), + sendQue ( *this, comBufMemMgrIn ), + recvQue ( comBufMemMgrIn ), + curDataMax ( MAX_TCP ), + curDataBytes ( 0ul ), + comBufMemMgr ( comBufMemMgrIn ), + cacRef ( cac ), + pCurData ( cac.allocateSmallBufferTCP () ), + pSearchDest ( pSearchDestIn ), + mutex ( mutexIn ), + cbMutex ( cbMutexIn ), + minorProtocolVersion ( minorVersion ), + state ( iiucs_connecting ), + sock ( INVALID_SOCKET ), + contigRecvMsgCount ( 0u ), + blockingForFlush ( 0u ), + socketLibrarySendBufferSize ( 0x1000 ), + unacknowledgedSendBytes ( 0u ), + channelCountTot ( 0u ), + _receiveThreadIsBusy ( false ), + busyStateDetected ( false ), + flowControlActive ( false ), + echoRequestPending ( false ), + oldMsgHeaderAvailable ( false ), + msgHeaderAvailable ( false ), + earlyFlush ( false ), + recvProcessPostponedFlush ( false ), + discardingPendingData ( false ), + socketHasBeenClosed ( false ), + unresponsiveCircuit ( false ) +{ + this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if ( this->sock == INVALID_SOCKET ) { + cac.releaseSmallBufferTCP ( this->pCurData ); + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + std :: string reason = + "CAC: TCP circuit creation failure because \""; + reason += sockErrBuf; + reason += "\""; + throw runtime_error ( reason ); + } + + int flag = true; + int status = setsockopt ( this->sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &flag, sizeof ( flag ) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: problems setting socket option TCP_NODELAY = \"%s\"\n", + sockErrBuf ); + } + + flag = true; + status = setsockopt ( this->sock , SOL_SOCKET, SO_KEEPALIVE, + ( char * ) &flag, sizeof ( flag ) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: problems setting socket option SO_KEEPALIVE = \"%s\"\n", + sockErrBuf ); + } + + // load message queue with messages informing server + // of version, user, and host name of client + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->versionMessage ( guard, this->priority() ); + this->userNameSetRequest ( guard ); + this->hostNameSetRequest ( guard ); + } + +# if 0 + { + int i; + + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made joh 11-10-98 + */ + i = MAX_MSG_SIZE; + status = setsockopt ( this->sock, SOL_SOCKET, SO_SNDBUF, + ( char * ) &i, sizeof ( i ) ); + if (status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: problems setting socket option SO_SNDBUF = \"%s\"\n", + sockErrBuf ); + } + i = MAX_MSG_SIZE; + status = setsockopt ( this->sock, SOL_SOCKET, SO_RCVBUF, + ( char * ) &i, sizeof ( i ) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: problems setting socket option SO_RCVBUF = \"%s\"\n", + sockErrBuf ); + } + } +# endif + + { + int nBytes; + osiSocklen_t sizeOfParameter = static_cast < int > ( sizeof ( nBytes ) ); + status = getsockopt ( this->sock, SOL_SOCKET, SO_SNDBUF, + ( char * ) &nBytes, &sizeOfParameter ); + if ( status < 0 || nBytes < 0 || + sizeOfParameter != static_cast < int > ( sizeof ( nBytes ) ) ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("CAC: problems getting socket option SO_SNDBUF = \"%s\"\n", + sockErrBuf ); + } + else { + this->socketLibrarySendBufferSize = static_cast < unsigned > ( nBytes ); + } + } + +# if 0 + // + // windows has a really strange implementation of thess options + // and we can avoid the need for this by using pthread_kill on unix + // + { + struct timeval timeout; + double pollInterval = connectionTimeout / 8.0; + timeout.tv_sec = static_cast < long > ( pollInterval ); + timeout.tv_usec = static_cast < long > + ( ( pollInterval - timeout.tv_sec ) * uSecPerSec ); + // intentionally ignore status as we dont expect that all systems + // will accept this request + setsockopt ( this->sock, SOL_SOCKET, SO_SNDTIMEO, + ( char * ) & timeout, sizeof ( timeout ) ); + // intentionally ignore status as we dont expect that all systems + // will accept this request + setsockopt ( this->sock, SOL_SOCKET, SO_RCVTIMEO, + ( char * ) & timeout, sizeof ( timeout ) ); + } +# endif + + if ( isNameService() ) { + pSearchDest->setCircuit ( this ); + } + + memset ( (void *) &this->curMsg, '\0', sizeof ( this->curMsg ) ); +} + +// this must always be called by the udp thread when it holds +// the callback lock. +void tcpiiu::start ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->recvThread.start (); +} + +void tcpiiu::initiateCleanShutdown ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( this->state == iiucs_connected ) { + if ( this->unresponsiveCircuit ) { + this->initiateAbortShutdown ( guard ); + } + else { + this->state = iiucs_clean_shutdown; + this->sendThreadFlushEvent.signal (); + this->flushBlockEvent.signal (); + } + } + else if ( this->state == iiucs_clean_shutdown ) { + if ( this->unresponsiveCircuit ) { + this->initiateAbortShutdown ( guard ); + } + } + else if ( this->state == iiucs_connecting ) { + this->initiateAbortShutdown ( guard ); + } +} + +void tcpiiu::disconnectNotify ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->state = iiucs_disconnected; + this->sendThreadFlushEvent.signal (); + this->flushBlockEvent.signal (); +} + +void tcpiiu::responsiveCircuitNotify ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + if ( this->unresponsiveCircuit ) { + this->unresponsiveCircuit = false; + while ( nciu * pChan = this->unrespCircuit.get() ) { + this->subscripUpdateReqPend.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_subscripUpdateReqPend; + pChan->connect ( cbGuard, guard ); + } + this->sendThreadFlushEvent.signal (); + } +} + +void tcpiiu::sendTimeoutNotify ( + callbackManager & mgr, + epicsGuard < epicsMutex > & guard ) +{ + mgr.cbGuard.assertIdenticalMutex ( this-> cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + this->unresponsiveCircuitNotify ( mgr.cbGuard, guard ); + // setup circuit probe sequence + this->recvDog.sendTimeoutNotify ( mgr.cbGuard, guard ); +} + +void tcpiiu::receiveTimeoutNotify ( + callbackManager & mgr, + epicsGuard < epicsMutex > & guard ) +{ + mgr.cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + this->unresponsiveCircuitNotify ( mgr.cbGuard, guard ); +} + +void tcpiiu::unresponsiveCircuitNotify ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + + if ( ! this->unresponsiveCircuit ) { + this->unresponsiveCircuit = true; + this->echoRequestPending = true; + this->sendThreadFlushEvent.signal (); + this->flushBlockEvent.signal (); + + // must not hold lock when canceling timer + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); + this->recvDog.cancel (); + this->sendDog.cancel (); + } + } + + if ( this->connectedList.count() ) { + char hostNameTmp[128]; + this->getHostName ( guard, hostNameTmp, sizeof ( hostNameTmp ) ); + genLocalExcep ( cbGuard, guard, this->cacRef, + ECA_UNRESPTMO, hostNameTmp ); + while ( nciu * pChan = this->connectedList.get () ) { + // The cac lock is released herein so there is concern that + // the list could be changed while we are traversing it. + // However, this occurs only if a circuit disconnects, + // a user deletes a channel, or a server disconnects a + // channel. The callback lock must be taken in all of + // these situations so this code is protected. + this->unrespCircuit.add ( *pChan ); + pChan->channelNode::listMember = + channelNode::cs_unrespCircuit; + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + } + } + } +} + +void tcpiiu::initiateAbortShutdown ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( ! this->discardingPendingData ) { + // force abortive shutdown sequence + // (discard outstanding sends and receives) + struct linger tmpLinger; + tmpLinger.l_onoff = true; + tmpLinger.l_linger = 0u; + int status = setsockopt ( this->sock, SOL_SOCKET, SO_LINGER, + reinterpret_cast ( &tmpLinger ), sizeof (tmpLinger) ); + if ( status != 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC TCP socket linger set error was %s\n", + sockErrBuf ); + } + this->discardingPendingData = true; + } + + iiu_conn_state oldState = this->state; + if ( oldState != iiucs_abort_shutdown && oldState != iiucs_disconnected ) { + this->state = iiucs_abort_shutdown; + + epicsSocketSystemCallInterruptMechanismQueryInfo info = + epicsSocketSystemCallInterruptMechanismQuery (); + switch ( info ) { + case esscimqi_socketCloseRequired: + // + // on winsock and probably vxWorks shutdown() does not + // unblock a thread in recv() so we use close() and introduce + // some complexity because we must unregister the fd early + // + if ( ! this->socketHasBeenClosed ) { + epicsSocketDestroy ( this->sock ); + this->socketHasBeenClosed = true; + } + break; + case esscimqi_socketBothShutdownRequired: + { + int status = ::shutdown ( this->sock, SHUT_RDWR ); + if ( status ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("CAC TCP socket shutdown error was %s\n", + sockErrBuf ); + } + } + break; + case esscimqi_socketSigAlarmRequired: + this->recvThread.interruptSocketRecv (); + this->sendThread.interruptSocketSend (); + break; + default: + break; + }; + + // + // wake up the send thread if it isnt blocking in send() + // + this->sendThreadFlushEvent.signal (); + this->flushBlockEvent.signal (); + } +} + +// +// tcpiiu::~tcpiiu () +// +tcpiiu :: ~tcpiiu () +{ + if ( this->pSearchDest ) { + this->pSearchDest->disable (); + } + + this->sendThread.exitWait (); + this->recvThread.exitWait (); + this->sendDog.cancel (); + this->recvDog.shutdown (); + + if ( ! this->socketHasBeenClosed ) { + epicsSocketDestroy ( this->sock ); + } + + // free message body cache + if ( this->pCurData ) { + if ( this->curDataMax == MAX_TCP ) { + this->cacRef.releaseSmallBufferTCP ( this->pCurData ); + } + else { + this->cacRef.releaseLargeBufferTCP ( this->pCurData ); + } + } +} + +void tcpiiu::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > locker ( this->mutex ); + char buf[256]; + this->hostNameCacheInstance.getName ( buf, sizeof ( buf ) ); + ::printf ( "Virtual circuit to \"%s\" at version V%u.%u state %u\n", + buf, CA_MAJOR_PROTOCOL_REVISION, + this->minorProtocolVersion, this->state ); + if ( level > 1u ) { + ::printf ( "\tcurrent data cache pointer = %p current data cache size = %lu\n", + static_cast < void * > ( this->pCurData ), this->curDataMax ); + ::printf ( "\tcontiguous receive message count=%u, busy detect bool=%u, flow control bool=%u\n", + this->contigRecvMsgCount, this->busyStateDetected, this->flowControlActive ); + ::printf ( "\receive thread is busy=%u\n", + this->_receiveThreadIsBusy ); + } + if ( level > 2u ) { + ::printf ( "\tvirtual circuit socket identifier %d\n", this->sock ); + ::printf ( "\tsend thread flush signal:\n" ); + this->sendThreadFlushEvent.show ( level-2u ); + ::printf ( "\tsend thread:\n" ); + this->sendThread.show ( level-2u ); + ::printf ( "\trecv thread:\n" ); + this->recvThread.show ( level-2u ); + ::printf ("\techo pending bool = %u\n", this->echoRequestPending ); + ::printf ( "IO identifier hash table:\n" ); + + if ( this->createReqPend.count () ) { + ::printf ( "Create request pending channels\n" ); + tsDLIterConst < nciu > pChan = this->createReqPend.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->createRespPend.count () ) { + ::printf ( "Create response pending channels\n" ); + tsDLIterConst < nciu > pChan = this->createRespPend.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->v42ConnCallbackPend.count () ) { + ::printf ( "V42 Conn Callback pending channels\n" ); + tsDLIterConst < nciu > pChan = this->v42ConnCallbackPend.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->subscripReqPend.count () ) { + ::printf ( "Subscription request pending channels\n" ); + tsDLIterConst < nciu > pChan = this->subscripReqPend.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->connectedList.count () ) { + ::printf ( "Connected channels\n" ); + tsDLIterConst < nciu > pChan = this->connectedList.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + if ( this->unrespCircuit.count () ) { + ::printf ( "Unresponsive circuit channels\n" ); + tsDLIterConst < nciu > pChan = this->unrespCircuit.firstIter (); + while ( pChan.valid () ) { + pChan->show ( level - 2u ); + pChan++; + } + } + } +} + +bool tcpiiu::setEchoRequestPending ( epicsGuard < epicsMutex > & guard ) // X aCC 361 +{ + guard.assertIdenticalMutex ( this->mutex ); + + this->echoRequestPending = true; + this->sendThreadFlushEvent.signal (); + if ( CA_V43 ( this->minorProtocolVersion ) ) { + // we send an echo + return true; + } + else { + // we send a NOOP + return false; + } +} + +void tcpiiu::flushIfRecvProcessRequested ( + epicsGuard < epicsMutex > & guard ) +{ + if ( this->recvProcessPostponedFlush ) { + this->flushRequest ( guard ); + this->recvProcessPostponedFlush = false; + } +} + +bool tcpiiu::processIncoming ( + const epicsTime & currentTime, + callbackManager & mgr ) +{ + mgr.cbGuard.assertIdenticalMutex ( this->cbMutex ); + + while ( true ) { + + // + // fetch a complete message header + // + if ( ! this->msgHeaderAvailable ) { + if ( ! this->oldMsgHeaderAvailable ) { + this->oldMsgHeaderAvailable = + this->recvQue.popOldMsgHeader ( this->curMsg ); + if ( ! this->oldMsgHeaderAvailable ) { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->flushIfRecvProcessRequested ( guard ); + return true; + } + } + if ( this->curMsg.m_postsize == 0xffff ) { + static const unsigned annexSize = + sizeof ( this->curMsg.m_postsize ) + + sizeof ( this->curMsg.m_count ); + if ( this->recvQue.occupiedBytes () < annexSize ) { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->flushIfRecvProcessRequested ( guard ); + return true; + } + this->curMsg.m_postsize = this->recvQue.popUInt32 (); + this->curMsg.m_count = this->recvQue.popUInt32 (); + } + this->msgHeaderAvailable = true; +# ifdef DEBUG + epicsGuard < epicsMutex > guard ( this->mutex ); + debugPrintf ( + ( "%s Cmd=%3u Type=%3u Count=%8u Size=%8u", + this->pHostName ( guard ), + this->curMsg.m_cmmd, + this->curMsg.m_dataType, + this->curMsg.m_count, + this->curMsg.m_postsize) ); + debugPrintf ( + ( " Avail=%8u Cid=%8u\n", + this->curMsg.m_available, + this->curMsg.m_cid) ); +# endif + } + + // check for 8 byte aligned protocol + if ( this->curMsg.m_postsize & 0x7 ) { + this->printFormated ( mgr.cbGuard, + "CAC: server sent missaligned payload 0x%x\n", + this->curMsg.m_postsize ); + return false; + } + + // + // make sure we have a large enough message body cache + // + if ( this->curMsg.m_postsize > this->curDataMax ) { + if ( this->curDataMax == MAX_TCP && + this->cacRef.largeBufferSizeTCP() >= this->curMsg.m_postsize ) { + char * pBuf = this->cacRef.allocateLargeBufferTCP (); + if ( pBuf ) { + this->cacRef.releaseSmallBufferTCP ( this->pCurData ); + this->pCurData = pBuf; + this->curDataMax = this->cacRef.largeBufferSizeTCP (); + } + else { + this->printFormated ( mgr.cbGuard, + "CAC: not enough memory for message body cache (ignoring response message)\n"); + } + } + } + + if ( this->curMsg.m_postsize <= this->curDataMax ) { + if ( this->curMsg.m_postsize > 0u ) { + this->curDataBytes += this->recvQue.copyOutBytes ( + &this->pCurData[this->curDataBytes], + this->curMsg.m_postsize - this->curDataBytes ); + if ( this->curDataBytes < this->curMsg.m_postsize ) { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->flushIfRecvProcessRequested ( guard ); + return true; + } + } + bool msgOK = this->cacRef.executeResponse ( mgr, *this, + currentTime, this->curMsg, this->pCurData ); + if ( ! msgOK ) { + return false; + } + } + else { + static bool once = false; + if ( ! once ) { + this->printFormated ( mgr.cbGuard, + "CAC: response with payload size=%u > EPICS_CA_MAX_ARRAY_BYTES ignored\n", + this->curMsg.m_postsize ); + once = true; + } + this->curDataBytes += this->recvQue.removeBytes ( + this->curMsg.m_postsize - this->curDataBytes ); + if ( this->curDataBytes < this->curMsg.m_postsize ) { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->flushIfRecvProcessRequested ( guard ); + return true; + } + } + + this->oldMsgHeaderAvailable = false; + this->msgHeaderAvailable = false; + this->curDataBytes = 0u; + } +# if defined ( __HP_aCC ) && _HP_aCC <= 033300 + return false; // to make hpux compiler happy... +# endif +} + +void tcpiiu::hostNameSetRequest ( epicsGuard < epicsMutex > & guard ) // X aCC 431 +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( ! CA_V41 ( this->minorProtocolVersion ) ) { + return; + } + + const char * pName = this->cacRef.pLocalHostName (); + unsigned size = strlen ( pName ) + 1u; + unsigned postSize = CA_MESSAGE_ALIGN ( size ); + assert ( postSize < 0xffff ); + + if ( this->sendQue.flushEarlyThreshold ( postSize + 16u ) ) { + this->flushRequest ( guard ); + } + + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_HOST_NAME, postSize, + 0u, 0u, 0u, 0u, + CA_V49 ( this->minorProtocolVersion ) ); + this->sendQue.pushString ( pName, size ); + this->sendQue.pushString ( cacNillBytes, postSize - size ); + minder.commit (); +} + +/* + * tcpiiu::userNameSetRequest () + */ +void tcpiiu::userNameSetRequest ( epicsGuard < epicsMutex > & guard ) // X aCC 431 +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( ! CA_V41 ( this->minorProtocolVersion ) ) { + return; + } + + const char *pName = this->cacRef.userNamePointer (); + unsigned size = strlen ( pName ) + 1u; + unsigned postSize = CA_MESSAGE_ALIGN ( size ); + assert ( postSize < 0xffff ); + + if ( this->sendQue.flushEarlyThreshold ( postSize + 16u ) ) { + this->flushRequest ( guard ); + } + + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_CLIENT_NAME, postSize, + 0u, 0u, 0u, 0u, + CA_V49 ( this->minorProtocolVersion ) ); + this->sendQue.pushString ( pName, size ); + this->sendQue.pushString ( cacNillBytes, postSize - size ); + minder.commit (); +} + +void tcpiiu::disableFlowControlRequest ( + epicsGuard < epicsMutex > & guard ) // X aCC 431 +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { + this->flushRequest ( guard ); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_EVENTS_ON, 0u, + 0u, 0u, 0u, 0u, + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::enableFlowControlRequest ( + epicsGuard < epicsMutex > & guard ) // X aCC 431 +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { + this->flushRequest ( guard ); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_EVENTS_OFF, 0u, + 0u, 0u, 0u, 0u, + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::versionMessage ( epicsGuard < epicsMutex > & guard, // X aCC 431 + const cacChannel::priLev & priority ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + assert ( priority <= 0xffff ); + + if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { + this->flushRequest ( guard ); + } + + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_VERSION, 0u, + static_cast < ca_uint16_t > ( priority ), + CA_MINOR_PROTOCOL_REVISION, 0u, 0u, + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::echoRequest ( epicsGuard < epicsMutex > & guard ) // X aCC 431 +{ + guard.assertIdenticalMutex ( this->mutex ); + + epicsUInt16 command = CA_PROTO_ECHO; + if ( ! CA_V43 ( this->minorProtocolVersion ) ) { + // we fake an echo to early server using a read sync + command = CA_PROTO_READ_SYNC; + } + + if ( this->sendQue.flushEarlyThreshold ( 16u ) ) { + this->flushRequest ( guard ); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + command, 0u, + 0u, 0u, 0u, 0u, + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::writeRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu &chan, unsigned type, arrayElementCount nElem, const void *pValue ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( INVALID_DB_REQ ( type ) ) { + throw cacChannel::badType (); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestWithPayLoad ( CA_PROTO_WRITE, + type, nElem, chan.getSID(guard), chan.getCID(guard), pValue, + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + + +void tcpiiu::writeNotifyRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu &chan, netWriteNotifyIO &io, unsigned type, + arrayElementCount nElem, const void *pValue ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( ! this->ca_v41_ok ( guard ) ) { + throw cacChannel::unsupportedByService(); + } + if ( INVALID_DB_REQ ( type ) ) { + throw cacChannel::badType (); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestWithPayLoad ( CA_PROTO_WRITE_NOTIFY, + type, nElem, chan.getSID(guard), io.getId(), pValue, + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::readNotifyRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu & chan, netReadNotifyIO & io, + unsigned dataType, arrayElementCount nElem ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( INVALID_DB_REQ ( dataType ) ) { + throw cacChannel::badType (); + } + arrayElementCount maxBytes; + if ( CA_V49 ( this->minorProtocolVersion ) ) { + maxBytes = this->cacRef.largeBufferSizeTCP (); + } + else { + maxBytes = MAX_TCP; + } + arrayElementCount maxElem = + ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + if ( nElem > maxElem ) { + throw cacChannel::msgBodyCacheTooSmall (); + } + if (nElem == 0 && !CA_V413(this->minorProtocolVersion)) + nElem = chan.getcount(); + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_READ_NOTIFY, 0u, + static_cast < ca_uint16_t > ( dataType ), + static_cast < ca_uint32_t > ( nElem ), + chan.getSID(guard), io.getId(), + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::createChannelRequest ( + nciu & chan, epicsGuard < epicsMutex > & guard ) // X aCC 431 +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( this->state != iiucs_connected && + this->state != iiucs_connecting ) { + return; + } + + const char *pName; + unsigned nameLength; + ca_uint32_t identity; + if ( this->ca_v44_ok ( guard ) ) { + identity = chan.getCID ( guard ); + pName = chan.pName ( guard ); + nameLength = chan.nameLen ( guard ); + } + else { + identity = chan.getSID ( guard ); + pName = 0; + nameLength = 0u; + } + + unsigned postCnt = CA_MESSAGE_ALIGN ( nameLength ); + + if ( postCnt >= 0xffff ) { + throw cacChannel::unsupportedByService(); + } + + comQueSendMsgMinder minder ( this->sendQue, guard ); + // + // The available field is used (abused) + // here to communicate the minor version number + // starting with CA 4.1. + // + this->sendQue.insertRequestHeader ( + CA_PROTO_CREATE_CHAN, postCnt, + 0u, 0u, identity, CA_MINOR_PROTOCOL_REVISION, + CA_V49 ( this->minorProtocolVersion ) ); + if ( nameLength ) { + this->sendQue.pushString ( pName, nameLength ); + } + if ( postCnt > nameLength ) { + this->sendQue.pushString ( cacNillBytes, postCnt - nameLength ); + } + minder.commit (); +} + +void tcpiiu::clearChannelRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 + ca_uint32_t sid, ca_uint32_t cid ) +{ + guard.assertIdenticalMutex ( this->mutex ); + // there are situations where the circuit is disconnected, but + // the channel does not know this yet + if ( this->state != iiucs_connected ) { + return; + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_CLEAR_CHANNEL, 0u, + 0u, 0u, sid, cid, + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +// +// this routine return void because if this internally fails the best response +// is to try again the next time that we reconnect +// +void tcpiiu::subscriptionRequest ( + epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu & chan, netSubscription & subscr ) +{ + guard.assertIdenticalMutex ( this->mutex ); + // there are situations where the circuit is disconnected, but + // the channel does not know this yet + if ( this->state != iiucs_connected && + this->state != iiucs_connecting ) { + return; + } + unsigned mask = subscr.getMask(guard); + if ( mask > 0xffff ) { + throw cacChannel::badEventSelection (); + } + arrayElementCount nElem = subscr.getCount ( + guard, CA_V413(this->minorProtocolVersion) ); + arrayElementCount maxBytes; + if ( CA_V49 ( this->minorProtocolVersion ) ) { + maxBytes = this->cacRef.largeBufferSizeTCP (); + } + else { + maxBytes = MAX_TCP; + } + unsigned dataType = subscr.getType ( guard ); + // data type bounds checked when sunscription created + arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + if ( nElem > maxElem ) { + throw cacChannel::msgBodyCacheTooSmall (); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + // nElement bounds checked above + this->sendQue.insertRequestHeader ( + CA_PROTO_EVENT_ADD, 16u, + static_cast < ca_uint16_t > ( dataType ), + static_cast < ca_uint32_t > ( nElem ), + chan.getSID(guard), subscr.getId(), + CA_V49 ( this->minorProtocolVersion ) ); + + // extension + this->sendQue.pushFloat32 ( 0.0f ); // m_lval + this->sendQue.pushFloat32 ( 0.0f ); // m_hval + this->sendQue.pushFloat32 ( 0.0f ); // m_toval + this->sendQue.pushUInt16 ( static_cast < ca_uint16_t > ( mask ) ); // m_mask + this->sendQue.pushUInt16 ( 0u ); // m_pad + minder.commit (); +} + +// +// this routine return void because if this internally fails the best response +// is to try again the next time that we reconnect +// +void tcpiiu::subscriptionUpdateRequest ( + epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu & chan, netSubscription & subscr ) +{ + guard.assertIdenticalMutex ( this->mutex ); + // there are situations where the circuit is disconnected, but + // the channel does not know this yet + if ( this->state != iiucs_connected ) { + return; + } + arrayElementCount nElem = subscr.getCount ( + guard, CA_V413(this->minorProtocolVersion) ); + arrayElementCount maxBytes; + if ( CA_V49 ( this->minorProtocolVersion ) ) { + maxBytes = this->cacRef.largeBufferSizeTCP (); + } + else { + maxBytes = MAX_TCP; + } + unsigned dataType = subscr.getType ( guard ); + // data type bounds checked when subscription constructed + arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType]; + if ( nElem > maxElem ) { + throw cacChannel::msgBodyCacheTooSmall (); + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + // nElem boounds checked above + this->sendQue.insertRequestHeader ( + CA_PROTO_READ_NOTIFY, 0u, + static_cast < ca_uint16_t > ( dataType ), + static_cast < ca_uint32_t > ( nElem ), + chan.getSID (guard), subscr.getId (), + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +void tcpiiu::subscriptionCancelRequest ( epicsGuard < epicsMutex > & guard, // X aCC 431 + nciu & chan, netSubscription & subscr ) +{ + guard.assertIdenticalMutex ( this->mutex ); + // there are situations where the circuit is disconnected, but + // the channel does not know this yet + if ( this->state != iiucs_connected ) { + return; + } + comQueSendMsgMinder minder ( this->sendQue, guard ); + this->sendQue.insertRequestHeader ( + CA_PROTO_EVENT_CANCEL, 0u, + static_cast < ca_uint16_t > ( subscr.getType ( guard ) ), + static_cast < ca_uint16_t > ( subscr.getCount ( + guard, CA_V413(this->minorProtocolVersion) ) ), + chan.getSID(guard), subscr.getId(), + CA_V49 ( this->minorProtocolVersion ) ); + minder.commit (); +} + +bool tcpiiu::sendThreadFlush ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( this->sendQue.occupiedBytes() > 0 ) { + while ( comBuf * pBuf = this->sendQue.popNextComBufToSend () ) { + epicsTime current = epicsTime::getCurrent (); + + unsigned bytesToBeSent = pBuf->occupiedBytes (); + bool success = false; + { + // no lock while blocking to send + epicsGuardRelease < epicsMutex > unguard ( guard ); + success = pBuf->flushToWire ( *this, current ); + pBuf->~comBuf (); + this->comBufMemMgr.release ( pBuf ); + } + + if ( ! success ) { + while ( ( pBuf = this->sendQue.popNextComBufToSend () ) ) { + pBuf->~comBuf (); + this->comBufMemMgr.release ( pBuf ); + } + return false; + } + + // set it here with this odd order because we must have + // the lock and we must have already sent the bytes + this->unacknowledgedSendBytes += bytesToBeSent; + if ( this->unacknowledgedSendBytes > + this->socketLibrarySendBufferSize ) { + this->recvDog.sendBacklogProgressNotify ( guard ); + } + } + } + + this->earlyFlush = false; + if ( this->blockingForFlush ) { + this->flushBlockEvent.signal (); + } + + return true; +} + +void tcpiiu :: flush ( epicsGuard < epicsMutex > & guard ) +{ + this->flushRequest ( guard ); + // the process thread is not permitted to flush as this + // can result in a push / pull deadlock on the TCP pipe. + // Instead, the process thread scheduals the flush with the + // send thread which runs at a higher priority than the + // receive thread. The same applies to the UDP thread for + // locking hierarchy reasons. + if ( ! epicsThreadPrivateGet ( caClientCallbackThreadId ) ) { + // enable / disable of call back preemption must occur here + // because the tcpiiu might disconnect while waiting and its + // pointer to this cac might become invalid + assert ( this->blockingForFlush < UINT_MAX ); + this->blockingForFlush++; + while ( this->sendQue.flushBlockThreshold() ) { + + bool userRequestsCanBeAccepted = + this->state == iiucs_connected || + ( ! this->ca_v42_ok ( guard ) && + this->state == iiucs_connecting ); + // fail the users request if we have a disconnected + // or unresponsive circuit + if ( ! userRequestsCanBeAccepted || + this->unresponsiveCircuit ) { + this->decrementBlockingForFlushCount ( guard ); + throw cacChannel::notConnected (); + } + + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->flushBlockEvent.wait ( 30.0 ); + } + this->decrementBlockingForFlushCount ( guard ); + } +} + +unsigned tcpiiu::requestMessageBytesPending ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); +#if 0 + if ( ! this->earlyFlush && this->sendQue.flushEarlyThreshold(0u) ) { + this->earlyFlush = true; + this->sendThreadFlushEvent.signal (); + } +#endif + return sendQue.occupiedBytes (); +} + +void tcpiiu::decrementBlockingForFlushCount ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + assert ( this->blockingForFlush > 0u ); + this->blockingForFlush--; + if ( this->blockingForFlush > 0 ) { + this->flushBlockEvent.signal (); + } +} + +osiSockAddr tcpiiu::getNetworkAddress ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->address(); +} + +// not inline because its virtual +bool tcpiiu::ca_v42_ok ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return CA_V42 ( this->minorProtocolVersion ); +} + +void tcpiiu::requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->recvProcessPostponedFlush = true; +} + +unsigned tcpiiu::getHostName ( + epicsGuard < epicsMutex > & guard, + char * pBuf, unsigned bufLength ) const throw () +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->hostNameCacheInstance.getName ( pBuf, bufLength ); +} + +const char * tcpiiu::pHostName ( + epicsGuard < epicsMutex > & guard ) const throw () +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->hostNameCacheInstance.pointer (); +} + +void tcpiiu::disconnectAllChannels ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, + class udpiiu & discIIU ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + + while ( nciu * pChan = this->createReqPend.get () ) { + discIIU.installDisconnectedChannel ( guard, *pChan ); + } + + while ( nciu * pChan = this->createRespPend.get () ) { + // we dont yet know the server's id so we cant + // send a channel delete request and will instead + // trust that the server can do the proper cleanup + // when the circuit disconnects + discIIU.installDisconnectedChannel ( guard, *pChan ); + } + + while ( nciu * pChan = this->v42ConnCallbackPend.get () ) { + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + discIIU.installDisconnectedChannel ( guard, *pChan ); + } + + while ( nciu * pChan = this->subscripReqPend.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + discIIU.installDisconnectedChannel ( guard, *pChan ); + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->connectedList.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + discIIU.installDisconnectedChannel ( guard, *pChan ); + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->unrespCircuit.get () ) { + // if we know that the circuit is unresponsive + // then we dont send a channel delete request and + // will instead trust that the server can do the + // proper cleanup when the circuit disconnects + pChan->disconnectAllIO ( cbGuard, guard ); + discIIU.installDisconnectedChannel ( guard, *pChan ); + } + + while ( nciu * pChan = this->subscripUpdateReqPend.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + discIIU.installDisconnectedChannel ( guard, *pChan ); + pChan->unresponsiveCircuitNotify ( cbGuard, guard ); + } + + this->channelCountTot = 0u; + this->initiateCleanShutdown ( guard ); +} + +void tcpiiu::unlinkAllChannels ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + guard.assertIdenticalMutex ( this->mutex ); + + while ( nciu * pChan = this->createReqPend.get () ) { + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->createRespPend.get () ) { + // we dont yet know the server's id so we cant + // send a channel delete request and will instead + // trust that the server can do the proper cleanup + // when the circuit disconnects + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->v42ConnCallbackPend.get () ) { + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->subscripReqPend.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->connectedList.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->unrespCircuit.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + // if we know that the circuit is unresponsive + // then we dont send a channel delete request and + // will instead trust that the server can do the + // proper cleanup when the circuit disconnects + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + + while ( nciu * pChan = this->subscripUpdateReqPend.get () ) { + pChan->disconnectAllIO ( cbGuard, guard ); + this->clearChannelRequest ( guard, + pChan->getSID(guard), pChan->getCID(guard) ); + pChan->serviceShutdownNotify ( cbGuard, guard ); + } + + this->channelCountTot = 0u; + this->initiateCleanShutdown ( guard ); +} + +void tcpiiu::installChannel ( + epicsGuard < epicsMutex > & guard, + nciu & chan, unsigned sidIn, + ca_uint16_t typeIn, arrayElementCount countIn ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + this->createReqPend.add ( chan ); + this->channelCountTot++; + chan.channelNode::listMember = channelNode::cs_createReqPend; + chan.searchReplySetUp ( *this, sidIn, typeIn, countIn, guard ); + // The tcp send thread runs at apriority below the udp thread + // so that this will not send small packets + this->sendThreadFlushEvent.signal (); +} + +bool tcpiiu :: connectNotify ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + bool wasExpected = false; + // this improves robustness in the face of a server sending + // protocol that does not match its declared protocol revision + if ( chan.channelNode::listMember == channelNode::cs_createRespPend ) { + this->createRespPend.remove ( chan ); + this->subscripReqPend.add ( chan ); + chan.channelNode::listMember = channelNode::cs_subscripReqPend; + wasExpected = true; + } + else if ( chan.channelNode::listMember == channelNode::cs_v42ConnCallbackPend ) { + this->v42ConnCallbackPend.remove ( chan ); + this->subscripReqPend.add ( chan ); + chan.channelNode::listMember = channelNode::cs_subscripReqPend; + wasExpected = true; + } + // the TCP send thread is awakened by its receive thread whenever the receive thread + // is about to block if this->subscripReqPend has items in it + return wasExpected; +} + +void tcpiiu::uninstallChan ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + switch ( chan.channelNode::listMember ) { + case channelNode::cs_createReqPend: + this->createReqPend.remove ( chan ); + break; + case channelNode::cs_createRespPend: + this->createRespPend.remove ( chan ); + break; + case channelNode::cs_v42ConnCallbackPend: + this->v42ConnCallbackPend.remove ( chan ); + break; + case channelNode::cs_subscripReqPend: + this->subscripReqPend.remove ( chan ); + break; + case channelNode::cs_connected: + this->connectedList.remove ( chan ); + break; + case channelNode::cs_unrespCircuit: + this->unrespCircuit.remove ( chan ); + break; + case channelNode::cs_subscripUpdateReqPend: + this->subscripUpdateReqPend.remove ( chan ); + break; + default: + errlogPrintf ( + "cac: attempt to uninstall channel from tcp iiu, but it inst installed there?" ); + } + chan.channelNode::listMember = channelNode::cs_none; + this->channelCountTot--; + if ( this->channelCountTot == 0 && ! this->isNameService() ) { + this->initiateCleanShutdown ( guard ); + } +} + +int tcpiiu :: printFormated ( + epicsGuard < epicsMutex > & cbGuard, + const char *pformat, ... ) +{ + cbGuard.assertIdenticalMutex ( this->cbMutex ); + + va_list theArgs; + int status; + + va_start ( theArgs, pformat ); + + status = this->cacRef.varArgsPrintFormated ( cbGuard, pformat, theArgs ); + + va_end ( theArgs ); + + return status; +} + +void tcpiiu::flushRequest ( epicsGuard < epicsMutex > & ) +{ + if ( this->sendQue.occupiedBytes () > 0 ) { + this->sendThreadFlushEvent.signal (); + } +} + +bool tcpiiu::bytesArePendingInOS () const +{ +#if 0 + FD_SET readBits; + FD_ZERO ( & readBits ); + FD_SET ( this->sock, & readBits ); + struct timeval tmo; + tmo.tv_sec = 0; + tmo.tv_usec = 0; + int status = select ( this->sock + 1, & readBits, NULL, NULL, & tmo ); + if ( status > 0 ) { + if ( FD_ISSET ( this->sock, & readBits ) ) { + return true; + } + } + return false; +#else + osiSockIoctl_t bytesPending = 0; /* shut up purifys yapping */ + int status = socket_ioctl ( this->sock, // X aCC 392 + FIONREAD, & bytesPending ); + if ( status >= 0 ) { + if ( bytesPending > 0 ) { + return true; + } + } + return false; +#endif +} + +double tcpiiu::receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const +{ + return this->recvDog.delay (); +} + +/* + * Certain OS, such as HPUX, do not unblock a socket system call + * when another thread asynchronously calls both shutdown() and + * close(). To solve this problem we need to employ OS specific + * mechanisms. + */ +void tcpRecvThread::interruptSocketRecv () +{ + epicsThreadId threadId = this->thread.getId (); + if ( threadId ) { + epicsSignalRaiseSigAlarm ( threadId ); + } +} +void tcpSendThread::interruptSocketSend () +{ + epicsThreadId threadId = this->thread.getId (); + if ( threadId ) { + epicsSignalRaiseSigAlarm ( threadId ); + } +} + +void tcpiiu::operator delete ( void * /* pCadaver */ ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about " + "placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +unsigned tcpiiu::channelCount ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->channelCountTot; +} + +void tcpiiu::uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > & guard, nciu & chan, + const class epicsTime & currentTime ) +{ + netiiu::uninstallChanDueToSuccessfulSearchResponse ( + guard, chan, currentTime ); +} + +bool tcpiiu::searchMsg ( + epicsGuard < epicsMutex > & guard, ca_uint32_t id, + const char * pName, unsigned nameLength ) +{ + return netiiu::searchMsg ( + guard, id, pName, nameLength ); +} + +SearchDestTCP :: SearchDestTCP ( + cac & cacIn, const osiSockAddr & addrIn ) : + _ptcpiiu ( NULL ), + _cac ( cacIn ), + _addr ( addrIn ), + _active ( false ) +{ +} + +void SearchDestTCP :: disable () +{ + _active = false; + _ptcpiiu = NULL; +} + +void SearchDestTCP :: enable () +{ + _active = true; +} + +void SearchDestTCP :: searchRequest ( + epicsGuard < epicsMutex > & guard, + const char * pBuf, size_t len ) +{ + // restart circuit if it was shut down + if ( ! _ptcpiiu ) { + tcpiiu * piiu = NULL; + bool newIIU = _cac.findOrCreateVirtCircuit ( + guard, _addr, cacChannel::priorityDefault, + piiu, CA_UKN_MINOR_VERSION, this ); + if ( newIIU ) { + piiu->start ( guard ); + } + _ptcpiiu = piiu; + } + + // does this server support TCP-based name resolution? + if ( CA_V412 ( _ptcpiiu->minorProtocolVersion ) ) { + guard.assertIdenticalMutex ( _ptcpiiu->mutex ); + assert ( CA_MESSAGE_ALIGN ( len ) == len ); + comQueSendMsgMinder minder ( _ptcpiiu->sendQue, guard ); + _ptcpiiu->sendQue.pushString ( pBuf, len ); + minder.commit (); + _ptcpiiu->flushRequest ( guard ); + } +} + +void SearchDestTCP :: show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + :: printf ( "tcpiiu :: SearchDestTCP\n" ); +} + +void tcpiiu :: versionRespNotify ( const caHdrLargeArray & msg ) +{ + this->minorProtocolVersion = msg.m_count; +} + +void tcpiiu :: searchRespNotify ( + const epicsTime & currentTime, const caHdrLargeArray & msg ) +{ + /* + * the type field is abused to carry the port number + * so that we can have multiple servers on one host + */ + osiSockAddr serverAddr; + if ( msg.m_cid != INADDR_BROADCAST ) { + serverAddr.ia.sin_family = AF_INET; + serverAddr.ia.sin_addr.s_addr = htonl ( msg.m_cid ); + serverAddr.ia.sin_port = htons ( msg.m_dataType ); + } + else { + serverAddr = this->address (); + } + cacRef.transferChanToVirtCircuit + ( msg.m_available, msg.m_cid, 0xffff, + 0, minorProtocolVersion, serverAddr, currentTime ); +} diff --git a/src/ca/templateInstances.cpp b/src/ca/templateInstances.cpp new file mode 100644 index 000000000..ae3f9245d --- /dev/null +++ b/src/ca/templateInstances.cpp @@ -0,0 +1,85 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#define epicsExportSharedSymbols +#include "virtualCircuit.h" +#include "bhe.h" +#include "cac.h" +#include "syncGroup.h" +#include "nciu.h" +#include "udpiiu.h" +#include "oldAccess.h" +#include "msgForMultiplyDefinedPV.h" +#include "repeaterClient.h" +#include "hostNameCache.h" +#include "comBuf.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class resTable < nciu, chronIntId >; +template class chronIntIdResTable < nciu >; +template class resTable < baseNMIU, chronIntId >; +template class chronIntIdResTable < baseNMIU >; +template class resTable < CASG, chronIntId >; +template class chronIntIdResTable < CASG >; +template class resTable < bhe, inetAddrID >; +template class resTable < tcpiiu, caServerID >; +template class tsFreeList < bhe, 0x100 >; +template class tsFreeList < tcpiiu, 32, epicsMutexNOOP >; +template class tsFreeList < netReadNotifyIO, 1024, epicsMutexNOOP >; +template class tsFreeList < netWriteNotifyIO, 1024, epicsMutexNOOP >; +template class tsFreeList < netSubscription, 1024, epicsMutexNOOP >; +template class tsFreeList < CASG, 128, epicsMutexNOOP >; +template class tsFreeList < syncGroupReadNotify, 128, epicsMutexNOOP >; +template class tsFreeList < syncGroupWriteNotify, 128, epicsMutexNOOP >; +template class tsFreeList < comBuf, 0x20 >; +template class tsFreeList < getCallback, 1024, epicsMutexNOOP >; +template class tsFreeList < getCopy, 1024, epicsMutexNOOP >; +template class tsFreeList < msgForMultiplyDefinedPV, 16 >; +template class tsFreeList < nciu, 1024, epicsMutexNOOP>; +template class tsFreeList < oldChannelNotify, 1024, epicsMutexNOOP >; +template class tsFreeList < oldSubscription, 1024, epicsMutexNOOP >; +template class tsFreeList < putCallback, 1024, epicsMutexNOOP >; +template class tsFreeList < repeaterClient, 0x20 >; +template class epicsSingleton < localHostName >; +template class epics_auto_ptr < epics_auto_ptr < class searchTimer >, eapt_array >; +template unsigned comBuf :: push ( const double * pValue, unsigned nElem ); +template unsigned comBuf :: push ( const float * pValue, unsigned nElem ); +template unsigned comBuf :: push ( const int * pValue, unsigned nElem ); +template unsigned comBuf :: push ( const short * pValue, unsigned nElem ); +template comBuf :: popStatus comBuf :: pop ( unsigned int & returnVal ); +template comBuf :: popStatus comBuf :: pop ( unsigned short & returnVal ); +template comBuf :: popStatus comBuf :: pop ( unsigned char & returnVal ); +template void WireSet ( float const &, unsigned char * ); +template void WireSet ( int const &, unsigned char * ); +template void WireSet ( short const &, unsigned char * ); + + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif diff --git a/src/ca/test_event.cpp b/src/ca/test_event.cpp new file mode 100644 index 000000000..c39375aef --- /dev/null +++ b/src/ca/test_event.cpp @@ -0,0 +1,588 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * T E S T _ E V E N T . C + * Author: Jeffrey O. Hill + * simple stub for testing monitors + */ + +#include + +#define epicsExportSharedSymbols +#include "cadef.h" + +extern "C" void epicsShareAPI ca_test_event ( struct event_handler_args args ) +{ + chtype nativeType = ca_field_type ( args.chid ); + const char * pNativeTypeName = ""; + if ( VALID_DB_REQ ( nativeType ) ) { + pNativeTypeName = dbr_text[nativeType]; + } + else { + if ( nativeType == TYPENOTCONN ) { + pNativeTypeName = ""; + } + } + + printf ( "ca_test_event() for channel \"%s\" with native type %s\n", + ca_name(args.chid), pNativeTypeName ); + + if ( ! ( CA_M_SUCCESS & args.status ) ) { + printf ( "Invalid CA status \"%s\"\n", ca_message ( args.status ) ); + return; + } + + if ( args.dbr ) { + ca_dump_dbr ( args.type, args.count, args.dbr ); + } +} + +/* + * ca_dump_dbr() + * dump the specified dbr type to stdout + */ +extern "C" void epicsShareAPI ca_dump_dbr ( + chtype type, unsigned count, const void * pbuffer ) +{ + unsigned i; + char tsString[50]; + + if ( INVALID_DB_REQ ( type ) ) { + printf ( "bad DBR type %ld\n", type ); + } + + printf ( "%s\t", dbr_text[type] ); + + switch ( type ) { + case DBR_STRING: + { + dbr_string_t *pString = (dbr_string_t *) pbuffer; + + for(i=0; istatus,pvalue->severity); + printf("\tValue: %s",pvalue->value); + break; + } + case DBR_STS_ENUM: + { + struct dbr_sts_enum *pvalue + = (struct dbr_sts_enum *)pbuffer; + dbr_enum_t *pEnum = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pEnum++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%u ",*pEnum); + } + break; + } + case DBR_STS_SHORT: + { + struct dbr_sts_short *pvalue + = (struct dbr_sts_short *)pbuffer; + dbr_short_t *pshort = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%u ",*pshort); + } + break; + } + case DBR_STS_FLOAT: + { + struct dbr_sts_float *pvalue + = (struct dbr_sts_float *)pbuffer; + dbr_float_t *pfloat = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case DBR_STS_CHAR: + { + struct dbr_sts_char *pvalue + = (struct dbr_sts_char *)pbuffer; + dbr_char_t *pchar = &pvalue->value; + + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%u ", *pchar); + } + break; + } + case DBR_STS_LONG: + { + struct dbr_sts_long *pvalue + = (struct dbr_sts_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case DBR_STS_DOUBLE: + { + struct dbr_sts_double *pvalue + = (struct dbr_sts_double *)pbuffer; + dbr_double_t *pdouble = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",(float)(*pdouble)); + } + break; + } + case DBR_TIME_STRING: + { + struct dbr_time_string *pvalue + = (struct dbr_time_string *) pbuffer; + + epicsTimeToStrftime(tsString,sizeof(tsString), + "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %s",tsString); + printf("\tValue: "); + printf("%s",pvalue->value); + break; + } + case DBR_TIME_ENUM: + { + struct dbr_time_enum *pvalue + = (struct dbr_time_enum *)pbuffer; + dbr_enum_t *pshort = &pvalue->value; + + epicsTimeToStrftime(tsString,sizeof(tsString), + "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %s",tsString); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case DBR_TIME_SHORT: + { + struct dbr_time_short *pvalue + = (struct dbr_time_short *)pbuffer; + dbr_short_t *pshort = &pvalue->value; + epicsTimeToStrftime(tsString,sizeof(tsString), + "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); + printf("%2d %2d", + pvalue->status, + pvalue->severity); + printf("\tTimeStamp: %s",tsString); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case DBR_TIME_FLOAT: + { + struct dbr_time_float *pvalue + = (struct dbr_time_float *)pbuffer; + dbr_float_t *pfloat = &pvalue->value; + + epicsTimeToStrftime(tsString,sizeof(tsString), + "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %s",tsString); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case DBR_TIME_CHAR: + { + struct dbr_time_char *pvalue + = (struct dbr_time_char *)pbuffer; + dbr_char_t *pchar = &pvalue->value; + + epicsTimeToStrftime(tsString,sizeof(tsString), + "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %s",tsString); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",(short)(*pchar)); + } + break; + } + case DBR_TIME_LONG: + { + struct dbr_time_long *pvalue + = (struct dbr_time_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + + epicsTimeToStrftime(tsString,sizeof(tsString), + "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %s",tsString); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case DBR_TIME_DOUBLE: + { + struct dbr_time_double *pvalue + = (struct dbr_time_double *)pbuffer; + dbr_double_t *pdouble = &pvalue->value; + + epicsTimeToStrftime(tsString,sizeof(tsString), + "%Y/%m/%d %H:%M:%S.%06f",&pvalue->stamp); + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %s",tsString); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",(float)(*pdouble)); + } + break; + } + case DBR_GR_SHORT: + { + struct dbr_gr_short *pvalue + = (struct dbr_gr_short *)pbuffer; + dbr_short_t *pshort = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case DBR_GR_FLOAT: + { + struct dbr_gr_float *pvalue + = (struct dbr_gr_float *)pbuffer; + dbr_float_t *pfloat = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case DBR_GR_ENUM: + { + struct dbr_gr_enum *pvalue + = (struct dbr_gr_enum *)pbuffer; + printf("%2d %2d",pvalue->status, + pvalue->severity); + printf("\tValue: %d",pvalue->value); + if(pvalue->no_str>0) { + printf("\n\t%3d",pvalue->no_str); + for (i = 0; i < (unsigned) pvalue->no_str; i++) + printf("\n\t%.26s",pvalue->strs[i]); + } + break; + } + case DBR_CTRL_ENUM: + { + struct dbr_ctrl_enum *pvalue + = (struct dbr_ctrl_enum *)pbuffer; + printf("%2d %2d",pvalue->status, + pvalue->severity); + printf("\tValue: %d",pvalue->value); + if(pvalue->no_str>0) { + printf("\n\t%3d",pvalue->no_str); + for (i = 0; i < (unsigned) pvalue->no_str; i++) + printf("\n\t%.26s",pvalue->strs[i]); + } + break; + } + case DBR_GR_CHAR: + { + struct dbr_gr_char *pvalue + = (struct dbr_gr_char *)pbuffer; + dbr_char_t *pchar = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%u ",*pchar); + } + break; + } + case DBR_GR_LONG: + { + struct dbr_gr_long *pvalue + = (struct dbr_gr_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case DBR_GR_DOUBLE: + { + struct dbr_gr_double *pvalue + = (struct dbr_gr_double *)pbuffer; + dbr_double_t *pdouble = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + (float)(pvalue->upper_disp_limit), + (float)(pvalue->lower_disp_limit), + (float)(pvalue->upper_alarm_limit), + (float)(pvalue->upper_warning_limit), + (float)(pvalue->lower_warning_limit), + (float)(pvalue->lower_alarm_limit)); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",(float)(*pdouble)); + } + break; + } + case DBR_CTRL_SHORT: + { + struct dbr_ctrl_short *pvalue + = (struct dbr_ctrl_short *)pbuffer; + dbr_short_t *pshort = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8d %8d", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case DBR_CTRL_FLOAT: + { + struct dbr_ctrl_float *pvalue + = (struct dbr_ctrl_float *)pbuffer; + dbr_float_t *pfloat = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8.3f %8.3f", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case DBR_CTRL_CHAR: + { + struct dbr_ctrl_char *pvalue + = (struct dbr_ctrl_char *)pbuffer; + dbr_char_t *pchar = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8d %8d", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%4d ",(short)(*pchar)); + } + break; + } + case DBR_CTRL_LONG: + { + struct dbr_ctrl_long *pvalue + = (struct dbr_ctrl_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8d %8d", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case DBR_CTRL_DOUBLE: + { + struct dbr_ctrl_double *pvalue + = (struct dbr_ctrl_double *)pbuffer; + dbr_double_t *pdouble = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + (float)(pvalue->upper_disp_limit), + (float)(pvalue->lower_disp_limit), + (float)(pvalue->upper_alarm_limit), + (float)(pvalue->upper_warning_limit), + (float)(pvalue->lower_warning_limit), + (float)(pvalue->lower_alarm_limit)); + printf(" %8.3f %8.3f", + (float)(pvalue->upper_ctrl_limit), + (float)(pvalue->lower_ctrl_limit)); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.6f ",(float)(*pdouble)); + } + break; + } + case DBR_STSACK_STRING: + { + struct dbr_stsack_string *pvalue + = (struct dbr_stsack_string *)pbuffer; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf(" %2d %2d",pvalue->ackt,pvalue->acks); + printf(" %s",pvalue->value); + break; + } + case DBR_CLASS_NAME: + { + dbr_class_name_t * pvalue = + ( dbr_class_name_t * ) pbuffer; + printf ( "%s", *pvalue ); + break; + } + default: + printf ( + "unsupported by ca_dbrDump()" ); + break; + } + printf("\n"); +} + diff --git a/src/ca/ucx.h b/src/ca/ucx.h new file mode 100644 index 000000000..c164161a5 --- /dev/null +++ b/src/ca/ucx.h @@ -0,0 +1,102 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * U C X . H + * UNIX ioctl structures and defines used for VAX/UCX + * + */ +#ifndef _UCX_H_ +# define _UCX_H_ +#ifdef UCX + +#define IFF_UP 0x1 /* interface is up */ +#define IFF_BROADCAST 0x2 /* broadcast address valid */ +#define IFF_LOOPBACK 0x8 /* is a loopback net */ +#define IFF_POINTOPOINT 0x10 /* interface is point to point */ +/* + * Interface request structure used for socket + * ioctl's. All interface ioctl's must have parameter + * definitions which begin with ifr_name. The + * remainder may be interface specific. + */ +struct ifreq { +#define IFNAMSIZ 16 + char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + short ifru_flags; + int ifru_metric; + caddr_t ifru_data; + } ifr_ifru; +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-to-p link */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_metric ifr_ifru.ifru_metric /* metric */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface */ +}; + +/* Structure used in SIOCGIFCONF request. + * Used to retrieve interface configuration + * for machine (useful for programs which + * must know all networks accessible). + */ +struct ifconf { + int ifc_len; /* size of associated buffer */ + union { + caddr_t ifcu_buf; + struct ifreq *ifcu_req; + } ifc_ifcu; +#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */ +#define ifc_req ifc_ifcu.ifcu_req /* array of structures returned */ +}; + +#ifndef NBBY +# define NBBY 8 +#endif + + +#ifndef FD_SETSIZE +# define FD_SETSIZE 256 +#endif + +typedef long fd_mask ; +#define NFDBITS (sizeof (fd_mask) * NBBY ) /* bits per mask */ +#ifndef howmany +# define howmany(x, y) (((x)+((y)-1))/(y)) +#endif + +/* + * Both DEC C and VAX C only allow 32 fd's at once + */ +typedef int fd_set ; + +#define FD_SET(n, p) (*(p) |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) (*(p) &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) (*(p) & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) memset((char *)(p), 0, sizeof (*(p))) + +#include +#define IO$_RECEIVE (IO$_WRITEVBLK) + +struct timezone { + int tz_minuteswest ; /* minutes west of Greenwich */ + int tz_dsttime ; /* type of dst correction */ +}; + +#define TWOPOWER32 4294967296.0 +#define TWOPOWER31 2147483648.0 +#define UNIX_EPOCH_AS_MJD 40587.0 +#endif +#endif + diff --git a/src/ca/udpiiu.cpp b/src/ca/udpiiu.cpp new file mode 100644 index 000000000..755875aa7 --- /dev/null +++ b/src/ca/udpiiu.cpp @@ -0,0 +1,1393 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#ifdef _MSC_VER +# pragma warning(disable:4355) +#endif + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" + +#include "envDefs.h" +#include "dbDefs.h" +#include "osiProcess.h" +#include "osiWireFormat.h" +#include "epicsAlgorithm.h" +#include "errlog.h" +#include "locationException.h" + +#define epicsExportSharedSymbols +#include "addrList.h" +#include "caerr.h" // for ECA_NOSEARCHADDR +#include "udpiiu.h" +#include "iocinf.h" +#include "inetAddrID.h" +#include "cac.h" +#include "disconnectGovernorTimer.h" + +// UDP protocol dispatch table +const udpiiu::pProtoStubUDP udpiiu::udpJumpTableCAC [] = +{ + &udpiiu::versionAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::searchRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::exceptionRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::beaconAction, + &udpiiu::notHereRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::badUDPRespAction, + &udpiiu::repeaterAckAction, +}; + +// +// udpiiu::udpiiu () +// +udpiiu::udpiiu ( + epicsGuard < epicsMutex > & cacGuard, + epicsTimerQueueActive & timerQueue, + epicsMutex & cbMutexIn, + epicsMutex & cacMutexIn, + cacContextNotify & ctxNotifyIn, + cac & cac, + unsigned port, + tsDLList < SearchDest > & searchDestListIn ) : + recvThread ( *this, ctxNotifyIn, cbMutexIn, "CAC-UDP", + epicsThreadGetStackSize ( epicsThreadStackMedium ), + cac::lowestPriorityLevelAbove ( + cac::lowestPriorityLevelAbove ( + cac.getInitializingThreadsPriority () ) ) ), + repeaterSubscribeTmr ( + *this, timerQueue, cbMutexIn, ctxNotifyIn ), + govTmr ( *this, timerQueue, cacMutexIn ), + maxPeriod ( maxSearchPeriodDefault ), + rtteMean ( minRoundTripEstimate ), + rtteMeanDev ( 0 ), + cacRef ( cac ), + cbMutex ( cbMutexIn ), + cacMutex ( cacMutexIn ), + nBytesInXmitBuf ( 0 ), + nTimers ( 0 ), + beaconAnomalyTimerIndex ( 0 ), + sequenceNumber ( 0 ), + lastReceivedSeqNo ( 0 ), + sock ( 0 ), + repeaterPort ( 0 ), + serverPort ( port ), + localPort ( 0 ), + shutdownCmd ( false ), + lastReceivedSeqNoIsValid ( false ) +{ + cacGuard.assertIdenticalMutex ( cacMutex ); + + if ( envGetConfigParamPtr ( & EPICS_CA_MAX_SEARCH_PERIOD ) ) { + long longStatus = envGetDoubleConfigParam ( + & EPICS_CA_MAX_SEARCH_PERIOD, & this->maxPeriod ); + if ( ! longStatus ) { + if ( this->maxPeriod < maxSearchPeriodLowerLimit ) { + epicsPrintf ( "\"%s\" out of range (low)\n", + EPICS_CA_MAX_SEARCH_PERIOD.name ); + this->maxPeriod = maxSearchPeriodLowerLimit; + epicsPrintf ( "Setting \"%s\" = %f seconds\n", + EPICS_CA_MAX_SEARCH_PERIOD.name, this->maxPeriod ); + } + } + else { + epicsPrintf ( "EPICS \"%s\" wasnt a real number\n", + EPICS_CA_MAX_SEARCH_PERIOD.name ); + epicsPrintf ( "Setting \"%s\" = %f seconds\n", + EPICS_CA_MAX_SEARCH_PERIOD.name, this->maxPeriod ); + } + } + + double powerOfTwo = log ( this->maxPeriod / minRoundTripEstimate ) / log ( 2.0 ); + this->nTimers = static_cast < unsigned > ( powerOfTwo + 1.0 ); + if ( this->nTimers > channelNode::getMaxSearchTimerCount () ) { + this->nTimers = channelNode::getMaxSearchTimerCount (); + epicsPrintf ( "\"%s\" out of range (high)\n", + EPICS_CA_MAX_SEARCH_PERIOD.name ); + epicsPrintf ( "Setting \"%s\" = %f seconds\n", + EPICS_CA_MAX_SEARCH_PERIOD.name, + (1<<(this->nTimers-1)) * minRoundTripEstimate ); + } + + powerOfTwo = log ( beaconAnomalySearchPeriod / minRoundTripEstimate ) / log ( 2.0 ); + this->beaconAnomalyTimerIndex = static_cast < unsigned > ( powerOfTwo + 1.0 ); + if ( this->beaconAnomalyTimerIndex >= this->nTimers ) { + this->beaconAnomalyTimerIndex = this->nTimers - 1; + } + + this->ppSearchTmr.reset ( new epics_auto_ptr < class searchTimer > [ this->nTimers ] ); + for ( unsigned i = 0; i < this->nTimers; i++ ) { + this->ppSearchTmr[i].reset ( + new searchTimer ( *this, timerQueue, i, cacMutexIn, + i > this->beaconAnomalyTimerIndex ) ); + } + + this->repeaterPort = + envGetInetPortConfigParam ( &EPICS_CA_REPEATER_PORT, + static_cast (CA_REPEATER_PORT) ); + + this->sock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( this->sock == INVALID_SOCKET ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("CAC: unable to create datagram socket because = \"%s\"\n", + sockErrBuf ); + throwWithLocation ( noSocket () ); + } + + int boolValue = true; + int status = setsockopt ( this->sock, SOL_SOCKET, SO_BROADCAST, + (char *) &boolValue, sizeof ( boolValue ) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("CAC: IP broadcasting enable failed because = \"%s\"\n", + sockErrBuf ); + } + +#if 0 + { + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made joh 11-10-98 + * + * bump up the UDP recv buffer + */ + int size = 1u<<15u; + status = setsockopt ( this->sock, SOL_SOCKET, SO_RCVBUF, + (char *)&size, sizeof (size) ); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: unable to set socket option SO_RCVBUF because \"%s\"\n", + sockErrBuf ); + } + } +#endif + + // force a bind to an unconstrained address so we can obtain + // the local port number below + static const unsigned short PORT_ANY = 0u; + osiSockAddr addr; + memset ( (char *)&addr, 0 , sizeof (addr) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); + addr.ia.sin_port = htons ( PORT_ANY ); // X aCC 818 + status = bind (this->sock, &addr.sa, sizeof (addr) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy (this->sock); + errlogPrintf ( "CAC: unable to bind to an unconstrained address because = \"%s\"\n", + sockErrBuf ); + throwWithLocation ( noSocket () ); + } + + { + osiSockAddr tmpAddr; + osiSocklen_t saddr_length = sizeof ( tmpAddr ); + status = getsockname ( this->sock, &tmpAddr.sa, &saddr_length ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsSocketDestroy ( this->sock ); + errlogPrintf ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf ); + throwWithLocation ( noSocket () ); + } + if ( tmpAddr.sa.sa_family != AF_INET) { + epicsSocketDestroy ( this->sock ); + errlogPrintf ( "CAC: UDP socket was not inet addr family\n" ); + throwWithLocation ( noSocket () ); + } + this->localPort = ntohs ( tmpAddr.ia.sin_port ); + } + + /* + * load user and auto configured + * broadcast address list + */ + ELLLIST dest; + ellInit ( & dest ); + configureChannelAccessAddressList ( & dest, this->sock, this->serverPort ); + while ( osiSockAddrNode * + pNode = reinterpret_cast < osiSockAddrNode * > ( ellGet ( & dest ) ) ) { + SearchDestUDP & searchDest = * + new SearchDestUDP ( pNode->addr, *this ); + _searchDestList.add ( searchDest ); + free ( pNode ); + } + + /* add list of tcp name service addresses */ + _searchDestList.add ( searchDestListIn ); + + caStartRepeaterIfNotInstalled ( this->repeaterPort ); + + this->pushVersionMsg (); + + // start timers and receive thread + for ( unsigned j =0; j < this->nTimers; j++ ) { + this->ppSearchTmr[j]->start ( cacGuard ); + } + this->govTmr.start (); + this->repeaterSubscribeTmr.start (); + this->recvThread.start (); +} + +/* + * udpiiu::~udpiiu () + */ +udpiiu::~udpiiu () +{ + { + epicsGuard < epicsMutex > cbGuard ( this->cbMutex ); + epicsGuard < epicsMutex > guard ( this->cacMutex ); + this->shutdown ( cbGuard, guard ); + } + + tsDLIter < SearchDest > iter ( _searchDestList.firstIter () ); + while ( iter.valid () ) + { + SearchDest & curr ( *iter ); + iter++; + delete & curr; + } + + epicsSocketDestroy ( this->sock ); +} + +void udpiiu::shutdown ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ) +{ + // stop all of the timers + this->repeaterSubscribeTmr.shutdown ( cbGuard, guard ); + this->govTmr.shutdown ( cbGuard, guard ); + for ( unsigned i =0; i < this->nTimers; i++ ) { + this->ppSearchTmr[i]->shutdown ( cbGuard, guard ); + } + + { + this->shutdownCmd = true; + epicsGuardRelease < epicsMutex > unguard ( guard ); + { + epicsGuardRelease < epicsMutex > cbUnguard ( cbGuard ); + + if ( ! this->recvThread.exitWait ( 0.0 ) ) { + unsigned tries = 0u; + + this->wakeupMsg (); + + // wait for recv threads to exit + double shutdownDelay = 1.0; + while ( ! this->recvThread.exitWait ( shutdownDelay ) ) { + this->wakeupMsg (); + if ( shutdownDelay < 16.0 ) { + shutdownDelay += shutdownDelay; + } + if ( ++tries > 3 ) { + fprintf ( stderr, "cac: timing out waiting for UDP thread shutdown\n" ); + } + } + } + } + } +} + +udpRecvThread::udpRecvThread ( + udpiiu & iiuIn, cacContextNotify & ctxNotifyIn, epicsMutex & cbMutexIn, + const char * pName, unsigned stackSize, unsigned priority ) : + iiu ( iiuIn ), cbMutex ( cbMutexIn ), ctxNotify ( ctxNotifyIn ), + thread ( *this, pName, stackSize, priority ) {} + +udpRecvThread::~udpRecvThread () +{ +} + +void udpRecvThread::start () +{ + this->thread.start (); +} + +bool udpRecvThread::exitWait ( double delay ) +{ + return this->thread.exitWait ( delay ); +} + +void udpRecvThread::show ( unsigned /* level */ ) const +{ +} + +void udpRecvThread::run () +{ + epicsThreadPrivateSet ( caClientCallbackThreadId, &this->iiu ); + + if ( this->iiu._searchDestList.count () == 0 ) { + callbackManager mgr ( this->ctxNotify, this->cbMutex ); + epicsGuard < epicsMutex > guard ( this->iiu.cacMutex ); + genLocalExcep ( mgr.cbGuard, guard, + this->iiu.cacRef, ECA_NOSEARCHADDR, NULL ); + } + + do { + osiSockAddr src; + osiSocklen_t src_size = sizeof ( src ); + int status = recvfrom ( this->iiu.sock, + this->iiu.recvBuf, sizeof ( this->iiu.recvBuf ), 0, + & src.sa, & src_size ); + + if ( status <= 0 ) { + + if ( status < 0 ) { + int errnoCpy = SOCKERRNO; + if ( + errnoCpy != SOCK_EINTR && + errnoCpy != SOCK_SHUTDOWN && + errnoCpy != SOCK_ENOTSOCK && + errnoCpy != SOCK_EBADF && + // Avoid spurious ECONNREFUSED bug in linux + errnoCpy != SOCK_ECONNREFUSED && + // Avoid ECONNRESET from disconnected socket bug + // in windows + errnoCpy != SOCK_ECONNRESET ) { + + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAC: UDP recv error was \"%s\"\n", + sockErrBuf ); + } + } + } + else if ( status > 0 ) { + this->iiu.postMsg ( src, this->iiu.recvBuf, + (arrayElementCount) status, epicsTime::getCurrent() ); + } + + } while ( ! this->iiu.shutdownCmd ); +} + +/* + * udpiiu::repeaterRegistrationMessage () + * + * register with the repeater + */ +void udpiiu::repeaterRegistrationMessage ( unsigned attemptNumber ) +{ + epicsGuard < epicsMutex > cbGuard ( this->cacMutex ); + caRepeaterRegistrationMessage ( this->sock, this->repeaterPort, attemptNumber ); +} + +/* + * caRepeaterRegistrationMessage () + * + * register with the repeater + */ +void epicsShareAPI caRepeaterRegistrationMessage ( + SOCKET sock, unsigned repeaterPort, unsigned attemptNumber ) +{ + osiSockAddr saddr; + caHdr msg; + int status; + int len; + + assert ( repeaterPort <= USHRT_MAX ); + unsigned short port = static_cast ( repeaterPort ); + + /* + * In 3.13 beta 11 and before the CA repeater calls local_addr() + * to determine a local address and does not allow registration + * messages originating from other addresses. In these + * releases local_addr() returned the address of the first enabled + * interface found, and this address may or may not have been the loop + * back address. Starting with 3.13 beta 12 local_addr() was + * changed to always return the address of the first enabled + * non-loopback interface because a valid non-loopback local + * address is required in the beacon messages. Therefore, to + * guarantee compatibility with past versions of the repeater + * we alternate between the address returned by local_addr() + * and the loopback address here. + * + * CA repeaters in R3.13 beta 12 and higher allow + * either the loopback address or the address returned + * by local address (the first non-loopback address found) + */ + if ( attemptNumber & 1 ) { + saddr = osiLocalAddr ( sock ); + if ( saddr.sa.sa_family != AF_INET ) { + /* + * use the loop back address to communicate with the CA repeater + * if this os does not have interface query capabilities + * + * this will only work with 3.13 beta 12 CA repeaters or later + */ + saddr.ia.sin_family = AF_INET; + saddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); + saddr.ia.sin_port = htons ( port ); + } + else { + saddr.ia.sin_port = htons ( port ); + } + } + else { + saddr.ia.sin_family = AF_INET; + saddr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); + saddr.ia.sin_port = htons ( port ); + } + + memset ( (char *) &msg, 0, sizeof (msg) ); + AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = REPEATER_REGISTER; // X aCC 818 + msg.m_available = saddr.ia.sin_addr.s_addr; + + /* + * Intentionally sending a zero length message here + * until most CA repeater daemons have been restarted + * (and only then will they accept the above protocol) + * (repeaters began accepting this protocol + * starting with EPICS 3.12) + */ +# if defined ( DOES_NOT_ACCEPT_ZERO_LENGTH_UDP ) + len = sizeof (msg); +# else + len = 0; +# endif + + status = sendto ( sock, (char *) &msg, len, 0, + &saddr.sa, sizeof ( saddr ) ); + if ( status < 0 ) { + int errnoCpy = SOCKERRNO; + /* + * Different OS return different codes when the repeater isnt running. + * Its ok to supress these messages because I print another warning message + * if we time out registerring with the repeater. + * + * Linux returns SOCK_ECONNREFUSED + * Windows 2000 returns SOCK_ECONNRESET + */ + if ( errnoCpy != SOCK_EINTR && + errnoCpy != SOCK_ECONNREFUSED && + errnoCpy != SOCK_ECONNRESET ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "error sending registration message to CA repeater daemon was \"%s\"\n", + sockErrBuf ); + } + } +} + +/* + * caStartRepeaterIfNotInstalled () + * + * Test for the repeater already installed + * + * NOTE: potential race condition here can result + * in two copies of the repeater being spawned + * however the repeater detects this, prints a message, + * and lets the other task start the repeater. + * + * QUESTION: is there a better way to test for a port in use? + * ANSWER: none that I can find. + * + * Problems with checking for the repeater installed + * by attempting to bind a socket to its address + * and port. + * + * 1) Closed socket may not release the bound port + * before the repeater wakes up and tries to grab it. + * Attempting to bind the open socket to another port + * also does not work. + * + * 072392 - problem solved by using SO_REUSEADDR + */ +void epicsShareAPI caStartRepeaterIfNotInstalled ( unsigned repeaterPort ) +{ + bool installed = false; + int status; + SOCKET tmpSock; + union { + struct sockaddr_in ia; + struct sockaddr sa; + } bd; + + if ( repeaterPort > 0xffff ) { + fprintf ( stderr, "caStartRepeaterIfNotInstalled () : strange repeater port specified\n" ); + return; + } + + tmpSock = epicsSocketCreate ( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if ( tmpSock != INVALID_SOCKET ) { + ca_uint16_t port = static_cast < ca_uint16_t > ( repeaterPort ); + memset ( (char *) &bd, 0, sizeof ( bd ) ); + bd.ia.sin_family = AF_INET; + bd.ia.sin_addr.s_addr = htonl ( INADDR_ANY ); + bd.ia.sin_port = htons ( port ); + status = bind ( tmpSock, &bd.sa, sizeof ( bd ) ); + if ( status < 0 ) { + if ( SOCKERRNO == SOCK_EADDRINUSE ) { + installed = true; + } + else { + fprintf ( stderr, "caStartRepeaterIfNotInstalled () : bind failed\n" ); + } + } + } + + /* + * turn on reuse only after the test so that + * this works on kernels that support multicast + */ + epicsSocketEnableAddressReuseDuringTimeWaitState ( tmpSock ); + + epicsSocketDestroy ( tmpSock ); + + if ( ! installed ) { + + /* + * This is not called if the repeater is known to be + * already running. (in the event of a race condition + * the 2nd repeater exits when unable to attach to the + * repeater's port) + */ + osiSpawnDetachedProcessReturn osptr = + osiSpawnDetachedProcess ( "CA Repeater", "caRepeater" ); + if ( osptr == osiSpawnDetachedProcessNoSupport ) { + epicsThreadId tid; + + tid = epicsThreadCreate ( "CAC-repeater", epicsThreadPriorityLow, + epicsThreadGetStackSize ( epicsThreadStackMedium ), caRepeaterThread, 0); + if ( tid == 0 ) { + fprintf ( stderr, "caStartRepeaterIfNotInstalled : unable to create CA repeater daemon thread\n" ); + } + } + else if ( osptr == osiSpawnDetachedProcessFail ) { + fprintf ( stderr, "caStartRepeaterIfNotInstalled (): unable to start CA repeater daemon detached process\n" ); + } + } +} + +bool udpiiu::badUDPRespAction ( + const caHdr &msg, const osiSockAddr &netAddr, const epicsTime ¤tTime ) +{ + char buf[64]; + sockAddrToDottedIP ( &netAddr.sa, buf, sizeof ( buf ) ); + char date[64]; + currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); + errlogPrintf ( "CAC: Undecipherable ( bad msg code %u ) UDP message from %s at %s\n", + msg.m_cmmd, buf, date ); + return false; +} + +bool udpiiu::versionAction ( + const caHdr & hdr, const osiSockAddr &, const epicsTime & /* currentTime */ ) +{ + epicsGuard < epicsMutex > guard ( this->cacMutex ); + + // update the round trip time estimate + if ( hdr.m_dataType & sequenceNoIsValid ) { + this->lastReceivedSeqNo = hdr.m_cid; + this->lastReceivedSeqNoIsValid = true; + } + + return true; +} + +bool udpiiu :: searchRespAction ( + const caHdr & msg, const osiSockAddr & addr, + const epicsTime & currentTime ) +{ + /* + * we dont currently know what to do with channel's + * found to be at non-IP type addresses + */ + if ( addr.sa.sa_family != AF_INET ) { + return true; + } + + /* + * Starting with CA V4.1 the minor version number + * is appended to the end of each UDP search reply. + * This value is ignored by earlier clients. + */ + ca_uint32_t minorVersion; + if ( msg.m_postsize >= sizeof ( minorVersion ) ){ + /* + * care is taken here not to break gcc 3.2 aggressive alias + * analysis rules + */ + const ca_uint8_t * pPayLoad = + reinterpret_cast < const ca_uint8_t *> ( & msg + 1 ); + unsigned byte0 = pPayLoad[0]; + unsigned byte1 = pPayLoad[1]; + minorVersion = ( byte0 << 8u ) | byte1; + } + else { + minorVersion = CA_UKN_MINOR_VERSION; + } + + /* + * the type field is abused to carry the port number + * so that we can have multiple servers on one host + */ + osiSockAddr serverAddr; + serverAddr.ia.sin_family = AF_INET; + if ( CA_V48 ( minorVersion ) ) { + if ( msg.m_cid != INADDR_BROADCAST ) { + serverAddr.ia.sin_addr.s_addr = htonl ( msg.m_cid ); + } + else { + serverAddr.ia.sin_addr = addr.ia.sin_addr; + } + serverAddr.ia.sin_port = htons ( msg.m_dataType ); + } + else if ( CA_V45 (minorVersion) ) { + serverAddr.ia.sin_port = htons ( msg.m_dataType ); + serverAddr.ia.sin_addr = addr.ia.sin_addr; + } + else { + serverAddr.ia.sin_port = htons ( this->serverPort ); + serverAddr.ia.sin_addr = addr.ia.sin_addr; + } + + if ( CA_V42 ( minorVersion ) ) { + cacRef.transferChanToVirtCircuit + ( msg.m_available, msg.m_cid, 0xffff, + 0, minorVersion, serverAddr, currentTime ); + } + else { + cacRef.transferChanToVirtCircuit + ( msg.m_available, msg.m_cid, msg.m_dataType, + msg.m_count, minorVersion, serverAddr, currentTime ); + } + + return true; +} + +bool udpiiu::beaconAction ( + const caHdr & msg, + const osiSockAddr & net_addr, const epicsTime & currentTime ) +{ + struct sockaddr_in ina; + + memset(&ina, 0, sizeof(struct sockaddr_in)); + + if ( net_addr.sa.sa_family != AF_INET ) { + return false; + } + + /* + * this allows a fan-out server to potentially + * insert the true address of the CA server + * + * old servers: + * 1) set this field to one of the ip addresses of the host _or_ + * 2) set this field to INADDR_ANY + * new servers: + * always set this field to INADDR_ANY + * + * clients always assume that if this + * field is set to something that isnt INADDR_ANY + * then it is the overriding IP address of the server. + */ + ina.sin_family = AF_INET; + ina.sin_addr.s_addr = htonl ( msg.m_available ); + if ( msg.m_count != 0 ) { + ina.sin_port = htons ( msg.m_count ); + } + else { + /* + * old servers dont supply this and the + * default port must be assumed + */ + ina.sin_port = htons ( this->serverPort ); + } + unsigned protocolRevision = msg.m_dataType; + ca_uint32_t beaconNumber = msg.m_cid; + + this->cacRef.beaconNotify ( ina, currentTime, + beaconNumber, protocolRevision ); + + return true; +} + +bool udpiiu::repeaterAckAction ( + const caHdr &, + const osiSockAddr &, const epicsTime &) +{ + this->repeaterSubscribeTmr.confirmNotify (); + return true; +} + +bool udpiiu::notHereRespAction ( + const caHdr &, + const osiSockAddr &, const epicsTime & ) +{ + return true; +} + +bool udpiiu::exceptionRespAction ( + const caHdr &msg, + const osiSockAddr & net_addr, const epicsTime & currentTime ) +{ + const caHdr &reqMsg = * ( &msg + 1 ); + char name[64]; + sockAddrToDottedIP ( &net_addr.sa, name, sizeof ( name ) ); + char date[64]; + currentTime.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S"); + + if ( msg.m_postsize > sizeof ( caHdr ) ){ + errlogPrintf ( + "error condition \"%s\" detected by %s with context \"%s\" at %s\n", + ca_message ( msg.m_available ), + name, reinterpret_cast ( &reqMsg + 1 ), date ); + } + else{ + errlogPrintf ( + "error condition \"%s\" detected by %s at %s\n", + ca_message ( msg.m_available ), name, date ); + } + + return true; +} + +void udpiiu::postMsg ( + const osiSockAddr & net_addr, + char * pInBuf, arrayElementCount blockSize, + const epicsTime & currentTime ) +{ + caHdr *pCurMsg; + + this->lastReceivedSeqNoIsValid = false; + this->lastReceivedSeqNo = 0u; + + while ( blockSize ) { + arrayElementCount size; + + if ( blockSize < sizeof ( *pCurMsg ) ) { + char buf[64]; + sockAddrToDottedIP ( &net_addr.sa, buf, sizeof ( buf ) ); + errlogPrintf ( + "%s: Undecipherable (too small) UDP msg from %s ignored\n", + __FILE__, buf ); + return; + } + + pCurMsg = reinterpret_cast < caHdr * > ( pInBuf ); + + /* + * fix endian of bytes + */ + pCurMsg->m_postsize = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_postsize ); + pCurMsg->m_cmmd = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_cmmd ); + pCurMsg->m_dataType = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_dataType ); + pCurMsg->m_count = AlignedWireRef < epicsUInt16 > ( pCurMsg->m_count ); + pCurMsg->m_available = AlignedWireRef < epicsUInt32 > ( pCurMsg->m_available ); + pCurMsg->m_cid = AlignedWireRef < epicsUInt32 > ( pCurMsg->m_cid ); + +#if 0 + printf ( "UDP Cmd=%3d Type=%3d Count=%4d Size=%4d", + pCurMsg->m_cmmd, + pCurMsg->m_dataType, + pCurMsg->m_count, + pCurMsg->m_postsize ); + printf (" Avail=%8x Cid=%6d\n", + pCurMsg->m_available, + pCurMsg->m_cid ); +#endif + + size = pCurMsg->m_postsize + sizeof ( *pCurMsg ); + + /* + * dont allow msg body extending beyond frame boundary + */ + if ( size > blockSize ) { + char buf[64]; + sockAddrToDottedIP ( &net_addr.sa, buf, sizeof ( buf ) ); + errlogPrintf ( + "%s: Undecipherable (payload too small) UDP msg from %s ignored\n", + __FILE__, buf ); + return; + } + + /* + * execute the response message + */ + pProtoStubUDP pStub; + if ( pCurMsg->m_cmmd < NELEMENTS ( udpJumpTableCAC ) ) { + pStub = udpJumpTableCAC [pCurMsg->m_cmmd]; + } + else { + pStub = &udpiiu::badUDPRespAction; + } + bool success = ( this->*pStub ) ( *pCurMsg, net_addr, currentTime ); + if ( ! success ) { + char buf[256]; + sockAddrToDottedIP ( &net_addr.sa, buf, sizeof ( buf ) ); + errlogPrintf ( "CAC: Undecipherable UDP message from %s\n", buf ); + return; + } + + blockSize -= size; + pInBuf += size;; + } +} + +bool udpiiu::pushVersionMsg () +{ + epicsGuard < epicsMutex > guard ( this->cacMutex ); + + this->sequenceNumber++; + + caHdr msg; + AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = CA_PROTO_VERSION; + AlignedWireRef < epicsUInt32 > ( msg.m_available ) = 0; + AlignedWireRef < epicsUInt16 > ( msg.m_dataType ) = sequenceNoIsValid; + AlignedWireRef < epicsUInt16 > ( msg.m_count ) = CA_MINOR_PROTOCOL_REVISION; + AlignedWireRef < epicsUInt32 > ( msg.m_cid ) = this->sequenceNumber; // sequence number + + return this->pushDatagramMsg ( guard, msg, 0, 0 ); +} + +bool udpiiu::pushDatagramMsg ( epicsGuard < epicsMutex > & guard, + const caHdr & msg, const void * pExt, ca_uint16_t extsize ) +{ + guard.assertIdenticalMutex ( this->cacMutex ); + + ca_uint16_t alignedExtSize = static_cast (CA_MESSAGE_ALIGN ( extsize )); + arrayElementCount msgsize = sizeof ( caHdr ) + alignedExtSize; + + /* fail out if max message size exceeded */ + if ( msgsize >= sizeof ( this->xmitBuf ) - 7 ) { + return false; + } + + if ( msgsize + this->nBytesInXmitBuf > sizeof ( this->xmitBuf ) ) { + return false; + } + + caHdr * pbufmsg = ( caHdr * ) &this->xmitBuf[this->nBytesInXmitBuf]; + *pbufmsg = msg; + memcpy ( pbufmsg + 1, pExt, extsize ); + if ( extsize != alignedExtSize ) { + char *pDest = (char *) ( pbufmsg + 1 ); + memset ( pDest + extsize, '\0', alignedExtSize - extsize ); + } + AlignedWireRef < epicsUInt16 > ( pbufmsg->m_postsize ) = alignedExtSize; + this->nBytesInXmitBuf += msgsize; + + return true; +} + +udpiiu :: SearchDestUDP :: SearchDestUDP ( + const osiSockAddr & destAddr, udpiiu & udpiiuIn ) : + _destAddr ( destAddr ), _udpiiu ( udpiiuIn ) +{ +} + +void udpiiu :: SearchDestUDP :: searchRequest ( + epicsGuard < epicsMutex > & guard, const char * pBuf, size_t bufSize ) +{ + guard.assertIdenticalMutex ( _udpiiu.cacMutex ); + assert ( bufSize <= INT_MAX ); + int bufSizeAsInt = static_cast < int > ( bufSize ); + while ( true ) { + // This const_cast is needed for vxWorks: + int status = sendto ( _udpiiu.sock, const_cast(pBuf), bufSizeAsInt, 0, + & _destAddr.sa, sizeof ( _destAddr.sa ) ); + if ( status == bufSizeAsInt ) { + break; + } + if ( status >= 0 ) { + errlogPrintf ( "CAC: UDP sendto () call returned strange xmit count?\n" ); + break; + } + else { + int localErrno = SOCKERRNO; + + if ( localErrno == SOCK_EINTR ) { + if ( _udpiiu.shutdownCmd ) { + break; + } + else { + continue; + } + } + else if ( localErrno == SOCK_SHUTDOWN ) { + break; + } + else if ( localErrno == SOCK_ENOTSOCK ) { + break; + } + else if ( localErrno == SOCK_EBADF ) { + break; + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + char buf[64]; + sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) ); + errlogPrintf ( + "CAC: error = \"%s\" sending UDP msg to %s\n", + sockErrBuf, buf); + break; + } + } + } +} + +void udpiiu :: SearchDestUDP :: show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( _udpiiu.cacMutex ); + char buf[64]; + sockAddrToDottedIP ( &_destAddr.sa, buf, sizeof ( buf ) ); + :: printf ( "UDP Search destination \"%s\"\n", buf ); +} + +udpiiu :: SearchRespCallback :: SearchRespCallback ( udpiiu & udpiiuIn ) : + _udpiiu ( udpiiuIn ) +{ +} + +void udpiiu :: SearchRespCallback :: notify ( + const caHdr & msg, const void * pPayloadUntyped, + const osiSockAddr & addr, const epicsTime & currentTime ) +{ + /* + * we dont currently know what to do with channel's + * found to be at non-IP type addresses + */ + if ( addr.sa.sa_family != AF_INET ) { + return; + } + + /* + * Starting with CA V4.1 the minor version number + * is appended to the end of each search reply. + * This value is ignored by earlier clients. + */ + ca_uint32_t minorVersion; + if ( msg.m_postsize >= sizeof ( minorVersion ) ){ + /* + * care is taken here not to break gcc 3.2 aggressive alias + * analysis rules + */ + const ca_uint8_t * pPayLoad = reinterpret_cast < const ca_uint8_t *> ( pPayloadUntyped ); + unsigned byte0 = pPayLoad[0]; + unsigned byte1 = pPayLoad[1]; + minorVersion = ( byte0 << 8u ) | byte1; + } + else { + minorVersion = CA_UKN_MINOR_VERSION; + } + + /* + * the type field is abused to carry the port number + * so that we can have multiple servers on one host + */ + osiSockAddr serverAddr; + serverAddr.ia.sin_family = AF_INET; + if ( CA_V48 ( minorVersion ) ) { + if ( msg.m_cid != INADDR_BROADCAST ) { + serverAddr.ia.sin_addr.s_addr = htonl ( msg.m_cid ); + } + else { + serverAddr.ia.sin_addr = addr.ia.sin_addr; + } + serverAddr.ia.sin_port = htons ( msg.m_dataType ); + } + else if ( CA_V45 (minorVersion) ) { + serverAddr.ia.sin_port = htons ( msg.m_dataType ); + serverAddr.ia.sin_addr = addr.ia.sin_addr; + } + else { + serverAddr.ia.sin_port = htons ( _udpiiu.serverPort ); + serverAddr.ia.sin_addr = addr.ia.sin_addr; + } + + if ( CA_V42 ( minorVersion ) ) { + _udpiiu.cacRef.transferChanToVirtCircuit + ( msg.m_available, msg.m_cid, 0xffff, + 0, minorVersion, serverAddr, currentTime ); + } + else { + _udpiiu.cacRef.transferChanToVirtCircuit + ( msg.m_available, msg.m_cid, msg.m_dataType, + msg.m_count, minorVersion, serverAddr, currentTime ); + } +} + +void udpiiu :: SearchRespCallback :: show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( _udpiiu.cacMutex ); + ::printf ( "udpiiu :: SearchRespCallback\n" ); +} + +bool udpiiu :: datagramFlush ( + epicsGuard < epicsMutex > & guard, const epicsTime & currentTime ) +{ + guard.assertIdenticalMutex ( cacMutex ); + + // dont send the version header by itself + if ( this->nBytesInXmitBuf <= sizeof ( caHdr ) ) { + return false; + } + + tsDLIter < SearchDest > iter ( _searchDestList.firstIter () ); + while ( iter.valid () ) + { + iter->searchRequest ( guard, this->xmitBuf, this->nBytesInXmitBuf ); + iter++; + } + + this->nBytesInXmitBuf = 0u; + + this->pushVersionMsg (); + + return true; +} + +void udpiiu :: show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->cacMutex ); + + ::printf ( "Datagram IO circuit (and disconnected channel repository)\n"); + if ( level > 1u ) { + ::printf ("\trepeater port %u\n", this->repeaterPort ); + ::printf ("\tdefault server port %u\n", this->serverPort ); + ::printf ( "Search Destination List with %u items\n", + _searchDestList.count () ); + if ( level > 2u ) { + tsDLIterConst < SearchDest > iter ( + _searchDestList.firstIter () ); + while ( iter.valid () ) + { + iter->show ( guard, level - 2 ); + iter++; + } + } + } + if ( level > 2u ) { + ::printf ("\tsocket identifier %d\n", this->sock ); + ::printf ("\tbytes in xmit buffer %u\n", this->nBytesInXmitBuf ); + ::printf ("\tshut down command bool %u\n", this->shutdownCmd ); + ::printf ( "\trecv thread exit signal:\n" ); + this->recvThread.show ( level - 2u ); + this->repeaterSubscribeTmr.show ( level - 2u ); + this->govTmr.show ( level - 2u ); + } + if ( level > 3u ) { + for ( unsigned i =0; i < this->nTimers; i++ ) { + this->ppSearchTmr[i]->show ( level - 3u ); + } + } +} + +bool udpiiu::wakeupMsg () +{ + caHdr msg; + AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = CA_PROTO_VERSION; + AlignedWireRef < epicsUInt32 > ( msg.m_available ) = 0u; + AlignedWireRef < epicsUInt16 > ( msg.m_dataType ) = 0u; + AlignedWireRef < epicsUInt16 > ( msg.m_count ) = 0u; + AlignedWireRef < epicsUInt32 > ( msg.m_cid ) = 0u; + AlignedWireRef < epicsUInt16 > ( msg.m_postsize ) = 0u; + + osiSockAddr addr; + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); + addr.ia.sin_port = htons ( this->localPort ); + + // send a wakeup msg so the UDP recv thread will exit + int status = sendto ( this->sock, reinterpret_cast < char * > ( &msg ), + sizeof (msg), 0, &addr.sa, sizeof ( addr.sa ) ); + if ( status == sizeof (msg) ) { + return true; + } + return false; +} + +void udpiiu::beaconAnomalyNotify ( + epicsGuard < epicsMutex > & cacGuard ) +{ + for ( unsigned i = this->beaconAnomalyTimerIndex+1u; + i < this->nTimers; i++ ) { + this->ppSearchTmr[i]->moveChannels ( cacGuard, + *this->ppSearchTmr[this->beaconAnomalyTimerIndex] ); + } +} + +void udpiiu::uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > & guard, nciu & chan, + const epicsTime & currentTime ) +{ + channelNode::channelState chanState = + chan.channelNode::listMember; + if ( chanState == channelNode::cs_disconnGov ) { + this->govTmr.uninstallChan ( guard, chan ); + } + else { + this->ppSearchTmr[ chan.getSearchTimerIndex ( guard ) ]-> + uninstallChanDueToSuccessfulSearchResponse ( + guard, chan, this->lastReceivedSeqNo, + this->lastReceivedSeqNoIsValid, currentTime ); + } +} + +void udpiiu::uninstallChan ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + channelNode::channelState chanState = + chan.channelNode::listMember; + if ( chanState == channelNode::cs_disconnGov ) { + this->govTmr.uninstallChan ( guard, chan ); + } + else { + this->ppSearchTmr[ chan.getSearchTimerIndex ( guard ) ]-> + uninstallChan ( guard, chan ); + } +} + +bool udpiiu::searchMsg ( + epicsGuard < epicsMutex > & guard, ca_uint32_t id, + const char * pName, unsigned nameLength ) +{ + caHdr msg; + AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = CA_PROTO_SEARCH; + AlignedWireRef < epicsUInt32 > ( msg.m_available ) = id; + AlignedWireRef < epicsUInt16 > ( msg.m_dataType ) = DONTREPLY; + AlignedWireRef < epicsUInt16 > ( msg.m_count ) = CA_MINOR_PROTOCOL_REVISION; + AlignedWireRef < epicsUInt32 > ( msg.m_cid ) = id; + return this->pushDatagramMsg ( + guard, msg, pName, (ca_uint16_t) nameLength ); +} + +void udpiiu::installNewChannel ( + epicsGuard < epicsMutex > & guard, nciu & chan, netiiu * & piiu ) +{ + piiu = this; + this->ppSearchTmr[0]->installChannel ( guard, chan ); +} + +void udpiiu::installDisconnectedChannel ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + chan.setServerAddressUnknown ( *this, guard ); + this->govTmr.installChan ( guard, chan ); +} + +void udpiiu::noSearchRespNotify ( + epicsGuard < epicsMutex > & guard, nciu & chan, unsigned index ) +{ + const unsigned nTimersMinusOne = this->nTimers - 1; + if ( index < nTimersMinusOne ) { + index++; + } + else { + index = nTimersMinusOne; + } + this->ppSearchTmr[index]->installChannel ( guard, chan ); +} + +void udpiiu::boostChannel ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + this->ppSearchTmr[this->beaconAnomalyTimerIndex]-> + installChannel ( guard, chan ); +} + +void udpiiu::govExpireNotify ( + epicsGuard < epicsMutex > & guard, nciu & chan ) +{ + this->ppSearchTmr[0]->installChannel ( guard, chan ); +} + +int udpiiu :: printFormated ( + epicsGuard < epicsMutex > & cbGuard, + const char * pformat, ... ) +{ + va_list theArgs; + int status; + + va_start ( theArgs, pformat ); + + status = this->cacRef.varArgsPrintFormated ( cbGuard, pformat, theArgs ); + + va_end ( theArgs ); + + return status; +} + +void udpiiu::updateRTTE ( epicsGuard < epicsMutex > & guard, double measured ) +{ + guard.assertIdenticalMutex ( this->cacMutex ); + if ( measured > maxRoundTripEstimate ) { + measured = maxRoundTripEstimate; + } + if ( measured < minRoundTripEstimate ) { + measured = minRoundTripEstimate; + } + double error = measured - this->rtteMean; + this->rtteMean += 0.125 * error; + if ( error < 0.0 ) { + error = - error; + } + this->rtteMeanDev = this->rtteMeanDev + .25 * ( error - this->rtteMeanDev ); +} + +double udpiiu::getRTTE ( epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->cacMutex ); + return this->rtteMean + 4 * this->rtteMeanDev; +} + +unsigned udpiiu::getHostName ( + epicsGuard < epicsMutex > & cacGuard, + char *pBuf, unsigned bufLength ) const throw () +{ + return netiiu::getHostName ( cacGuard, pBuf, bufLength ); +} + +const char * udpiiu::pHostName ( + epicsGuard < epicsMutex > & cacGuard ) const throw () +{ + return netiiu::pHostName ( cacGuard ); +} + +bool udpiiu::ca_v42_ok ( + epicsGuard < epicsMutex > & cacGuard ) const +{ + return netiiu::ca_v42_ok ( cacGuard ); +} + +bool udpiiu::ca_v41_ok ( + epicsGuard < epicsMutex > & cacGuard ) const +{ + return netiiu::ca_v41_ok ( cacGuard ); +} + +void udpiiu::writeRequest ( + epicsGuard < epicsMutex > & guard, + nciu & chan, unsigned type, + arrayElementCount nElem, const void * pValue ) +{ + netiiu::writeRequest ( guard, chan, type, nElem, pValue ); +} + +void udpiiu::writeNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netWriteNotifyIO & io, unsigned type, + arrayElementCount nElem, const void *pValue ) +{ + netiiu::writeNotifyRequest ( guard, chan, io, type, nElem, pValue ); +} + +void udpiiu::readNotifyRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netReadNotifyIO & io, unsigned type, arrayElementCount nElem ) +{ + netiiu::readNotifyRequest ( guard, chan, io, type, nElem ); +} + +void udpiiu::clearChannelRequest ( + epicsGuard < epicsMutex > & guard, + ca_uint32_t sid, ca_uint32_t cid ) +{ + netiiu::clearChannelRequest ( guard, sid, cid ); +} + +void udpiiu::subscriptionRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netSubscription & subscr ) +{ + netiiu::subscriptionRequest ( guard, chan, subscr ); +} + +void udpiiu::subscriptionUpdateRequest ( + epicsGuard < epicsMutex > & guard, nciu & chan, + netSubscription & subscr ) +{ + netiiu::subscriptionUpdateRequest ( + guard, chan, subscr ); +} + +void udpiiu::subscriptionCancelRequest ( + epicsGuard < epicsMutex > & guard, + nciu & chan, netSubscription & subscr ) +{ + netiiu::subscriptionCancelRequest ( guard, chan, subscr ); +} + +void udpiiu::flushRequest ( + epicsGuard < epicsMutex > & guard ) +{ + netiiu::flushRequest ( guard ); +} + +unsigned udpiiu::requestMessageBytesPending ( + epicsGuard < epicsMutex > & guard ) +{ + return netiiu::requestMessageBytesPending ( guard ); +} + +void udpiiu::flush ( + epicsGuard < epicsMutex > & guard ) +{ + netiiu::flush ( guard ); +} + +void udpiiu::requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & guard ) +{ + netiiu::requestRecvProcessPostponedFlush ( guard ); +} + +osiSockAddr udpiiu::getNetworkAddress ( + epicsGuard < epicsMutex > & guard ) const +{ + return netiiu::getNetworkAddress ( guard ); +} + +double udpiiu::receiveWatchdogDelay ( + epicsGuard < epicsMutex > & guard ) const +{ + return netiiu::receiveWatchdogDelay ( guard ); +} + +ca_uint32_t udpiiu::datagramSeqNumber ( + epicsGuard < epicsMutex > & ) const +{ + return this->sequenceNumber; +} + diff --git a/src/ca/udpiiu.h b/src/ca/udpiiu.h new file mode 100644 index 000000000..0f3d1a313 --- /dev/null +++ b/src/ca/udpiiu.h @@ -0,0 +1,300 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef udpiiuh +#define udpiiuh + +#ifdef epicsExportSharedSymbols +# define udpiiuh_accessh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "osiSock.h" +#include "epicsThread.h" +#include "epicsMemory.h" +#include "epicsTime.h" +#include "tsMinMax.h" +#include "tsDLList.h" + +#ifdef udpiiuh_accessh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "netiiu.h" +#include "searchTimer.h" +#include "disconnectGovernorTimer.h" +#include "repeaterSubscribeTimer.h" +#include "SearchDest.h" + +extern "C" void cacRecvThreadUDP ( void *pParam ); + +epicsShareFunc void epicsShareAPI caStartRepeaterIfNotInstalled ( + unsigned repeaterPort ); +epicsShareFunc void epicsShareAPI caRepeaterRegistrationMessage ( + SOCKET sock, unsigned repeaterPort, unsigned attemptNumber ); +extern "C" epicsShareFunc void caRepeaterThread ( + void * pDummy ); +epicsShareFunc void ca_repeater ( void ); + +class cac; +class cacContextNotify; + +class udpRecvThread : + private epicsThreadRunable { +public: + udpRecvThread ( + class udpiiu & iiuIn, cacContextNotify &, epicsMutex &, + const char * pName, unsigned stackSize, unsigned priority ); + virtual ~udpRecvThread (); + void start (); + bool exitWait ( double delay ); + void show ( unsigned level ) const; +private: + class udpiiu & iiu; + epicsMutex & cbMutex; + cacContextNotify & ctxNotify; + epicsThread thread; + void run(); +}; + +static const double minRoundTripEstimate = 32e-3; // seconds +static const double maxRoundTripEstimate = 30; // seconds +static const double maxSearchPeriodDefault = 5.0 * 60.0; // seconds +static const double maxSearchPeriodLowerLimit = 60.0; // seconds +static const double beaconAnomalySearchPeriod = 5.0; // seconds + +class udpiiu : + private netiiu, + private searchTimerNotify, + private disconnectGovernorNotify, + private repeaterTimerNotify { +public: + udpiiu ( + epicsGuard < epicsMutex > & cacGuard, + class epicsTimerQueueActive &, + epicsMutex & callbackControl, + epicsMutex & mutualExclusion, + cacContextNotify &, + class cac &, + unsigned port, + tsDLList < SearchDest > & ); + virtual ~udpiiu (); + void installNewChannel ( + epicsGuard < epicsMutex > &, nciu &, netiiu * & ); + void installDisconnectedChannel ( + epicsGuard < epicsMutex > &, nciu & ); + void beaconAnomalyNotify ( + epicsGuard < epicsMutex > & guard ); + void shutdown ( epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void show ( unsigned level ) const; + + // exceptions + class noSocket {}; + +private: + class SearchDestUDP : + public SearchDest { + public: + SearchDestUDP ( const osiSockAddr &, udpiiu & ); + void searchRequest ( + epicsGuard < epicsMutex > &, const char * pBuf, size_t bufLen ); + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; + private: + osiSockAddr _destAddr; + udpiiu & _udpiiu; + }; + class SearchRespCallback : + public SearchDest :: Callback { + public: + SearchRespCallback ( udpiiu & ); + void notify ( + const caHdr &, const void * pPayload, + const osiSockAddr &, const epicsTime & ); + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; + private: + udpiiu & _udpiiu; + }; + char xmitBuf [MAX_UDP_SEND]; + char recvBuf [MAX_UDP_RECV]; + udpRecvThread recvThread; + repeaterSubscribeTimer repeaterSubscribeTmr; + disconnectGovernorTimer govTmr; + tsDLList < SearchDest > _searchDestList; + double maxPeriod; + double rtteMean; + double rtteMeanDev; + cac & cacRef; + mutable epicsMutex & cbMutex; + mutable epicsMutex & cacMutex; + epics_auto_ptr < epics_auto_ptr < class searchTimer >, eapt_array > ppSearchTmr; + unsigned nBytesInXmitBuf; + unsigned nTimers; + unsigned beaconAnomalyTimerIndex; + ca_uint32_t sequenceNumber; + ca_uint32_t lastReceivedSeqNo; + SOCKET sock; + ca_uint16_t repeaterPort; + ca_uint16_t serverPort; + ca_uint16_t localPort; + bool shutdownCmd; + bool lastReceivedSeqNoIsValid; + + bool wakeupMsg (); + + void postMsg ( + const osiSockAddr & net_addr, + char *pInBuf, arrayElementCount blockSize, + const epicsTime ¤Time ); + + bool pushDatagramMsg ( epicsGuard < epicsMutex > &, + const caHdr & hdr, const void * pExt, + ca_uint16_t extsize); + + typedef bool ( udpiiu::*pProtoStubUDP ) ( + const caHdr &, + const osiSockAddr &, const epicsTime & ); + + // UDP protocol dispatch table + static const pProtoStubUDP udpJumpTableCAC[]; + + // UDP protocol stubs + bool versionAction ( + const caHdr &, + const osiSockAddr &, const epicsTime & ); + bool badUDPRespAction ( + const caHdr &msg, + const osiSockAddr &netAddr, const epicsTime & ); + bool searchRespAction ( + const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool exceptionRespAction ( + const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool beaconAction ( + const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool notHereRespAction ( + const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + bool repeaterAckAction ( + const caHdr &msg, + const osiSockAddr &net_addr, const epicsTime & ); + + // netiiu stubs + unsigned getHostName ( + epicsGuard < epicsMutex > &, char * pBuf, + unsigned bufLength ) const throw (); + const char * pHostName ( + epicsGuard < epicsMutex > & ) const throw (); + bool ca_v41_ok ( + epicsGuard < epicsMutex > & ) const; + bool ca_v42_ok ( + epicsGuard < epicsMutex > & ) const; + unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void flush ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void writeRequest ( + epicsGuard < epicsMutex > &, nciu &, + unsigned type, arrayElementCount nElem, + const void *pValue ); + void writeNotifyRequest ( + epicsGuard < epicsMutex > &, + nciu &, netWriteNotifyIO &, + unsigned type, arrayElementCount nElem, + const void *pValue ); + void readNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, + netReadNotifyIO &, unsigned type, + arrayElementCount nElem ); + void clearChannelRequest ( + epicsGuard < epicsMutex > &, + ca_uint32_t sid, ca_uint32_t cid ); + void subscriptionRequest ( + epicsGuard < epicsMutex > &, + nciu &, netSubscription & ); + void subscriptionUpdateRequest ( + epicsGuard < epicsMutex > &, + nciu &, netSubscription & ); + void subscriptionCancelRequest ( + epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ); + void flushRequest ( + epicsGuard < epicsMutex > & ); + void requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & ); + osiSockAddr getNetworkAddress ( + epicsGuard < epicsMutex > & ) const; + void uninstallChan ( + epicsGuard < epicsMutex > &, nciu & ); + void uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > &, nciu &, + const class epicsTime & currentTime ); + double receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const; + bool searchMsg ( + epicsGuard < epicsMutex > &, ca_uint32_t id, + const char * pName, unsigned nameLength ); + + // searchTimerNotify stubs + double getRTTE ( epicsGuard < epicsMutex > & ) const; + void updateRTTE ( epicsGuard < epicsMutex > &, double rtte ); + bool pushVersionMsg (); + void boostChannel ( + epicsGuard < epicsMutex > & guard, nciu & chan ); + void noSearchRespNotify ( + epicsGuard < epicsMutex > &, nciu & chan, unsigned index ); + bool datagramFlush ( + epicsGuard < epicsMutex > &, const epicsTime & currentTime ); + ca_uint32_t datagramSeqNumber ( + epicsGuard < epicsMutex > & ) const; + + // disconnectGovernorNotify + void govExpireNotify ( + epicsGuard < epicsMutex > &, nciu & ); + + // repeaterTimerNotify + void repeaterRegistrationMessage ( + unsigned attemptNumber ); + + int printFormated ( + epicsGuard < epicsMutex > & callbackControl, + const char * pformat, ... ); + + udpiiu ( const udpiiu & ); + udpiiu & operator = ( const udpiiu & ); + + friend class udpRecvThread; + + // These are needed for the vxWorks 5.5 compiler: + friend class udpiiu::SearchDestUDP; + friend class udpiiu::SearchRespCallback; +}; + +#endif // udpiiuh + diff --git a/src/ca/virtualCircuit.h b/src/ca/virtualCircuit.h new file mode 100644 index 000000000..3d3031f31 --- /dev/null +++ b/src/ca/virtualCircuit.h @@ -0,0 +1,424 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef virtualCircuith +#define virtualCircuith + +#include "epicsMemory.h" +#include "tsDLList.h" +#include "tsMinMax.h" + +#include "comBuf.h" +#include "caServerID.h" +#include "netiiu.h" +#include "comQueSend.h" +#include "comQueRecv.h" +#include "tcpRecvWatchdog.h" +#include "tcpSendWatchdog.h" +#include "hostNameCache.h" +#include "SearchDest.h" +#include "compilerDependencies.h" + +class callbackManager; + +// a modified ca header with capacity for large arrays +struct caHdrLargeArray { + ca_uint32_t m_postsize; // size of message extension + ca_uint32_t m_count; // operation data count + ca_uint32_t m_cid; // channel identifier + ca_uint32_t m_available; // protocol stub dependent + ca_uint16_t m_dataType; // operation data type + ca_uint16_t m_cmmd; // operation to be performed +}; + +class ipAddrToAsciiEngine; + +class tcpRecvThread : private epicsThreadRunable { +public: + tcpRecvThread ( + class tcpiiu & iiuIn, epicsMutex & cbMutexIn, cacContextNotify &, + const char * pName, unsigned int stackSize, unsigned int priority ); + virtual ~tcpRecvThread (); + void start (); + void exitWait (); + bool exitWait ( double delay ); + void interruptSocketRecv (); + void show ( unsigned level ) const; +private: + epicsThread thread; + class tcpiiu & iiu; + epicsMutex & cbMutex; + cacContextNotify & ctxNotify; + void run (); + void connect ( + epicsGuard < epicsMutex > & guard ); + bool validFillStatus ( + epicsGuard < epicsMutex > & guard, + const statusWireIO & stat ); +}; + +class tcpSendThread : private epicsThreadRunable { +public: + tcpSendThread ( + class tcpiiu & iiuIn, const char * pName, + unsigned int stackSize, unsigned int priority ); + virtual ~tcpSendThread (); + void start (); + void exitWait (); + void interruptSocketSend (); + void show ( unsigned level ) const; +private: + epicsThread thread; + class tcpiiu & iiu; + void run (); +}; + +class SearchDestTCP : public SearchDest { +public: + SearchDestTCP ( cac &, const osiSockAddr & ); + void searchRequest ( epicsGuard < epicsMutex > & guard, + const char * pbuf, size_t len ); + void show ( epicsGuard < epicsMutex > & guard, unsigned level ) const; + void setCircuit ( tcpiiu * ); + void disable (); + void enable (); +private: + tcpiiu * _ptcpiiu; + cac & _cac; + const osiSockAddr _addr; + bool _active; +}; + +class tcpiiu : + public netiiu, public tsDLNode < tcpiiu >, + public tsSLNode < tcpiiu >, public caServerID, + private wireSendAdapter, private wireRecvAdapter { + friend void SearchDestTCP::searchRequest ( epicsGuard < epicsMutex > & guard, + const char * pbuf, size_t len ); +public: + tcpiiu ( cac & cac, epicsMutex & mutualExclusion, epicsMutex & callbackControl, + cacContextNotify &, double connectionTimeout, epicsTimerQueue & timerQueue, + const osiSockAddr & addrIn, comBufMemoryManager &, unsigned minorVersion, + ipAddrToAsciiEngine & engineIn, const cacChannel::priLev & priorityIn, + SearchDestTCP * pSearchDestIn = NULL); + ~tcpiiu (); + void start ( + epicsGuard < epicsMutex > & ); + void responsiveCircuitNotify ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void sendTimeoutNotify ( + callbackManager & cbMgr, + epicsGuard < epicsMutex > & guard ); + void receiveTimeoutNotify( + callbackManager &, + epicsGuard < epicsMutex > & ); + void beaconAnomalyNotify ( + epicsGuard < epicsMutex > & ); + void beaconArrivalNotify ( + epicsGuard < epicsMutex > & ); + void probeResponseNotify ( + epicsGuard < epicsMutex > & ); + + void flushRequest ( + epicsGuard < epicsMutex > & ); + unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void flush ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + + void show ( unsigned level ) const; + bool setEchoRequestPending ( + epicsGuard < epicsMutex > & ); + void requestRecvProcessPostponedFlush ( + epicsGuard < epicsMutex > & ); + void clearChannelRequest ( + epicsGuard < epicsMutex > &, + ca_uint32_t sid, ca_uint32_t cid ); + + bool ca_v41_ok ( + epicsGuard < epicsMutex > & ) const; + bool ca_v42_ok ( + epicsGuard < epicsMutex > & ) const; + bool ca_v44_ok ( + epicsGuard < epicsMutex > & ) const; + bool ca_v49_ok ( + epicsGuard < epicsMutex > & ) const; + + unsigned getHostName ( + epicsGuard < epicsMutex > &, + char *pBuf, unsigned bufLength ) const throw (); + bool alive ( + epicsGuard < epicsMutex > & ) const; + bool connecting ( + epicsGuard < epicsMutex > & ) const; + bool receiveThreadIsBusy ( + epicsGuard < epicsMutex > & ); + osiSockAddr getNetworkAddress ( + epicsGuard < epicsMutex > & ) const; + int printFormated ( + epicsGuard < epicsMutex > & cbGuard, + const char *pformat, ... ); + unsigned channelCount ( + epicsGuard < epicsMutex > & ); + void disconnectAllChannels ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard, class udpiiu & ); + void unlinkAllChannels ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void installChannel ( + epicsGuard < epicsMutex > &, nciu & chan, + unsigned sidIn, ca_uint16_t typeIn, arrayElementCount countIn ); + void uninstallChan ( + epicsGuard < epicsMutex > & guard, nciu & chan ); + bool connectNotify ( + epicsGuard < epicsMutex > &, nciu & chan ); + + void searchRespNotify ( + const epicsTime &, const caHdrLargeArray & ); + void versionRespNotify ( const caHdrLargeArray & ); + + void * operator new ( size_t size, + tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & )) + +private: + hostNameCache hostNameCacheInstance; + tcpRecvThread recvThread; + tcpSendThread sendThread; + tcpRecvWatchdog recvDog; + tcpSendWatchdog sendDog; + comQueSend sendQue; + comQueRecv recvQue; + // nciu state field tells us which list + // protected by the callback mutex + tsDLList < nciu > createReqPend; + tsDLList < nciu > createRespPend; + tsDLList < nciu > v42ConnCallbackPend; + tsDLList < nciu > subscripReqPend; + tsDLList < nciu > connectedList; + tsDLList < nciu > unrespCircuit; + tsDLList < nciu > subscripUpdateReqPend; + caHdrLargeArray curMsg; + arrayElementCount curDataMax; + arrayElementCount curDataBytes; + comBufMemoryManager & comBufMemMgr; + cac & cacRef; + char * pCurData; + SearchDestTCP * pSearchDest; + epicsMutex & mutex; + epicsMutex & cbMutex; + unsigned minorProtocolVersion; + enum iiu_conn_state { + iiucs_connecting, // pending circuit connect + iiucs_connected, // live circuit + iiucs_clean_shutdown, // live circuit will shutdown when flush completes + iiucs_disconnected, // socket informed us of disconnect + iiucs_abort_shutdown // socket has been closed + } state; + epicsEvent sendThreadFlushEvent; + epicsEvent flushBlockEvent; + SOCKET sock; + unsigned contigRecvMsgCount; + unsigned blockingForFlush; + unsigned socketLibrarySendBufferSize; + unsigned unacknowledgedSendBytes; + unsigned channelCountTot; + bool _receiveThreadIsBusy; + bool busyStateDetected; // only modified by the recv thread + bool flowControlActive; // only modified by the send process thread + bool echoRequestPending; + bool oldMsgHeaderAvailable; + bool msgHeaderAvailable; + bool earlyFlush; + bool recvProcessPostponedFlush; + bool discardingPendingData; + bool socketHasBeenClosed; + bool unresponsiveCircuit; + + bool processIncoming ( + const epicsTime & currentTime, callbackManager & ); + unsigned sendBytes ( const void *pBuf, + unsigned nBytesInBuf, const epicsTime & currentTime ); + void recvBytes ( + void * pBuf, unsigned nBytesInBuf, statusWireIO & ); + const char * pHostName ( + epicsGuard < epicsMutex > & ) const throw (); + double receiveWatchdogDelay ( + epicsGuard < epicsMutex > & ) const; + void unresponsiveCircuitNotify ( + epicsGuard < epicsMutex > & cbGuard, + epicsGuard < epicsMutex > & guard ); + void initiateCleanShutdown ( + epicsGuard < epicsMutex > & ); + void initiateAbortShutdown ( + epicsGuard < epicsMutex > & ); + void disconnectNotify ( + epicsGuard < epicsMutex > & ); + bool bytesArePendingInOS () const; + void decrementBlockingForFlushCount ( + epicsGuard < epicsMutex > & guard ); + bool isNameService () const; + + // send protocol stubs + void echoRequest ( + epicsGuard < epicsMutex > & ); + void versionMessage ( + epicsGuard < epicsMutex > &, const cacChannel::priLev & priority ); + void disableFlowControlRequest ( + epicsGuard < epicsMutex > & ); + void enableFlowControlRequest ( + epicsGuard < epicsMutex > & ); + void hostNameSetRequest ( + epicsGuard < epicsMutex > & ); + void userNameSetRequest ( + epicsGuard < epicsMutex > & ); + void createChannelRequest ( + nciu &, epicsGuard < epicsMutex > & ); + void writeRequest ( + epicsGuard < epicsMutex > &, nciu &, + unsigned type, arrayElementCount nElem, const void *pValue ); + void writeNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, + netWriteNotifyIO &, unsigned type, + arrayElementCount nElem, const void *pValue ); + void readNotifyRequest ( + epicsGuard < epicsMutex > &, nciu &, + netReadNotifyIO &, unsigned type, + arrayElementCount nElem ); + void subscriptionRequest ( + epicsGuard < epicsMutex > &, + nciu &, netSubscription & subscr ); + void subscriptionUpdateRequest ( + epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ); + void subscriptionCancelRequest ( + epicsGuard < epicsMutex > &, + nciu & chan, netSubscription & subscr ); + void flushIfRecvProcessRequested ( + epicsGuard < epicsMutex > & ); + bool sendThreadFlush ( + epicsGuard < epicsMutex > & ); + + // netiiu stubs + void uninstallChanDueToSuccessfulSearchResponse ( + epicsGuard < epicsMutex > &, nciu &, const class epicsTime & ); + bool searchMsg ( + epicsGuard < epicsMutex > &, ca_uint32_t id, + const char * pName, unsigned nameLength ); + + friend class tcpRecvThread; + friend class tcpSendThread; + + tcpiiu ( const tcpiiu & ); + tcpiiu & operator = ( const tcpiiu & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +inline void * tcpiiu::operator new ( size_t size, + tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & mgr ) +{ + return mgr.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void tcpiiu::operator delete ( void * pCadaver, + tsFreeList < class tcpiiu, 32, epicsMutexNOOP > & mgr ) +{ + mgr.release ( pCadaver ); +} +#endif + +inline bool tcpiiu::ca_v41_ok ( + epicsGuard < epicsMutex > & ) const +{ + return CA_V41 ( this->minorProtocolVersion ); +} + +inline bool tcpiiu::ca_v44_ok ( + epicsGuard < epicsMutex > & ) const +{ + return CA_V44 ( this->minorProtocolVersion ); +} + +inline bool tcpiiu::ca_v49_ok ( + epicsGuard < epicsMutex > & ) const +{ + return CA_V49 ( this->minorProtocolVersion ); +} + +inline bool tcpiiu::alive ( + epicsGuard < epicsMutex > & ) const // X aCC 361 +{ + return ( this->state == iiucs_connecting || + this->state == iiucs_connected ); +} + +inline bool tcpiiu::connecting ( + epicsGuard < epicsMutex > & ) const +{ + return ( this->state == iiucs_connecting ); +} + +inline bool tcpiiu::receiveThreadIsBusy ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->_receiveThreadIsBusy; +} + +inline void tcpiiu::beaconAnomalyNotify ( + epicsGuard < epicsMutex > & guard ) +{ + //guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + this->recvDog.beaconAnomalyNotify ( guard ); +} + +inline void tcpiiu::beaconArrivalNotify ( + epicsGuard < epicsMutex > & guard ) +{ + //guard.assertIdenticalMutex ( this->cacRef.mutexRef () ); + this->recvDog.beaconArrivalNotify ( guard ); +} + +inline void tcpiiu::probeResponseNotify ( + epicsGuard < epicsMutex > & cbGuard ) +{ + this->recvDog.probeResponseNotify ( cbGuard ); +} + +inline bool tcpiiu::isNameService () const +{ + return ( this->pSearchDest != NULL ); +} + +inline void SearchDestTCP::setCircuit ( tcpiiu * piiu ) +{ + _ptcpiiu = piiu; +} + +#endif // ifdef virtualCircuith diff --git a/src/cap5/CA.pm b/src/cap5/CA.pm new file mode 100644 index 000000000..3aa7c7f51 --- /dev/null +++ b/src/cap5/CA.pm @@ -0,0 +1,650 @@ +# Bootstrap wrapper for the Perl 5 Channel Access client module. +# This wrapper also contains the POD documentation for the module. + +use strict; +use warnings; + +my $version = '0.4'; + +exists $ENV{EPICS_HOST_ARCH} + or die "EPICS_HOST_ARCH environment variable not set"; + + +package CA; + +our $VERSION = $version; + + +package Cap5; +# This package is required because the loadable library containing the +# Perl interface code shouldn't be called CA but DynaLoader needs the +# package name to match the library name. The loadable library actually +# declares the packages for both Cap5 and CA which is why this works, +# although the only symbols in the Cap5 package are associated with the +# requirements of the DynaLoader module. + +our $VERSION = $version; +our @ISA = qw(DynaLoader); + +require DynaLoader; + +# Add our lib/ directory to the shared library search path +use File::Basename; +my $Lib = dirname(__FILE__); +push @DynaLoader::dl_library_path, "$Lib/../$ENV{EPICS_HOST_ARCH}"; + +bootstrap Cap5 $VERSION; + + +package CA; +# This END block runs the ca_context_destroy function, but we don't +# document it since the user never needs to call it explicitly. + +END { CA->context_destroy; } + + +package CA::Subscription; +# A subscription reference is a distinct object type. This package +# provides a convenience method allowing a subscription to clear itself. + +our $VERSION = $version; + +sub clear { + CA->clear_subscription(shift); +} + +1; +__END__ + +=head1 NAME + +CA - Perl 5 interface to EPICS Channel Access + +=head1 SYNOPSIS + + use lib '/path/to/cap5/lib/perl'; + use CA; + + my $chan = CA->new('pvname'); + CA->pend_io(1); + + my @access = ('no ', ''); + printf " PV name: %s\n", $chan->name; + printf " Data type: %s\n", $chan->field_type; + printf " Element count: %d\n", $chan->element_count; + printf " Host: %s\n", $chan->host_name; + printf " State: %s\n", $chan->state; + printf " Access: %sread, %swrite\n", + $access[$chan->read_access], $access[$chan->write_access]; + + die "PV not found!" unless $chan->is_connected; + + $chan->get; + CA->pend_io(1); + printf " Value: %s\n", $chan->value; + + $chan->create_subscription('v', \&callback, 'DBR_TIME_DOUBLE'); + CA->pend_event(10); + + sub callback { + my ($chan, $status, $data) = @_; + if ($status) { + printf "%-30s %s\n", $chan->name, $status; + } else { + printf " Value: %g\n", $data->{value}; + printf " Severity: %s\n", $data->{severity}; + printf " Timestamp: %d.%09d\n", + $data->{stamp}, $data->{stamp_fraction}; + } + } + + +=head1 DESCRIPTION + +C is an efficient interface to the EPICS Channel Access client library for +use by Perl 5 programs. It provides most of the functionality of the C library +(omitting Synchronous Groups) but only handles the three standard Perl data +types integer (long), floating point (double) and string (now including long +strings). Programmers who understand the C API will very quickly pick up how to +use this library since the calls and concepts are virtually identical. + + +=head1 FUNCTIONS + + +=head2 Constructor + +=over 4 + +=item new( I ) + +=item new( I, I ) + +Create a channel for the named PV. If given, I will be called whenever the +connection state of the channel changes. The arguments passed to I are the +channel object and a scalar value that is true if the channel is now up. + +The underlying CA channel will be cleaned up properly when the channel object is +garbage-collected by Perl. + +=back + + +=head2 Object Methods + +The following methods are provided for channel objects returned by +C<< CA->new() >>. + +=over 4 + + +=item name + +The PV name provided when this channel was created. + + +=item field_type + +Returns the native DBF type of the process variable as a string, or the string +C if unconnected. + + +=item element_count + +The maximum array element count from the server. Zero if the channel is not +connected. + + +=item host_name + +A string containing the server's hostname and port number. If the channel is +disconnected it will report C<< >>. + + +=item read_access + +=item write_access + +A true/false value that indicates whether the client has read or write access to +the specified channel. + + +=item state + +A string giving the current connection state of the channel, one of C, C, C or C. + + +=item is_connected + +Returns C if the channel is currently connected, else C. Use this +in preference to the equivalent code Sstate eq 'connected' >>>. + + +=item get + +=item value + +The C method makes a C request for a single element of the Perl +type closest to the channel's native data type; a C field will be +fetched as a DBF_STRING, and a C array with multiple elements will +converted into a Perl string. Once the server has returned the value (for which +see the C function below) it can be retrieved using the channel's +C method. Note that the C method deliberately only provides limited +capabilities; the C method must be used for more complex +requirements. + + +=item get_callback( I ) + +=item get_callback( I, I ) + +=item get_callback( I, I ) + +=item get_callback( I, I, I ) + +The C method takes a subroutine reference or name and calls that +routine when the server returns the data requested. With no other arguments the +data type requested will be the widened form of the channel's native type +(widening is discussed below), and if the channel is an array the request will +fetch all available elements. + +The element count can be overridden by providing an integer argument in the +range 0 .. C, where zero means use the current length from the +server. Note that the count argument must be an integer; add 0 to it if it is +necessary to convert it from a string. +The optional data type I should be a string naming +the desired C type; the actual type used will have the C part +widened to one of C, C, C or C. The valid type +names are listed in the L under the +section titled Channel Access Data Types; look in the CA Type Code column of the +two tables. + +The callback subroutine will be given three arguments: the channel object, a +status value from the server, and the returned data. If there were no errors +the status value will be C and the data will be valid; if an error +occurred the data will be C and the status a printable string giving more +information. The format of the data is described under L +below. + +Callback subroutines should only call Perl's C, C or similar +functions if they are expecting the program to exit at that time; attempts to +C with an exception object in the callback and catch that using C in +the main thread are not likely to succeed and will probably result in a crash. +Callbacks should not perform any operations that would block for more than a +fraction of a second as this will hold up network communications with the +relevent server and could cause the Perl program and/or the Channel Access +server to crash. Calling C<< CA->pend_event >> from within a callback is not +permitted by the underlying Channel Access library. + + +=item create_subscription( I, I ) + +=item create_subscription( I, I, I ) + +=item create_subscription( I, I, I ) + +=item create_subscription( I, I, I, I ) + +Register a state change subscription and specify a subroutine to be called +whenever the process variable undergoes a significant state change. I +must be a string containing one or more of the letters C, C, C and C

    +which indicate that this subscription is for Value, Log (Archive), Alarm and +Property changes. The subroutine I is called as described for the +C method above, and the same optional I and I +arguments may be supplied to modify the data type and element count requested +from the server. + +The C method returns a C object which is +required to cancel that particular subscription. Either call the C +method on that object directly, or pass it to the C<< CA->clear_subscription >> +class method. + + +=item put( I ) + +=item put( I, I, ... ) + +The C method makes a C or C call depending on the +number of elements given in its argument list. The data type used will be the +native type of the channel, widened to one of C, array of C, +C or C. + + +=item put_callback( I, I ) + +=item put_callback( I, I, I, ... ) + +C is similar to the C method with the addition of the +subroutine reference or name I which is called when the server reports that +all actions resulting from the put have completed. For some applications this +callback can be delayed by minutes, hours or possibly even longer. The data +type is chosen the same way as for C. The arguments to the subroutine will +be the channel object and the status value from the server, which is either +C or a printable string if an error occurred. The same restrictions +apply to the callback subroutine as described in C above. + + +=item put_acks( I ) + +=item put_acks( I, I ) + +Applications that need to ackowledge alarms by doing a C with type +C can do so using the C method. The severity argument +may be provided as an integer from zero through three or as a string containing +one of the corresponding EPICS severity names C, C, C or +C. If a subroutine reference is provided it will be called after the +operation has completed on the server as described in C above. + + +=item put_ackt( I ) + +=item put_ackt( I, I ) + +This method is for applications that need to enable/disable transient alarms by +doing a C with type C. The C argument is a +true/false value, and an optional subroutine reference can be provided as +described above. + + +=item change_connection_event( I ) + +This method replaces, adds or cancels the connection handler subroutine for the +channel; see the C constructor for details. If I is C any +existing handler is removed, otherwise the new subroutine will be used for all +future connection events on this channel. + +=back + + +=head2 Channel Data + +The data provided to a callback function registered with either C +or C can be a scalar value or a reference to an array or a +hash, depending on the data type that was used for the data transfer. If the +request was for a single item of one of the basic data types, the data argument +will be a perl scalar that holds the value directly. If the request was for +multiple items of one of the basic types, the data argument will be a reference +to an array holding the data. There is one exception though; if the data type +requested was for an array of C values that array will be represented +as a single Perl string contining all the characters before the first zero byte. + +If the request was for one of the compound data types, the data argument will be +a reference to a hash with keys as described below. Keys that are not classed +as metadata are named directly after the fields in the C C, +and are only included when the C structure contains that particular field. + + +=head3 Metadata + +These metadata will always be present in the hash: + + +=over 4 + +=item TYPE + +The C name of the data type from the server. This might have been +widened from the original type used to request or subscribe for the data. + + +=item COUNT + +The number of elements in the data returned by the server. If the data type is +C the value given for C is the number of bytes (including any +trailing zeros) returned by the server, although the value field is given as a +Perl string contining all the characters before the first zero byte. + +=back + + +=head3 Fixed Fields + +These fields are always present in the hash: + +=over 4 + + +=item value + +The actual process variable data, expressed as a Perl scalar or a reference to +an array of scalars, depending on the request. An array of C elements +will be represented as a string; to access the array elements as numeric values +the request must be for the C equivalent data type. + +If I is C or C, C can be accessed both +as the integer choice value and (if within range) as the string associated with +that particular choice. + + +=item status + +The alarm status of the PV as a printable string, or C if not in alarm. + + +=item severity + +The alarm severity of the PV, or C if not in alarm. A defined severity +can be used as a human readable string or as a number giving the numeric value +of the alarm severity (1 = C, 2 = C, 3 = C). + +=back + + +=head3 Ephemeral Fields + +These fields are only present for some values of I: + +=over 4 + + +=item strs + +A reference to an array containing all the possible choice strings for an ENUM. + +Present only when I is C or C. + + +=item no_str + +The number of choices defined for an ENUM. + +Present only when I is C or C. + + +=item stamp + +The process variable timestamp, converted to a local C. This value is +suitable for passing to the perl C or C functions. + +Present only when I is C. + +=item stamp_fraction + +The fractional part of the process variable timestamp as a positive floating +point number less than 1.0. + +Present only when I is C. + + +=item ackt + +The value of the process variable's transient acknowledgment flag, an integer. + +Present only when I is C. + + +=item acks + +The alarm severity of the highest unacknowledged alarm for this process +variable. As with the C value, this scalar is both a string and +numeric severity. + +Present only when I is C. + + +=item precision + +The process variable's display precision, an integer giving the number of +decimal places to display. + +Present only when I is C or C. + + +=item units + +The engineering units string for the process variable. + +Present only when I is C or C where C is +not C. + + +=item upper_disp_limit + +=item lower_disp_limit + +The display range for the process variable; graphical tools often provide a way +to override these limits. + +Present only when I is C or C where C is +not C. + + +=item upper_alarm_limit + +=item upper_warning_limit + +=item lower_warning_limit + +=item lower_alarm_limit + +These items give the values at which the process variable should go into an +alarm state, although in practice the alarm severity associated with each level +is not provided. + +Present only when I is C or C where C is +not C. + + +=item upper_ctrl_limit + +=item lower_ctrl_limit + +The range over which a client can control the value of the process variable. + +Present only when I is C where C is not C. + +=back + + +=head2 Class Methods + + +The following functions are not channel methods, and should be called using the +class method syntax, e.g. C<< CA->pend_io(10) >>. + +=over 4 + +=item flush_io + +Flush outstanding IO requests to the server. This routine is useful for users +who need to flush requests prior to performing client side labor in parallel +with labor performed in the server. Outstanding requests are also sent whenever +the buffer which holds them becomes full. Note that the routine can return +before all flush operations have completed. + + +=item test_io + +This function tests to see if all C requests are complete and channels +created without a connection callback subroutine are connected. It will return +a true value if all such operations are complete, otherwise false. + + +=item pend_io( I ) + +This function flushes the send buffer and then blocks until all outstanding +C requests complete and all channels created without a connection callback +subroutine have connected for the first time. Unlike C, this +routine does not process CA's background activities if no IO requests are +pending. + +If any I/O or connection operations remain incomplete after I seconds, +the function will die with the error C; see L +below. A I interval of zero is taken to mean wait forever if +necessary. The I value should take into account worst case network +delays such as Ethernet collision exponential back off until retransmission +delays which can be quite long on overloaded networks. + + +=item pend_event( I ) + +Flush the send buffer and process CA's background activities for I +seconds. This function always blocks for the full I period, and if a +value of zero is used it will never return. + +It is generally advisable to replace any uses of Perl's built-in function +C with calls to this routine, allowing Channel Access to make use of the +delay time to perform any necessary housekeeping operations. + + +=item poll + +Flush the send buffer and process any outstanding CA background activity. + + +=item clear_subscription( I ) + +Cancel a subscription. Note that for this to take effect immediately it is +necessary to call C<< CA->flush_io >> or one of the other class methods that +flushes the send buffer. + + +=item add_exception_event( I ) + +Trap exception events and execute I whenever they occur. The subroutine is +provided with four arguments: The channel object (if applicable), the status +value from the server, a printable context string giving more information about +the error, and a hash reference containing some additional data. If the +exception is not specific to a particular channel the channel object will be +C. The status value is a printable string. The hash may contain any of +the following members: + +=over 8 + +=item * OP + +The operation in progress when the exception occurred. This scalar when used as +a string is one of C, C, C, C, +C or C but can also be accessed as an integer (0-5). + +=item * TYPE + +The C name of the data type involved. + +=item * COUNT + +The number of elements in the request. + +=item * FILE + +=item * LINE + +These refer to the source file and line number inside the CA client library +where the exception was noticed. + +=back + +=item replace_printf_handler( I ) + +This function provides a method to trap error messages from the CA client +library and redirect them to somewhere other than the C stream. The +subroutine provided will be called with a single string argument every time the +client library wishes to output an error or warning message. Note that a single +error or warning message may result in several calls to this subroutine. + +To revert back to the original handler, call C<< CA->replace_printf_handler() >> +passing C as the subroutine reference. + +=back + + +=head1 ERROR HANDLING + +Errors in using the library will be indicated by the module throwing an +exception, i.e. calling C with an appropriate error message. These +exceptions can be caught using the standard Parl C statement and +testing the C<$@> variable afterwards; if not caught, they will cause the +running program to C with an appropriate error message pointing to the +program line that called the C library. + +Errors messages reported by the underlying CA client library all start with the +string C and the remainder of the symbol for the associated CA error +number, and are followed after a space-hyphen-space by a human-readable message +describing the error. Errors that are detected by the perl interface layer do +not follow this pattern, but are still printable strings. + + +=head1 SEE ALSO + +=over + +=item [1] R3.14 Channel Access Reference Manual by Jeffrey O. Hill + +L + +=back + + +=head1 AUTHOR + +Andrew Johnson, Eanj@aps.anl.govE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2008 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut diff --git a/src/cap5/Cap5.xs b/src/cap5/Cap5.xs new file mode 100644 index 000000000..c3e4d0e50 --- /dev/null +++ b/src/cap5/Cap5.xs @@ -0,0 +1,1429 @@ +/* Provides an EPICS Channel Access client interface for Perl5. */ + +/* This macro disables perl's reentr.inc file, which we don't need + * here and just generates unnecessary compiler warnings. */ +#define REENTRINC + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include "cadef.h" +#include "db_access.h" +#include "alarm.h" +#include "alarmString.h" + +typedef union { + dbr_long_t iv; + dbr_double_t nv; + dbr_string_t pv; +} CA_data; + +typedef struct CA_channel { + chid chan; + CA_data data; /* Value storage for CA::get */ + char *sdata; /* String storage for CA::get */ + size_t ssize; /* Length allocated for sdata, excluding nil */ + SV *chan_ref; + SV *conn_sub; + SV *rights_sub; +} CA_channel; + +static +void *p5_ctx; + +static +const char * get_error_msg(int status) { + static const char * const messages[] = { + "ECA_NORMAL - Normal successful completion", + "ECA_MAXIOC - Maximum simultaneous IOC connections exceeded", + "ECA_UKNHOST - Unknown internet host", + "ECA_UKNSERV - Unknown internet service", + "ECA_SOCK - Unable to allocate a new socket", + "ECA_CONN - Unable to connect to internet host or service", + "ECA_ALLOCMEM - Unable to allocate additional dynamic memory", + "ECA_UKNCHAN - Unknown IO channel", + "ECA_UKNFIELD - Record field specified inappropriate for channel specified", + "ECA_TOLARGE - The requested data transfer is greater than available memory or EPICS_CA_MAX_ARRAY_BYTES", + "ECA_TIMEOUT - User specified timeout on IO operation expired", + "ECA_NOSUPPORT - Sorry, that feature is planned but not supported at this time", + "ECA_STRTOBIG - The supplied string is unusually large", + "ECA_DISCONNCHID - The request was ignored because the specified channel is disconnected", + "ECA_BADTYPE - The data type specifed is invalid", + "ECA_CHIDNOTFND - Remote Channel not found", + "ECA_CHIDRETRY - Unable to locate all user specified channels", + "ECA_INTERNAL - Channel Access Internal Failure", + "ECA_DBLCLFAIL - The requested local DB operation failed", + "ECA_GETFAIL - Channel read request failed", + "ECA_PUTFAIL - Channel write request failed", + "ECA_ADDFAIL - Channel subscription request failed", + "ECA_BADCOUNT - Invalid element count requested", + "ECA_BADSTR - Invalid string", + "ECA_DISCONN - Virtual circuit disconnect", + "ECA_DBLCHNL - Identical process variable names on multiple servers", + "ECA_EVDISALLOW - Request inappropriate within subscription (monitor) update callback", + "ECA_BUILDGET - Database value get for that channel failed during channel search", + "ECA_NEEDSFP - Unable to initialize without the vxWorks VX_FP_TASK task option set", + "ECA_OVEVFAIL - Event queue overflow has prevented first pass event after event add", + "ECA_BADMONID - Bad event subscription (monitor) identifier", + "ECA_NEWADDR - Remote channel has new network address", + "ECA_NEWCONN - New or resumed network connection", + "ECA_NOCACTX - Specified task isnt a member of a CA context", + "ECA_DEFUNCT - Attempt to use defunct CA feature failed", + "ECA_EMPTYSTR - The supplied string is empty", + "ECA_NOREPEATER - Unable to spawn the CA repeater thread- auto reconnect will fail", + "ECA_NOCHANMSG - No channel id match for search reply- search reply ignored", + "ECA_DLCKREST - Reseting dead connection- will try to reconnect", + "ECA_SERVBEHIND - Server (IOC) has fallen behind or is not responding- still waiting", + "ECA_NOCAST - No internet interface with broadcast available", + "ECA_BADMASK - Invalid event selection mask", + "ECA_IODONE - IO operations have completed", + "ECA_IOINPROGRESS - IO operations are in progress", + "ECA_BADSYNCGRP - Invalid synchronous group identifier", + "ECA_PUTCBINPROG - Put callback timed out", + "ECA_NORDACCESS - Read access denied", + "ECA_NOWTACCESS - Write access denied", + "ECA_ANACHRONISM - Requested feature is no longer supported", + "ECA_NOSEARCHADDR - Empty PV search address list", + "ECA_NOCONVERT - No reasonable data conversion between client and server types", + "ECA_BADCHID - Invalid channel identifier", + "ECA_BADFUNCPTR - Invalid function pointer", + "ECA_ISATTACHED - Thread is already attached to a client context", + "ECA_UNAVAILINSERV - Not supported by attached service", + "ECA_CHANDESTROY - User destroyed channel", + "ECA_BADPRIORITY - Invalid channel priority", + "ECA_NOTTHREADED - Preemptive callback not enabled - additional threads may not join context", + "ECA_16KARRAYCLIENT - Client's protocol revision does not support transfers exceeding 16k bytes", + "ECA_CONNSEQTMO - Virtual circuit connection sequence aborted", + "ECA_UNRESPTMO - Virtual circuit unresponsive" + }; + + return messages[CA_EXTRACT_MSG_NO(status)]; +} + + +static +chtype best_type(CA_channel *pch) { + switch (ca_field_type(pch->chan)) { + case DBF_STRING: + case DBF_ENUM: + return DBF_STRING; + case DBF_CHAR: + if (ca_element_count(pch->chan) > 1) + return DBF_CHAR; + /* Fall through */ + case DBF_INT: + case DBF_LONG: + return DBF_LONG; + case DBF_FLOAT: + case DBF_DOUBLE: + return DBF_DOUBLE; + } + croak("Unexpected field type %s", + dbf_type_to_text(ca_field_type(pch->chan))); +} + + +static +SV * newSVdbf(chtype type, const void *dbr, int index) { + switch (type) { + char *pc; + size_t len; + + case DBR_STRING: + pc = (char *)dbr + index * MAX_STRING_SIZE; + len = strlen(pc); + return newSVpv(pc, len < MAX_STRING_SIZE ? len : MAX_STRING_SIZE); + case DBR_LONG: + return newSViv(((dbr_long_t *)dbr)[index]); + case DBR_DOUBLE: + return newSVnv(((dbr_double_t *)dbr)[index]); + default: + croak("Unexpected data type %s", dbf_type_to_text(type)); + } +} + + +static +SV * newSValarm(int sevr) { + SV *alarm = &PL_sv_undef; + if (sevr) { + alarm = newSViv(sevr); + sv_setpv(alarm, epicsAlarmSeverityStrings[sevr]); + SvIOK_on(alarm); + } + return alarm; +} + +static +void hashAdd(HV *hash, const char *key, I32 klen, SV *val) { + SV **result = hv_store(hash, key, klen, val, 0); + + if (result == NULL) + SvREFCNT_dec(val); +} + +static +SV * newSVdbr(struct event_handler_args *peha) { + const int is_primitive = dbr_type_is_plain(peha->type) || + (peha->type == DBR_CLASS_NAME); + HV *hash; + SV *val; + chtype value_type; + union db_access_val *u; + + if (dbr_type_is_STRING(peha->type) || + peha->type == DBR_STSACK_STRING || + peha->type == DBR_CLASS_NAME) + value_type = DBR_STRING; + else if (dbr_type_is_CHAR(peha->type)) + value_type = DBR_CHAR; + else if (dbr_type_is_LONG(peha->type)) + value_type = DBR_LONG; + else if (dbr_type_is_DOUBLE(peha->type)) + value_type = DBR_DOUBLE; + else if (dbr_type_is_ENUM(peha->type)) + /* Only seen as DBR_GR_ENUM and DBR_CTRL_ENUM */ + value_type = DBR_ENUM; + else { + croak("Unexpected data type %s", + dbf_type_to_text(peha->type)); + } + + if (is_primitive) { + if (value_type == DBR_CHAR) { + /* Long string => Perl scalar */ + ((char *)peha->dbr) [peha->count - 1] = 0; + return newSVpv(peha->dbr, 0); + } + + if (peha->count != 1) { + /* Array of values => Perl array reference */ + AV *array; + int i; + + array = newAV(); + for (i = 0; i < peha->count; i++) { + av_push(array, newSVdbf(value_type, peha->dbr, i)); + } + return newRV_noinc((SV *)array); + } + + /* Single value => Perl scalar */ + return newSVdbf(value_type, peha->dbr, 0); + } + + /* Compound => Perl hash reference */ + u = (union db_access_val *)peha->dbr; + hash = newHV(); + + /* Add basic meta-data */ + hashAdd(hash, "TYPE", 4, + newSVpv(dbr_type_to_text(peha->type), 0)); + hashAdd(hash, "COUNT", 5, newSViv(peha->count)); + + /* Alarm status and severity are always in the same place */ + if (u->slngval.status) + val = newSVpv(epicsAlarmConditionStrings[u->slngval.status], 0); + else + val = &PL_sv_undef; + hashAdd(hash, "status", 6, val); + hashAdd(hash, "severity", 8, + newSValarm(u->slngval.severity)); + + if (peha->type == DBR_GR_ENUM || + peha->type == DBR_CTRL_ENUM) { + AV *strings = newAV(); + int n = u->genmval.no_str; + int i; + + val = newSViv(u->genmval.value); + + for (i = 0; i < n; i++) { + size_t slen = strlen(u->genmval.strs[i]); + if (slen > MAX_ENUM_STRING_SIZE) + slen = MAX_ENUM_STRING_SIZE; + av_push(strings, newSVpv(u->genmval.strs[i], slen)); + if (i == u->genmval.value) { + sv_setpvn(val, u->genmval.strs[i], slen); + SvIOK_on(val); + } + } + hashAdd(hash, "strs", 4, + newRV_noinc((SV *)strings)); + hashAdd(hash, "no_str", 6, + newSViv(u->genmval.no_str)); + hashAdd(hash, "value", 5, val); + + return newRV_noinc((SV *)hash); + } + + /* Value */ + if (value_type == DBR_CHAR) { + char *str = dbr_value_ptr(peha->dbr, peha->type); + + /* Long string => Perl scalar */ + str[peha->count - 1] = 0; + val = newSVpv(str, 0); + } else if (peha->count == 1) { + /* Single value => Perl scalar */ + val = newSVdbf(value_type, + dbr_value_ptr(peha->dbr, peha->type), 0); + } else { + /* Array of values => Perl array reference */ + AV *array = newAV(); + int i; + + for (i = 0; i < peha->count; i++) { + av_push(array, newSVdbf(value_type, + dbr_value_ptr(peha->dbr, peha->type), i)); + } + val = newRV_noinc((SV *)array); + } + hashAdd(hash, "value", 5, val); + + /* Timestamp follows status and severity in DBR_TIME */ + if (dbr_type_is_TIME(peha->type)) { + struct timespec t; + + epicsTimeToTimespec(&t, &u->tlngval.stamp); + hashAdd(hash, "stamp", 5, + newSViv(t.tv_sec)); + hashAdd(hash, "stamp_fraction", 14, + newSVnv((double)t.tv_nsec / 1e9)); + } + else if (peha->type == DBR_STSACK_STRING) { + struct dbr_stsack_string *s = (struct dbr_stsack_string *)peha->dbr; + + hashAdd(hash, "ackt", 4, + newSViv(s->ackt)); + hashAdd(hash, "acks", 4, + newSValarm(s->acks)); + } + else if (value_type != DBR_STRING && + (dbr_type_is_GR(peha->type) || + dbr_type_is_CTRL(peha->type))) { + char *units; + size_t ulen; + void *limit; + int i = dbr_type_is_CTRL(peha->type) ? 7 : 5; + + if (value_type == DBR_DOUBLE) { + units = u->gdblval.units; + limit = &u->gdblval.upper_disp_limit; + hashAdd(hash, "precision", 9, + newSViv(u->gdblval.precision)); + } else { /* value_type == DBR_LONG */ + units = u->glngval.units; + limit = &u->glngval.upper_disp_limit; + } + + ulen = strlen(units); + hashAdd(hash, "units", 5, newSVpv(units, + ulen < MAX_UNITS_SIZE ? ulen : MAX_UNITS_SIZE)); + + while (i >= 0) { + static const char * const limit_name[] = { + "upper_disp_limit", "lower_disp_limit", + "upper_alarm_limit", "upper_warning_limit", + "lower_warning_limit", "lower_alarm_limit", + "upper_ctrl_limit", "lower_ctrl_limit", + }; + + hashAdd(hash, limit_name[i], strlen(limit_name[i]), + newSVdbf(value_type, limit, i)); + i--; + } + } + + return newRV_noinc((SV *)hash); +} + + +enum io_type { + IO_GET, + IO_PUT, + IO_MONITOR, +}; + +static +void io_handler(struct event_handler_args *peha, enum io_type io) { + PERL_SET_CONTEXT(p5_ctx); + { + CA_channel *pch = ca_puser(peha->chid); + SV *code = (SV *)peha->usr; + SV *status = &PL_sv_undef; + SV *data = &PL_sv_undef; + dSP; + + ENTER; + SAVETMPS; + + if (peha->status != ECA_NORMAL) { + status = sv_2mortal(newSVpv(get_error_msg(peha->status), 0)); + } else if (io != IO_PUT) { + data = sv_2mortal(newSVdbr(peha)); + } + + sv_setsv(ERRSV, &PL_sv_undef); + + PUSHMARK(SP); + XPUSHs(pch->chan_ref); + XPUSHs(status); + XPUSHs(data); + PUTBACK; + + call_sv(code, G_VOID | G_DISCARD | G_EVAL | G_KEEPERR); + + if (io != IO_MONITOR) + SvREFCNT_dec(code); + + if (SvTRUE(ERRSV)) + croak(NULL); + + FREETMPS; + LEAVE; + } +} + + +static +int replace_handler(SV * sub, SV ** ph_sub, long *phandler) { + if (SvOK(sub) && SvTRUE(sub)) { + if (*ph_sub != NULL) { + SvSetSV(*ph_sub, sub); + return FALSE; + } + *ph_sub = newSVsv(sub); + } else { + if (*ph_sub == NULL) + return FALSE; + + SvREFCNT_dec(*ph_sub); + *ph_sub = NULL; + *phandler = 0; + } + return TRUE; +} + + +/******************************************************************************/ + +/* CA::new($class, $name, [\&sub]) */ + +static +void connect_handler(struct connection_handler_args cha) { + CA_channel *pch = ca_puser(cha.chid); + + PERL_SET_CONTEXT(p5_ctx); + { + dSP; + + SvSetSV(ERRSV, &PL_sv_undef); + + PUSHMARK(SP); + XPUSHs(pch->chan_ref); + XPUSHs(cha.op == CA_OP_CONN_UP ? &PL_sv_yes : &PL_sv_no); + PUTBACK; + + call_sv(pch->conn_sub, G_EVAL | G_VOID | G_DISCARD | G_KEEPERR); + + if (SvTRUE(ERRSV)) + croak(NULL); + } +} + +SV * CA_new(const char *class, const char *name, ...) { + dXSARGS; + SV *ca_ref = newSViv(0); + SV *ca_obj = newSVrv(ca_ref, class); + CA_channel *pch; + caCh *handler; + int status; + + Newz(0, pch, 1, CA_channel); + sv_setiv(ca_obj, (IV)pch); + SvREADONLY_on(ca_obj); + + pch->chan_ref = ca_ref; + SvREFCNT_inc(ca_ref); + + if (items > 2 + && SvOK(ST(2))) { + /* Connection handler provided */ + pch->conn_sub = newSVsv(ST(2)); + handler = &connect_handler; + } else + handler = NULL; + + status = ca_create_channel(name, handler, pch, 0, &pch->chan); + if (status != ECA_NORMAL) { + SvREFCNT_dec(ca_ref); + if (pch->conn_sub) + SvREFCNT_dec(pch->conn_sub); + croak(get_error_msg(status)); + } + + return ca_ref; +} + +static int destroyed = 0; + +/* CA::DESTROY($ca_ref) */ + +void CA_DESTROY(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + int status; + + status = destroyed ? ECA_NORMAL : ca_clear_channel(pch->chan); + + if (pch->conn_sub) + SvREFCNT_dec(pch->conn_sub); + + if (pch->rights_sub) + SvREFCNT_dec(pch->rights_sub); + + if (pch->sdata) + Safefree(pch->sdata); + + SvREFCNT_dec(pch->chan_ref); + Safefree(pch); + + if (status != ECA_NORMAL) + croak(get_error_msg(status)); +} + + +/* CA::context_destroy($class) */ + +void CA_context_destroy(const char *class) { + ca_context_destroy(); + destroyed = 1; +} + + +/* CA::change_connection_event($ca_ref, \$sub) */ + +void CA_change_connection_event(SV *ca_ref, SV *sub) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + caCh *handler = &connect_handler; + int status; + + if (! replace_handler(sub, &pch->conn_sub, (long *)&handler)) + return; + + status = ca_change_connection_event(pch->chan, handler); + + if (status != ECA_NORMAL) { + croak(get_error_msg(status)); + } +} + + +/* CA::put($ca_ref, @values) */ + +void CA_put(SV *ca_ref, SV *val, ...) { + dXSARGS; + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + int num_values = items - 1; + int status; + + if (num_values == 1) { + if (ca_field_type(pch->chan) == DBF_CHAR && + ca_element_count(pch->chan) > 1) { + size_t len; + char *long_string = SvPV(val, len); + + status = ca_array_put(DBF_CHAR, len+1, pch->chan, long_string); + } else { + union { + dbr_long_t dbr_long; + dbr_double_t dbr_double; + dbr_string_t dbr_string; + } data; + chtype type = best_type(pch); + + switch (type) { + case DBF_LONG: + data.dbr_long = SvIV(val); + break; + case DBF_DOUBLE: + data.dbr_double = SvNV(val); + break; + case DBF_STRING: + strncpy(data.dbr_string, SvPV_nolen(val), MAX_STRING_SIZE); + break; + } + status = ca_put(type, pch->chan, &data); + } + } else { + union { + dbr_char_t *dbr_char; + dbr_long_t *dbr_long; + dbr_double_t *dbr_double; + char *dbr_string; + void *dbr; + } p; + int i; + chtype type = best_type(pch); + + switch (type) { + case DBF_CHAR: + New(0, p.dbr_char, num_values, dbr_char_t); + for (i = 0; i < num_values; i++) { + p.dbr_char[i] = SvIV(ST(i + 1)); + } + break; + case DBF_LONG: + New(0, p.dbr_long, num_values, dbr_long_t); + for (i = 0; i < num_values; i++) { + p.dbr_long[i] = SvIV(ST(i + 1)); + } + break; + case DBF_DOUBLE: + New(0, p.dbr_double, num_values, dbr_double_t); + for (i = 0; i < num_values; i++) { + p.dbr_double[i] = SvNV(ST(i + 1)); + } + break; + case DBF_STRING: + New(0, p.dbr_string, num_values * MAX_STRING_SIZE, char); + for (i = 0; i < num_values; i++) { + char * src = SvPV_nolen(ST(i + 1)); + strncpy(p.dbr_string + i, src, MAX_STRING_SIZE); + } + break; + } + + status = ca_array_put(type, num_values, pch->chan, p.dbr); + Safefree(p.dbr); + } + if (status != ECA_NORMAL) { + croak(get_error_msg(status)); + } + XSRETURN(0); +} + + +/* CA::put_callback($ca_ref, \&sub, @values) */ + +static +void put_handler(struct event_handler_args eha) { + io_handler(&eha, IO_PUT); +} + +void CA_put_callback(SV *ca_ref, SV *sub, SV *val, ...) { + dXSARGS; + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + SV *put_sub = newSVsv(sub); + int num_values = items - 2; + int status; + + if (num_values == 1) { + if (ca_field_type(pch->chan) == DBF_CHAR && + ca_element_count(pch->chan) > 1) { + size_t len; + char *long_string = SvPV(val, len); + + status = ca_array_put_callback(DBF_CHAR, len+1, pch->chan, + long_string, put_handler, put_sub); + } else { + union { + dbr_long_t dbr_long; + dbr_double_t dbr_double; + dbr_string_t dbr_string; + } data; + chtype type = best_type(pch); + + switch (type) { + case DBF_LONG: + data.dbr_long = SvIV(val); + break; + case DBF_DOUBLE: + data.dbr_double = SvNV(val); + break; + case DBF_STRING: + strncpy(data.dbr_string, SvPV_nolen(val), MAX_STRING_SIZE); + break; + } + + status = ca_put_callback(type, pch->chan, &data, put_handler, put_sub); + } + } else { + union { + dbr_char_t *dbr_char; + dbr_long_t *dbr_long; + dbr_double_t *dbr_double; + char *dbr_string; + void *dbr; + } p; + int i; + chtype type = best_type(pch); + + switch (type) { + case DBF_CHAR: + New(0, p.dbr_char, num_values, dbr_char_t); + for (i = 0; i < num_values; i++) { + p.dbr_char[i] = SvIV(ST(i + 1)); + } + break; + case DBF_LONG: + New(0, p.dbr_long, num_values, dbr_long_t); + for (i = 0; i < num_values; i++) { + p.dbr_long[i] = SvIV(ST(i + 2)); + } + break; + case DBF_DOUBLE: + New(0, p.dbr_double, num_values, dbr_double_t); + for (i = 0; i < num_values; i++) { + p.dbr_double[i] = SvNV(ST(i + 2)); + } + break; + case DBF_STRING: + New(0, p.dbr_string, num_values * MAX_STRING_SIZE, char); + for (i = 0; i < num_values; i++) { + char * src = SvPV_nolen(ST(i + 2)); + strncpy(p.dbr_string + i, src, MAX_STRING_SIZE); + } + break; + } + + status = ca_array_put_callback(type, num_values, pch->chan, p.dbr, + put_handler, put_sub); + Safefree(p.dbr); + } + if (status != ECA_NORMAL) { + SvREFCNT_dec(put_sub); + croak(get_error_msg(status)); + } + XSRETURN(0); +} + + +/* CA::put_acks($ca_ref, $sevr, [\&sub]) */ + +void CA_put_acks(SV *ca_ref, SV *sevr, ...) { + dXSARGS; + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + dbr_put_acks_t acks; + int status; + + if (! SvOK(sevr)) { + acks = NO_ALARM; + } else if (SvIOK(sevr)) { + acks = SvIV(sevr); + if (acks > INVALID_ALARM) + croak("Bad acknowledgement severity %d", acks); + } else { + size_t slen; + char *sname = SvPV(sevr, slen); + for (acks = NO_ALARM; acks <= INVALID_ALARM; acks++) { + if (strcmp(sname, epicsAlarmSeverityStrings[acks]) == 0) + break; + } + if (acks > INVALID_ALARM) + croak("Bad acknowledgment severity '%s'", sname); + } + + if (items > 2) { + SV *put_sub = newSVsv(ST(2)); + status = ca_put_callback(DBR_PUT_ACKS, pch->chan, &acks, + put_handler, put_sub); + if (status != ECA_NORMAL) + SvREFCNT_dec(put_sub); + } else + status = ca_put(DBR_PUT_ACKS, pch->chan, &acks); + + if (status != ECA_NORMAL) + croak(get_error_msg(status)); + + XSRETURN(0); +} + + +/* CA::put_ackt($ca_ref, $trans, [\&sub]) */ + +void CA_put_ackt(SV *ca_ref, int ack, ...) { + dXSARGS; + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + dbr_put_ackt_t ackt = !! ack; /* 0 or 1 only */ + int status; + + if (items > 2) { + SV *put_sub = newSVsv(ST(2)); + status = ca_put_callback(DBR_PUT_ACKT, pch->chan, &ackt, + put_handler, put_sub); + if (status != ECA_NORMAL) + SvREFCNT_dec(put_sub); + } else + status = ca_put(DBR_PUT_ACKS, pch->chan, &ackt); + + if (status != ECA_NORMAL) + croak(get_error_msg(status)); + + XSRETURN(0); +} + + +/* CA::get($ca_ref) */ + +void CA_get(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + size_t count = ca_element_count(pch->chan); + int status; + + if (ca_field_type(pch->chan) == DBF_CHAR && + count > 1) { + if (!pch->sdata) { + New(0, pch->sdata, count + 1, char); + pch->ssize = count; + } else if (pch->ssize < count) { /* Reconnected to larger array? */ + Safefree(pch->sdata); + New(0, pch->sdata, count + 1, char); + pch->ssize = count; + } + status = ca_array_get(DBF_CHAR, 0, pch->chan, pch->sdata); + } else { + status = ca_get(best_type(pch), pch->chan, &pch->data); + } + if (status != ECA_NORMAL) { + croak(get_error_msg(status)); + } +} + + +/* CA::value($ca_ref) */ + +SV * CA_value(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + if (ca_field_type(pch->chan) == DBF_CHAR && + ca_element_count(pch->chan) > 1 && + pch->sdata) { + return newSVpv(pch->sdata, 0); + } + return newSVdbf(best_type(pch), &pch->data, 0); +} + + +/* CA::get_callback($ca_ref, \&sub, [$type | $count]) */ + +static +void get_handler(struct event_handler_args eha) { + io_handler(&eha, IO_GET); +} + +void CA_get_callback(SV *ca_ref, SV *sub, ...) { + dXSARGS; + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + SV *get_sub = newSVsv(sub); + int status; + chtype type = best_type(pch); + int count = 0; + int i = 2; + const char *croak_msg; + + while (items > i + && SvOK(ST(i))) { + if (SvIOK(ST(i))) { + /* Interger => Count arg, zero means current size */ + count = SvIV(ST(i)); + if (count < 0 || count > ca_element_count(pch->chan)) { + croak_msg = "Requested array size is out of range"; + goto exit_croak; + } + } else if (SvPOKp(ST(i))) { + /* String => Type arg */ + char *treq = SvPV_nolen(ST(i)); + + dbr_text_to_type(treq, type); + if (type < 0 || + type == DBR_PUT_ACKT || + type == DBR_PUT_ACKS) { + croak_msg = "Requested DBR type is invalid"; + goto exit_croak; + } else if (type == DBR_GR_ENUM || + type == DBR_CTRL_ENUM || + type == DBR_CLASS_NAME || + type == DBR_STSACK_STRING) + /* The above types are supported */ ; + else if (dbr_type_is_SHORT(type)) + type += (DBR_LONG - DBR_SHORT); + else if (dbr_type_is_FLOAT(type)) + type += (DBR_DOUBLE - DBR_FLOAT); + else if (dbr_type_is_ENUM(type)) + type += (DBR_STRING - DBR_ENUM); + } + i++; + } + + status = ca_array_get_callback(type, count, + pch->chan, get_handler, get_sub); + if (status != ECA_NORMAL) { + croak_msg = get_error_msg(status); + goto exit_croak; + } + + XSRETURN(0); + return; + +exit_croak: + SvREFCNT_dec(get_sub); + croak(croak_msg); +} + + +/* CA::create_subscription($ca_ref, $mask, \&sub, [$type | $count]) */ + +static +void subscription_handler(struct event_handler_args eha) { + io_handler(&eha, IO_MONITOR); +} + +SV * CA_create_subscription(SV *ca_ref, const char *mask_str, SV *sub, ...) { + dXSARGS; + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + SV *mon_sub = newSVsv(sub); + SV *mon_ref = newSViv(0); + SV *mon_obj = newSVrv(mon_ref, "CA::Subscription"); + chtype type = best_type(pch); + int count = ca_element_count(pch->chan); + int i = 3; + int mask = 0; + evid event; + int status; + const char *croak_msg; + + if (strchr(mask_str, 'v') || strchr(mask_str, 'V')) mask |= DBE_VALUE; + if (strchr(mask_str, 'l') || strchr(mask_str, 'L')) mask |= DBE_LOG; + if (strchr(mask_str, 'a') || strchr(mask_str, 'A')) mask |= DBE_ALARM; + if (strchr(mask_str, 'p') || strchr(mask_str, 'P')) mask |= DBE_PROPERTY; + + while (items > i + && SvOK(ST(i))) { + if (SvIOK(ST(i))) { + /* Interger => Count arg, zero means current size */ + count = SvIV(ST(i)); + if (count < 0 || count > ca_element_count(pch->chan)) { + croak_msg = "Requested array size is out of range"; + goto exit_croak; + } + } else if (SvPOKp(ST(i))) { + /* String => Type arg */ + size_t tlen; + char *treq = SvPV(ST(i), tlen); + + dbr_text_to_type(treq, type); + if (type < 0) { + croak_msg = "Unknown data type"; + goto exit_croak; + } + if (type == DBR_PUT_ACKT || + type == DBR_PUT_ACKS) { + croak_msg = "DBR_PUT_ACK types are write-only"; + goto exit_croak; + } else if (type == DBR_CLASS_NAME || + type == DBR_STSACK_STRING) + /* These break the dbr_type_is macros */ ; + else if (dbr_type_is_SHORT(type)) + type += (DBR_LONG - DBR_SHORT); + else if (dbr_type_is_FLOAT(type)) + type += (DBR_DOUBLE - DBR_FLOAT); + else if (dbr_type_is_ENUM(type)) + type += (DBR_STRING - DBR_ENUM); + } + i++; + } + + status = ca_create_subscription(type, count, pch->chan, mask, + subscription_handler, mon_sub, &event); + if (status != ECA_NORMAL) { + croak_msg = get_error_msg(status); + goto exit_croak; + } + + sv_setiv(mon_obj, (IV)event); + SvREADONLY_on(mon_obj); + SvREFCNT_inc(mon_ref); + + return mon_ref; + +exit_croak: + SvREFCNT_dec(mon_ref); + SvREFCNT_dec(mon_sub); + croak(croak_msg); +} + + +/* CA::clear_subscription($class, $subscription) */ + +void CA_clear_subscription(const char *class, SV *mon_ref) { + evid event = (evid)SvIV(SvRV(mon_ref)); + int status; + + if (! sv_isa(mon_ref, "CA::Subscription")) { + croak("Not a CA::Subscription"); + } + + status = ca_clear_subscription(event); + + if (status != ECA_NORMAL) { + croak(get_error_msg(status)); + } +} + + +/* CA::pend_io($class, $timeout) */ + +void CA_pend_io(const char *class, double timeout) { + int status = ca_pend_io(timeout); + if (status != ECA_NORMAL) { + croak(get_error_msg(status)); + } +} + +/* CA::test_io($class) */ + +int CA_test_io(const char *class) { + return (ca_test_io() == ECA_IODONE); +} + +/* CA::pend_event($class, $timeout) */ + +void CA_pend_event(const char *class, double timeout) { + int status = ca_pend_event(timeout); + if (status != ECA_TIMEOUT) { + croak(get_error_msg(status)); + } +} + +/* CA::poll($class) */ + +void CA_poll(const char *class) { + ca_poll(); +} + + +/* CA::flush_io($class) */ + +void CA_flush_io(const char *class) { + ca_flush_io(); +} + + +/* CA::add_exception_event($class, \&sub) */ + +static +SV * exception_sub = NULL; + +static +void exception_handler(struct exception_handler_args eha) { + if (! exception_sub) + return; + + PERL_SET_CONTEXT(p5_ctx); + { + SV *channel = &PL_sv_undef; + SV *status = &PL_sv_undef; + HV *hash = newHV(); + SV *op; + const char *opString[] = { + "GET", "PUT", "CREATE_CHANNEL", "ADD_EVENT", "CLEAR_EVENT", "OTHER" + }; + dSP; + + ENTER; + SAVETMPS; + + if (eha.chid) { + CA_channel *pch = ca_puser(eha.chid); + channel = pch->chan_ref; + } + if (eha.stat != ECA_NORMAL) { + status = sv_2mortal(newSVpv(get_error_msg(eha.stat), 0)); + } + + op = newSViv(eha.op); + sv_setpv(op, opString[eha.op]); + SvIOK_on(op); + hashAdd(hash, "OP", 2, op); + hashAdd(hash, "TYPE", 4, + newSVpv(dbr_type_to_text(eha.type), 0)); + hashAdd(hash, "COUNT", 5, newSViv(eha.count)); + if (eha.pFile) + hashAdd(hash, "FILE", 4, newSVpv(eha.pFile, 0)); + if (eha.lineNo) + hashAdd(hash, "LINE", 4, newSVuv(eha.lineNo)); + + PUSHMARK(SP); + XPUSHs(channel); + XPUSHs(status); + XPUSHs(sv_2mortal(newSVpv(eha.ctx, 0))); + XPUSHs(sv_2mortal(newRV_noinc((SV *)hash))); + PUTBACK; + + call_sv(exception_sub, G_EVAL | G_VOID | G_DISCARD); + + FREETMPS; + LEAVE; + } +} + +void CA_add_exception_event(const char *class, SV *sub) { + caExceptionHandler *handler = exception_handler; + int status; + + if (! replace_handler(sub, &exception_sub, (long *)&handler)) + return; + + status = ca_add_exception_event(handler, NULL); + + if (status != ECA_NORMAL) { + SvREFCNT_dec(exception_sub); + exception_sub = NULL; + croak(get_error_msg(status)); + } +} + + +/* CA::replace_printf_handler($class, \&sub) */ + +static +SV * printf_sub = NULL; + +static +int printf_handler(const char *format, va_list args) { + if (! printf_sub) + return 0; + + PERL_SET_CONTEXT(p5_ctx); + { + SV *printf_str; + dSP; + va_list argcopy; + + ENTER; + SAVETMPS; + +#ifdef __GNUC__ + __va_copy(argcopy, args); +#else + va_copy(argcopy, args); +#endif + + printf_str = NEWSV(0, strlen(format) + 32); + sv_vsetpvf(printf_str, format, &argcopy); + va_end(argcopy); + + PUSHMARK(SP); + XPUSHs(sv_2mortal(printf_str)); + PUTBACK; + + call_sv(printf_sub, G_EVAL | G_VOID | G_DISCARD); + + FREETMPS; + LEAVE; + } + return 0; +} + +void CA_replace_printf_handler(const char *class, SV *sub) { + caPrintfFunc *handler = printf_handler; + int status; + + if (! replace_handler(sub, &printf_sub, (long *)&handler)) + return; + + status = ca_replace_printf_handler(handler); + + if (status != ECA_NORMAL) { + SvREFCNT_dec(printf_sub); + printf_sub = NULL; + croak(get_error_msg(status)); + } +} + + +/* CA::field_type($ca_ref) */ + +const char * CA_field_type(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + chtype t = ca_field_type(pch->chan); + if (t == TYPENOTCONN) + return "TYPENOTCONN"; + return dbr_type_to_text(t); +} + + +/* CA::element_count($ca_ref) */ + +int CA_element_count(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + return ca_element_count(pch->chan); +} + + +/* CA::name($ca_ref) */ + +const char * CA_name(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + return ca_name(pch->chan); +} + + +/* CA::state($ca_ref) */ + +const char * CA_state(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + static const char * const state_name[] = { + "never connected", "previously connected", "connected", "closed" + }; + return state_name[ca_state(pch->chan)]; +} + + +/* CA::is_connected($ca_ref) */ + +int CA_is_connected(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + return ca_state(pch->chan) == cs_conn; +} + + +/* CA::host_name($ca_ref) */ + +const char * CA_host_name(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + return ca_host_name(pch->chan); +} + + +/* CA::read_access($ca_ref) */ + +int CA_read_access(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + return ca_read_access(pch->chan); +} + + +/* CA::write_access($ca_ref) */ + +int CA_write_access(SV *ca_ref) { + CA_channel *pch = (CA_channel *)SvIV(SvRV(ca_ref)); + return ca_write_access(pch->chan); +} + +/******************************************************************************/ + +MODULE = Cap5 PACKAGE = Cap5 + +MODULE = Cap5 PACKAGE = CA PREFIX = CA_ + +PROTOTYPES: DISABLE + +BOOT: + p5_ctx = Perl_get_context(); + + +SV * +CA_new (class, name, ...) + const char * class + const char * name + PREINIT: + I32* temp; + CODE: + temp = PL_markstack_ptr++; + RETVAL = CA_new(class, name); + PL_markstack_ptr = temp; + OUTPUT: + RETVAL + +void +CA_DESTROY (ca_ref) + SV * ca_ref + +void +CA_context_destroy (class) + const char * class + +void +CA_change_connection_event (ca_ref, sub) + SV * ca_ref + SV * sub + +void +CA_put (ca_ref, val, ...) + SV * ca_ref + SV * val + PREINIT: + I32* temp; + PPCODE: + temp = PL_markstack_ptr++; + CA_put(ca_ref, val); + if (PL_markstack_ptr != temp) { + /* truly void, because dXSARGS not invoked */ + PL_markstack_ptr = temp; + XSRETURN_EMPTY; /* return empty stack */ + } + /* must have used dXSARGS; list context implied */ + return; /* assume stack size is correct */ + +void +CA_put_callback (ca_ref, sub, val, ...) + SV * ca_ref + SV * sub + SV * val + PREINIT: + I32* temp; + PPCODE: + temp = PL_markstack_ptr++; + CA_put_callback(ca_ref, sub, val); + if (PL_markstack_ptr != temp) { + /* truly void, because dXSARGS not invoked */ + PL_markstack_ptr = temp; + XSRETURN_EMPTY; /* return empty stack */ + } + /* must have used dXSARGS; list context implied */ + return; /* assume stack size is correct */ + +void +CA_put_acks (ca_ref, sevr, ...) + SV * ca_ref + SV * sevr + PREINIT: + I32* temp; + PPCODE: + temp = PL_markstack_ptr++; + CA_put_acks(ca_ref, sevr); + if (PL_markstack_ptr != temp) { + /* truly void, because dXSARGS not invoked */ + PL_markstack_ptr = temp; + XSRETURN_EMPTY; /* return empty stack */ + } + /* must have used dXSARGS; list context implied */ + return; /* assume stack size is correct */ + +void +CA_put_ackt (ca_ref, ack, ...) + SV * ca_ref + int ack + PREINIT: + I32* temp; + PPCODE: + temp = PL_markstack_ptr++; + CA_put_ackt(ca_ref, ack); + if (PL_markstack_ptr != temp) { + /* truly void, because dXSARGS not invoked */ + PL_markstack_ptr = temp; + XSRETURN_EMPTY; /* return empty stack */ + } + /* must have used dXSARGS; list context implied */ + return; /* assume stack size is correct */ + +void +CA_get (ca_ref) + SV * ca_ref + +SV * +CA_value (ca_ref) + SV * ca_ref + +void +CA_get_callback (ca_ref, sub, ...) + SV * ca_ref + SV * sub + PREINIT: + I32* temp; + PPCODE: + temp = PL_markstack_ptr++; + CA_get_callback(ca_ref, sub); + if (PL_markstack_ptr != temp) { + /* truly void, because dXSARGS not invoked */ + PL_markstack_ptr = temp; + XSRETURN_EMPTY; /* return empty stack */ + } + /* must have used dXSARGS; list context implied */ + return; /* assume stack size is correct */ + +SV * +CA_create_subscription (ca_ref, mask_str, sub, ...) + SV * ca_ref + const char * mask_str + SV * sub + PREINIT: + I32* temp; + CODE: + temp = PL_markstack_ptr++; + RETVAL = CA_create_subscription(ca_ref, mask_str, sub); + PL_markstack_ptr = temp; + OUTPUT: + RETVAL + +void +CA_clear_subscription (class, mon_ref) + const char * class + SV * mon_ref + +void +CA_pend_io (class, timeout) + const char * class + double timeout + +int +CA_test_io (class) + const char * class + +void +CA_pend_event (class, timeout) + const char * class + double timeout + +void +CA_poll (class) + const char * class + +void +CA_flush_io (class) + const char * class + +void +CA_add_exception_event (class, sub) + const char * class + SV * sub + +void +CA_replace_printf_handler (class, sub) + const char * class + SV * sub + +const char * +CA_field_type (ca_ref) + SV * ca_ref + +int +CA_element_count (ca_ref) + SV * ca_ref + +const char * +CA_name (ca_ref) + SV * ca_ref + +const char * +CA_state (ca_ref) + SV * ca_ref + +int +CA_is_connected (ca_ref) + SV * ca_ref + +const char * +CA_host_name (ca_ref) + SV * ca_ref + +int +CA_read_access (ca_ref) + SV * ca_ref + +int +CA_write_access (ca_ref) + SV * ca_ref + diff --git a/src/cap5/Makefile b/src/cap5/Makefile new file mode 100644 index 000000000..05f3de1e3 --- /dev/null +++ b/src/cap5/Makefile @@ -0,0 +1,64 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP=../.. +include $(TOP)/configure/CONFIG + + +# Use hdepends command (not GNU compiler flags) +# to generate header file dependancies for Darwin. +# Darwin has multiple -arch compiler flags. +ifeq ($(OS_CLASS),Darwin) +HDEPENDS_METHOD = CMD +endif + +ifneq ($(findstring darwin,$(T_A)),) + # Perl loadable libraries on Darwin have funny names + LOADABLE_SHRLIB_PREFIX = + LOADABLE_SHRLIB_SUFFIX = .bundle +endif + +ifeq ($(findstring $(OS_CLASS),WIN32 cygwin32),) + # Doesn't build on WIN32 + LOADABLE_LIBRARY_HOST = Cap5 + + PERL_SCRIPTS += cainfo.pl + PERL_SCRIPTS += caput.pl + PERL_SCRIPTS += caget.pl + PERL_SCRIPTS += capr.pl + PERL_SCRIPTS += camonitor.pl + + PERL_MODULES += CA.pm +endif + +Cap5_SRCS = Cap5.xs +Cap5_LIBS = ca Com +Cap5_INCLUDES = -I$(shell $(PERL) ../perlConfig.pl archlib)/CORE +Cap5_CFLAGS = $(shell $(PERL) ../perlConfig.pl ccflags) + +ifeq ($(findstring Host,$(VALID_BUILDS)),Host) + # Can only create docs in Host build + HTMLS_DIR = . + HTMLS = CA.html +endif + +include $(TOP)/configure/RULES + +ifdef T_A + EXTUTILS = $(shell $(PERL) ../perlConfig.pl privlib)/ExtUtils + + %.c: ../%.xs + $(RM) $@ $@_new + $(PERL) $(EXTUTILS)/xsubpp -typemap $(EXTUTILS)/typemap $< > $@_new && $(MV) $@_new $@ + + %.html: ../%.pm + $(RM) $@ + podchecker $< && pod2html --infile=$< --outfile=$@ + + clean:: + $(RM) Cap5.c +endif diff --git a/src/cap5/caget.pl b/src/cap5/caget.pl new file mode 100644 index 000000000..7fe60d8c5 --- /dev/null +++ b/src/cap5/caget.pl @@ -0,0 +1,187 @@ +#!/usr/bin/perl + +use strict; + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + +use Getopt::Std; +use Scalar::Util qw(looks_like_number); +use CA; + +our ($opt_0, $opt_a, $opt_d, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n); +our ($opt_s, $opt_S, $opt_t); +our $opt_c = 0; +our $opt_F = ' '; +our $opt_w = 1; + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; + +HELP_MESSAGE() unless getopts('0:ac:d:e:f:F:g:hnsStw:'); +HELP_MESSAGE() if $opt_h; + +die "caget: -c option takes a positive number\n" + unless looks_like_number($opt_c) && $opt_c >= 0; + +die "No pv name specified. ('caget -h' gives help.)\n" + unless @ARGV; + +my @chans = map { CA->new($_); } @ARGV; + +eval { CA->pend_io($opt_w); }; +if ($@) { + if ($@ =~ m/^ECA_TIMEOUT/) { + my $err = (@chans > 1) ? 'some PV(s)' : "'" . $chans[0]->name . "'"; + print "Channel connect timed out: $err not found.\n"; + @chans = grep { $_->is_connected } @chans; + } else { + die $@; + } +} + +my %rtype; + +map { + my $type; + if ($opt_d) { + $type = $opt_d; + } else { + $type = $_->field_type; + $type = 'DBR_STRING' + if $opt_s && $type =~ m/ ^ DBR_ ( DOUBLE | FLOAT ) $ /x; + $type = 'DBR_LONG' + if ($opt_n && $type eq 'DBR_ENUM') + || (!$opt_S && $type eq 'DBR_CHAR'); + $type =~ s/^DBR_/DBR_TIME_/ + if $opt_a; + } + $rtype{$_} = $type; + $_->get_callback(\&get_callback, $type, 0+$opt_c); +} @chans; + +my $incomplete = @chans; +CA->pend_event(0.1) while $incomplete; + + +sub get_callback { + my ($chan, $status, $data) = @_; + die $status if $status; + display($chan, $rtype{$chan}, $data); + $incomplete--; +} + +sub format_number { + my ($data, $type) = @_; + if ($type =~ m/_DOUBLE$/) { + return sprintf "%.${opt_e}e", $data if $opt_e; + return sprintf "%.${opt_f}f", $data if $opt_f; + return sprintf "%.${opt_g}g", $data if $opt_g; + } + if ($type =~ m/_LONG$/) { + return sprintf "%lx", $data if $opt_0 eq 'x'; + return sprintf "%lo", $data if $opt_0 eq 'o'; + if ($opt_0 eq 'b') { + my $bin = unpack "B*", pack "l", $data; + $bin =~ s/^0*//; + return $bin; + } + } + return $data; +} + +sub display { + my ($chan, $type, $data) = @_; + if (ref $data eq 'ARRAY') { + display($chan, $type, join($opt_F, scalar @{$data}, @{$data})); + } elsif (ref $data eq 'HASH') { + printf "%s\n", $chan->name; + my $dtype = $data->{TYPE}; # Can differ from request + printf " Native data type: %s\n", $chan->field_type; + printf " Request type: %s\n", $dtype; + printf " Element count: %d\n", $data->{COUNT} + if exists $data->{COUNT}; + my $val = $data->{value}; + if (ref $val eq 'ARRAY') { + printf " Value: %s\n", join($opt_F, + map { format_number($_, $dtype); } @{$val}); + } else { + printf " Value: %s\n", format_number($val, $dtype); + } + if (exists $data->{stamp}) { + my @t = localtime $data->{stamp}; + splice @t, 6; + $t[5] += 1900; + $t[0] += $data->{stamp_fraction}; + printf " Timestamp: %4d-%02d-%02d %02d:%02d:%09.6f\n", + reverse @t; + } + printf " Status: %s\n", $data->{status}; + printf " Severity: %s\n", $data->{severity}; + if (exists $data->{units}) { + printf " Units: %s\n", $data->{units}; + } + if (exists $data->{precision}) { + printf " Precision: %d\n", $data->{precision}; + } + if (exists $data->{upper_disp_limit}) { + printf " Lo disp limit: %g\n", $data->{lower_disp_limit}; + printf " Hi disp limit: %g\n", $data->{upper_disp_limit}; + } + if (exists $data->{upper_alarm_limit}) { + printf " Lo alarm limit: %g\n", $data->{lower_alarm_limit}; + printf " Lo warn limit: %g\n", $data->{lower_warning_limit}; + printf " Hi warn limit: %g\n", $data->{upper_warning_limit}; + printf " Hi alarm limit: %g\n", $data->{upper_alarm_limit}; + } + if (exists $data->{upper_ctrl_limit}) { + printf " Lo ctrl limit: %g\n", $data->{lower_ctrl_limit}; + printf " Hi ctrl limit: %g\n", $data->{upper_ctrl_limit}; + } + } else { + my $value = format_number($data, $type); + if ($opt_t) { + print "$value\n"; + } else { + printf "%s%s%s\n", $chan->name, $opt_F, $value; + } + } +} + +sub HELP_MESSAGE { + print STDERR "\nUsage: caget [options] ...\n", + "\n", + " -h: Help: Print this message\n", + "Channel Access options:\n", + " -w : Wait time, specifies longer CA timeout, default is $opt_w second\n", + "Format options:\n", + " -t: Terse mode - print only value, without name\n", + " -a: Wide mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n", + " -d : Request specific dbr type from one of the following:\n", + " DBR_STRING DBR_LONG DBR_DOUBLE\n", + " DBR_STS_STRING DBR_STS_LONG DBR_STS_DOUBLE\n", + " DBR_TIME_STRING DBR_TIME_LONG DBR_TIME_DOUBLE\n", + " DBR_GR_STRING DBR_GR_LONG DBR_GR_DOUBLE DBR_GR_ENUM\n", + " DBR_CTRL_STRING DBR_CTRL_LONG DBR_CTRL_DOUBLE DBR_CTRL_ENUM\n", + " DBR_CLASS_NAME DBR_STSACK_STRING\n", + "Arrays: Value format: print number of values, then list of values\n", + " Default: Print all values\n", + " -c : Print first elements of an array\n", + " -S: Print array of char as a string (long string)\n", + "Enum format:\n", + " -n: Print DBF_ENUM value as number (default is enum string)\n", + "Floating point type format:\n", + " Default: Use %g format\n", + " -e : Use %e format, with a precision of digits\n", + " -f : Use %f format, with a precision of digits\n", + " -g : Use %g format, with a precision of digits\n", + " -s: Get value as string (may honour server-side precision)\n", + "Integer number format:\n", + " Default: Print as decimal number\n", + " -0x: Print as hex number\n", + " -0o: Print as octal number\n", + " -0b: Print as binary number\n", + "Set output field separator:\n", + " -F : Use to separate fields on output\n", + "\n"; + exit 1; +} diff --git a/src/cap5/cainfo.pl b/src/cap5/cainfo.pl new file mode 100644 index 000000000..2f8231494 --- /dev/null +++ b/src/cap5/cainfo.pl @@ -0,0 +1,64 @@ +#!/usr/bin/perl + +use strict; + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + +use Getopt::Std; +use CA; + +our $opt_w = 1; +our $opt_h; + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; + +HELP_MESSAGE() unless getopts('hw:'); +HELP_MESSAGE() if $opt_h; + +die "No pv name specified. ('cainfo -h' gives help.)\n" + unless @ARGV; + +my @chans = map { CA->new($_); } @ARGV; + +eval { + CA->pend_io($opt_w); +}; +if ($@) { + if ($@ =~ m/^ECA_TIMEOUT/) { + my $err = (@chans > 1) ? 'some PV(s)' : "'" . $chans[0]->name . "'"; + print "Channel connect timed out: $err not found.\n"; + } else { + die $@; + } +} + +map { display($_); } @chans; + + +sub display { + my $chan = shift; + + printf "%s\n", $chan->name; + printf " State: %s\n", $chan->state; + printf " Host: %s\n", $chan->host_name; + + my @access = ('no ', ''); + printf " Access: %sread, %swrite\n", + $access[$chan->read_access], $access[$chan->write_access]; + + printf " Data type: %s\n", $chan->field_type; + printf " Element count: %d\n", $chan->element_count; +} + +sub HELP_MESSAGE { + print STDERR "\nUsage: cainfo [options] ...\n", + "\n", + " -h: Help: Print this message\n", + "Channel Access options:\n", + " -w : Wait time, specifies CA timeout, default is $opt_w second\n", + "\n", + "Example: cainfo my_channel another_channel\n", + "\n"; + exit 1; +} diff --git a/src/cap5/camonitor.pl b/src/cap5/camonitor.pl new file mode 100644 index 000000000..559ec1233 --- /dev/null +++ b/src/cap5/camonitor.pl @@ -0,0 +1,149 @@ +#!/usr/bin/perl + +use strict; + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + +use Getopt::Std; +use Scalar::Util qw(looks_like_number); +use CA; + +our ($opt_0, $opt_e, $opt_f, $opt_g, $opt_h, $opt_n, $opt_s, $opt_S); +our $opt_c = 0; +our $opt_F = ' '; +our $opt_w = 1; +our $opt_m = 'va'; + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; + +HELP_MESSAGE() unless getopts('0:c:e:f:F:g:hm:nsSw:'); +HELP_MESSAGE() if $opt_h; + +die "caget: -c option takes a positive number\n" + unless looks_like_number($opt_c) && $opt_c >= 0; + +die "No pv name specified. ('camonitor -h' gives help.)\n" + unless @ARGV; + +my %monitors; +my @chans = map { CA->new($_, \&conn_callback); } @ARGV; + +my $fmt = ($opt_F eq ' ') ? "%-30s %s\n" : "%s$opt_F%s\n"; + +CA->pend_event($opt_w); +map { + printf $fmt, $_->name, '*** Not connected (PV not found)' + unless $monitors{$_}; +} @chans; +CA->pend_event(0); + + +sub conn_callback { + my ($chan, $up) = @_; + if ($up && ! $monitors{$chan}) { + my $type = $chan->field_type; + $type = 'DBR_STRING' + if $opt_s && $type =~ m/ ^ DBR_ ( DOUBLE | FLOAT ) $ /x; + $type = 'DBR_LONG' + if ($opt_n && $type eq 'DBR_ENUM') + || (!$opt_S && $type eq 'DBR_CHAR'); + $type =~ s/^DBR_/DBR_TIME_/; + + $monitors{$chan} = + $chan->create_subscription($opt_m, \&mon_callback, $type, 0+$opt_c); + } +} + +sub mon_callback { + my ($chan, $status, $data) = @_; + if ($status) { + printf $fmt, $chan->name, $status; + } else { + display($chan, $data); + } +} + +sub format_number { + my ($data, $type) = @_; + if ($type =~ m/_DOUBLE$/) { + return sprintf "%.${opt_e}e", $data if $opt_e; + return sprintf "%.${opt_f}f", $data if $opt_f; + return sprintf "%.${opt_g}g", $data if $opt_g; + } + if ($type =~ m/_LONG$/) { + return sprintf "%lx", $data if $opt_0 eq 'x'; + return sprintf "%lo", $data if $opt_0 eq 'o'; + if ($opt_0 eq 'b') { + my $bin = unpack "B*", pack "l", $data; + $bin =~ s/^0*//; + return $bin; + } + } + return $data; +} + +sub display { + my ($chan, $data) = @_; + die "Internal error" + unless ref $data eq 'HASH'; + + my $type = $data->{TYPE}; + my $value = $data->{value}; + if (ref $value eq 'ARRAY') { + $value = join($opt_F, $data->{COUNT}, + map { format_number($_, $type); } @{$value}); + } else { + $value = format_number($value, $type); + } + my $stamp; + if (exists $data->{stamp}) { + my @t = localtime $data->{stamp}; + splice @t, 6; + $t[5] += 1900; + $t[0] += $data->{stamp_fraction}; + $stamp = sprintf "%4d-%02d-%02d %02d:%02d:%09.6f", reverse @t; + } + printf $fmt, $chan->name, join($opt_F, + $stamp, $value, $data->{status}, $data->{severity}); +} + +sub HELP_MESSAGE { + print STDERR "\nUsage: camonitor [options] ...\n", + "\n", + " -h: Help: Print this message\n", + "Channel Access options:\n", + " -w : Wait time, specifies longer CA timeout, default is $opt_w second\n", + " -m : Specify CA event mask to use, with being any combination of\n", + " 'v' (value), 'a' (alarm), 'l' (log/archive), 'p' (property)", + " Default: '$opt_m'\n", +# "Timestamps:\n", +# " Default: Print absolute timestamps (as reported by CA)\n", +# " -r: Relative timestamps (time elapsed since start of program)\n", +# " -i: Incremental timestamps (time elapsed since last update)\n", +# " -I: Incremental timestamps (time elapsed since last update for this channel)\n", + "Enum format:\n", + " -n: Print DBF_ENUM values as number (default are enum string values)\n", + "Arrays: Value format: print number of values, then list of values\n", + " Default: Print all values\n", + " -c : Print first elements of an array\n", + " -S: Print array of char as a string (long string)\n", + "Floating point type format:\n", + " Default: Use %g format\n", + " -e : Use %e format, with a precision of digits\n", + " -f : Use %f format, with a precision of digits\n", + " -g : Use %g format, with a precision of digits\n", + " -s: Get value as string (may honour server-side precision)\n", + "Integer number format:\n", + " Default: Print as decimal number\n", + " -0x: Print as hex number\n", + " -0o: Print as octal number\n", + " -0b: Print as binary number\n", + "Alternate output field separator:\n", + " -F : Use to separate fields on output\n", + "\n", + "Example: camonitor -f8 my_channel another_channel\n", + " (doubles are printed as %f with 8 decimal digits)\n", + "\n"; + exit 1; +} diff --git a/src/cap5/capr.pl b/src/cap5/capr.pl new file mode 100644 index 000000000..4f8abac3d --- /dev/null +++ b/src/cap5/capr.pl @@ -0,0 +1,423 @@ +#!/usr/bin/perl -w + +####################################################################### +# +# capr: A program that attempts to do a "dbpr" command via channel +# access. +# +####################################################################### + +use strict; + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + +use Getopt::Std; +use EPICS::Path; +use CA; + +######### Globals ########## + +our ($opt_h, $opt_f, $opt_r); +our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} || "$Bin/../../dbd/softIoc.dbd"; +our $opt_w = 1; + +my %record = (); # Empty hash to put dbd data in +my $iIdx = 0; # Array indexes for interest, data type and base +my $tIdx = 1; +my $bIdx = 2; +my %device = (); # Empty hash to record which rec types have device support + +# EPICS field types +my %fieldType = ( + DBF_CHAR => 'DBF_CHAR', + DBF_UCHAR => 'DBF_CHAR', + DBF_DOUBLE => 'DBF_FLOAT', + DBF_FLOAT => 'DBF_FLOAT', + DBF_LONG => 'DBF_LONG', + DBF_SHORT => 'DBF_LONG', + DBF_ULONG => 'DBF_LONG', + DBF_USHORT => 'DBF_LONG', + DBF_DEVICE => 'DBF_STRING', + DBF_ENUM => 'DBF_STRING', + DBF_FWDLINK => 'DBF_STRING', + DBF_INLINK => 'DBF_STRING', + DBF_MENU => 'DBF_STRING', + DBF_OUTLINK => 'DBF_STRING', + DBF_STRING => 'DBF_STRING', + DBF_NOACCESS => 'DBF_NOACCESS', +); + +# globals for sub caget +my %callback_data; +my %timed_out; +my $callback_incomplete; + +######### Main program ############ + +HELP_MESSAGE() unless getopts('hd:f:rw:'); +HELP_MESSAGE() if $opt_h; + +die "File $opt_d not found. (\"capr.pl -h\" gives help)\n" + unless -f $opt_d; + +parseDbd($opt_d); +print "Using $opt_d\n\n"; + +# Print a list of record types +if ($opt_r) { + print ("Record types found:\n"); + printList(0); + exit; +} + +# Print the fields defined for given record +if ($opt_f) { + printRecordList($opt_f); + exit; +} + +HELP_MESSAGE() unless @ARGV; + +$_ = shift; +if (@ARGV) { + # Drop any ".FIELD" part + s/\. \w+ $//x; + printRecord($_, @ARGV); +} else { + if (m/^ \s* ([]+:;<>0-9A-Za-z[-]+) (?:\. \w+)? \s* , \s* (\d+) \s* $/x) { + # Recognizes ",n" as an interest leve, drops any ".FIELD" part + printRecord($1, $2); + } else { + # Drop any ".FIELD" part + s/\. \w+ $//x; + printRecord($_, 0); + } +} + +########## End of main ########### + + + +# parseDbd +# Takes given dbd file and parses it to produce a hash table of record types +# giving their fields, and for each field its interest level and data type. +# usage: parseDbd("fileName"); +# Output goes to the global %record, a hash of references to other hashes +# containing the fields of each record type. Those hash values (keyed by +# field name) are references to arrays containing the interest level, data +# type and number base of the field. +sub parseDbd { + my $dbdFile = shift; + + open(DBD, "< $dbdFile") or die "Can't open file $dbdFile: $!\n"; + my @dbd = ; + close(DBD) or die "Can't close $dbdFile: $!\n"; + + my $i = 1; + my $level = 0; + my $isArecord = 0; + my $isAfield = 0; + my $thisRecord; + my $thisField; + my $thisType; + my $field = {}; + my $interest = 0; + my $thisBase = 'DECIMAL'; + + while (@dbd) { + $_ = shift @dbd; + chomp; + if ( m/recordtype \s* \( \s* (\w+) \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 0; + $isArecord = 1; + $thisRecord = $1; + } + elsif ( m/field \s* \( \s* (\w+) \s* , \s* (\w+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 1 && $isArecord; + $thisField = $1; + $thisType = $2; + $isAfield = 1; + } + elsif ( m/interest \s* \( \s* (\w+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 2 && $isAfield; + $interest = $1; + } + elsif ( m/base \s* \( \s* (\w+) \s* \)/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 2 && $isAfield; + $thisBase = $1; + } + elsif ( m/device \s* \( (\w+) \s* ,/x ) { + die "File format error at line $i of file\n $opt_d\n" + unless $level == 0; + $device{$1}++; + } + if ( m/\{/ ) { + $level++; + } + if ( m/\}/ ) { + if ($level == 2 && $isAfield) { + my $params = []; + $params->[$iIdx] = $interest; + $params->[$tIdx] = $thisType; + $params->[$bIdx] = $thisBase; + $field->{$thisField} = $params; + $isAfield = 0; + $interest = 0; # reset default + $thisBase = 'DECIMAL'; # reset default + } + elsif ($level == 1 && $isArecord) { + $isArecord = 0; + $record{$thisRecord} = $field; + $field = {}; # start another hash + } + $level--; + } + $i++; + } +} + + +# Given a record name, attempts to find the record and its type. +# Usage: $recordType = getRecType("recordName"); +sub getRecType { + my $arg = shift; + my $name = "$arg.RTYP"; + + my $fields_read = caget($name); + + die "Could not determine record type of $arg\n" + unless $fields_read == 1; + + return $callback_data{$name}; +} + +# Given the record type and field, returns the interest level, data type +# and number base for the field +# Usage: ($dataType, $interest, $base) = getFieldParams($recType, $field); +sub getFieldParams { + my ($recType, $field) = @_; + + my $params = $record{$recType}{$field} or + die "Can't find params for $recType.$field"; + exists($fieldType{$params->[$tIdx]}) || + die "Field data type $field for $recType not found in dbd file --"; + exists($params->[$iIdx]) || + die "Interest level for $field in $recType not found in dbd file --"; + + my $fType = $fieldType{$params->[$tIdx]}; + my $fInterest = $params->[$iIdx]; + my $fBase = $params->[$bIdx]; + return ($fType, $fInterest, $fBase); +} + +# Prints field name and data for given field. Formats output so +# that fields align in to 4 columns. Tries to imitate dbpf format +# Usage: printField( $fieldName, $data, $dataType, $base, $firstColumnPosn) +sub printField { + my ($fieldName, $fieldData, $dataType, $base, $col) = @_; + + my $screenWidth = 80; + my ($outStr, $wide); + + my $field = "$fieldName:"; + + if ( $dataType eq 'DBF_STRING' ) { + $outStr = sprintf('%-5s %s', $field, $fieldData); + } elsif ( $base eq 'HEX' ) { + my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData; + $outStr = sprintf('%-5s 0x%x', $field, $val); + } elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) { + $outStr = sprintf('%-5s %.8f', $field, $fieldData); + } elsif ( $dataType eq 'DBF_CHAR' ) { + $outStr = sprintf('%-5s %d', $field, ord($fieldData)); + }else { + # DBF_LONG, DBF_SHORT, DBF_UCHAR, DBF_ULONG, DBF_USHORT + $outStr = sprintf('%-5s %d', $field, $fieldData); + } + + my $len = length($outStr); + if ($len <= 20) { $wide = 20; } + elsif ( $len <= 40 ) { $wide = 40; } + elsif ( $len <= 60 ) { $wide = 60; } + else { $wide = 80;} + + my $pad = $wide - $len; + + $col += $wide; + if ($col > $screenWidth ) { + print("\n"); + $col = $wide; + } + + print $outStr, ' ' x $pad; + + return $col; +} + +# Query for a list of fields simultaneously. +# The results are filled in the the %callback_data global hash +# and the result of the operation is the number of read pvs +# +# NOTE: Not re-entrant because results are written to global hash +# %callback_data +# +# Usage: $fields_read = caget( @pvlist ) +sub caget { + my @chans = map { CA->new($_); } @_; + + #clear results; + %callback_data = (); + %timed_out = (); + + eval { CA->pend_io($opt_w); }; + if ($@) { + if ($@ =~ m/^ECA_TIMEOUT/) { + my $err = (@chans > 1) ? 'some PV(s)' : '"' . $chans[0]->name . '"'; + print "Channel connect timed out: $err not found.\n"; + foreach my $chan (@chans) { + $timed_out{$chan->name} = $chan->is_connected; + } + @chans = grep { $_->is_connected } @chans; + } else { + die $@; + } + } + + map { + my $type; + $type = $_->field_type; + $_->get_callback(\&caget_callback, $type); + } @chans; + + my $fields_read = @chans; + $callback_incomplete = @chans; + CA->pend_event(0.1) while $callback_incomplete; + return $fields_read; +} + +sub caget_callback { + my ($chan, $status, $data) = @_; + die $status if $status; + $callback_data{$chan->name} = $data; + $callback_incomplete--; +} + +# Given record name and interest level prints data from record fields +# that are at or below the interest level specified. +# Usage: printRecord($recordName, $interestLevel) +sub printRecord { + my ($name, $interest) = @_; + + my $recType = getRecType($name); + print("Record $name type $recType\n"); + die "Record type $recType not found\n" + unless exists $record{$recType}; + + #capture list of fields + my @readlist = (); #fields to read via CA + my @fields_pr = (); #fields for print-out + my @ftypes = (); #types, from parser + my @bases = (); #bases, from parser + foreach my $field (sort keys %{$record{$recType}}) { + # Skip DTYP field if this rec type doesn't have device support defined + if ($field eq 'DTYP' && !(exists($device{$recType}))) { next; } + + my ($fType, $fInterest, $base) = getFieldParams($recType, $field); + unless( $fType eq 'DBF_NOACCESS' ) { + if ($interest >= $fInterest ) { + my $fToGet = "$name.$field"; + push @fields_pr, $field; + push @readlist, $fToGet; + push @ftypes, $fType; + push @bases, $base; + } + } + } + my $fields_read = caget( @readlist ); + + # print while iterating over lists gathered + my $col = 0; + for (my $i=0; $i < scalar @readlist; $i++) { + my $field = $fields_pr[$i]; + my $fToGet = $readlist[$i]; + my ($fType, $data, $base); + if ($timed_out{$fToGet}) { + $fType = $fieldType{DBF_STRING}; + $data = ''; + } + else { + $fType = $ftypes[$i]; + $base = $bases[$i]; + $data = $callback_data{$fToGet}; + } + $col = printField($field, $data, $fType, $base, $col); + } + print("\n"); # Final newline +} + +# Prints list of record types found in dbd file. If level > 0 +# then the fields of that record type, their interest levels and types are +# also printed. +# Diagnostic routine, usage: void printList(level); +sub printList { + my $level = shift; + + foreach my $rkey (sort keys(%record)) { + print(" $rkey\n"); + if ($level > 0) { + foreach my $fkey (keys %{$record{$rkey}}) { + print("\tField $fkey - interest $record{$rkey}{$fkey}[$iIdx] "); + print("- type $record{$rkey}{$fkey}[$tIdx] "); + print("- base $record{$rkey}{$fkey}[$bIdx]\n"); + } + } + } +} + +# Prints list of fields with interest levels for given record type +# Diagnostic routine, usage: void printRecordList("recordType"); +sub printRecordList { + my $type = shift; + + if (exists($record{$type}) ) { + print("Record type - $type\n"); + foreach my $fkey (sort keys %{$record{$type}}) { + printf('%-4s', $fkey); + printf(" interest = $record{$type}{$fkey}[$iIdx]"); + printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]); + print (" base = $record{$type}{$fkey}[$bIdx]\n"); + } + } + else { + print("Record type $type not defined in dbd file $opt_d\n"); + } +} + +sub HELP_MESSAGE { + print STDERR "\n", + "Usage: capr.pl -h\n", + " capr.pl [-d file.dbd] [-w seconds] -r\n", + " capr.pl [-d file.dbd] [-w seconds] -f record_type\n", + " capr.pl [-d file.dbd] [-w seconds] record_name [interest]\n", + "Description:\n", + " Attempts to perform a \"dbpr\" record print via channel access for \n", + " record_name at an interest level which defaults to level 0.\n\n", + " The -r or -f options cause it to print record type or field lists.\n", + "\n", + "Options:\n", + " -h Prints this help message.\n", + " -r Lists all record types in the dbd file.\n", + " -f record_type: Lists all fields plus their interest level, data type\n", + " and number base for the given record_type.\n", + " -d file.dbd: The dbd file containing record type definitions.\n", + " This can be set using the EPICS_CAPR_DBD_FILE environment variable.\n", + " Currently ", AbsPath($opt_d), "\n", + " -w seconds: CA connection timeout, currently $opt_w\n", + "\n"; + exit 1; +} diff --git a/src/cap5/caput.pl b/src/cap5/caput.pl new file mode 100644 index 000000000..bc62eead4 --- /dev/null +++ b/src/cap5/caput.pl @@ -0,0 +1,186 @@ +#!/usr/bin/perl + +use strict; + +use FindBin qw($Bin); +use lib "$Bin/../../lib/perl"; + +use Getopt::Std; +use CA; + +our ($opt_0, $opt_c, $opt_e, $opt_f, $opt_g, $opt_h, $opt_l, + $opt_n, $opt_s, $opt_S, $opt_t); +our $opt_w = 1; + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; + +HELP_MESSAGE() unless getopts('achlnsStw:'); +HELP_MESSAGE() if $opt_h; + +die "No pv name specified. ('caput -h' gives help.)\n" + unless @ARGV; +my $pv = shift; + +die "No value specified. ('caput -h' gives help.)\n" + unless @ARGV; + +my $chan = CA->new($pv); +eval { + CA->pend_io($opt_w); +}; +if ($@) { + if ($@ =~ m/^ECA_TIMEOUT/) { + print "Channel connect timed out: '$pv' not found.\n"; + exit 2; + } else { + die $@; + } +} + +die "Write access denied for '$pv'.\n" unless $chan->write_access; + +my $n = $chan->element_count(); +die "Too many values given, '$pv' limit is $n\n" + unless $n >= @ARGV; + +my $type = $chan->field_type; +$type = 'DBR_STRING' + if $opt_s && $type =~ m/ ^ DBR_ (ENUM | FLOAT | DOUBLE) $ /x; +$type = 'DBR_LONG' + if ($opt_n && $type eq 'DBR_ENUM') + || (!$opt_S && $type eq 'DBR_CHAR'); +$type =~ s/^DBR_/DBR_TIME_/ + if $opt_l; + +my @values; +if ($type !~ m/ ^ DBR_ (STRING | ENUM | CHAR) $ /x) { + # Make @ARGV strings numeric + @values = map { +$_; } @ARGV; +} else { + # Use strings + @values = @ARGV; +} + +my $done = 0; +if ($opt_t) { + do_put(); +} else { + $chan->get_callback(\&old_callback, $type); +} +CA->pend_event(0.1) until $done; + + +sub old_callback { + my ($chan, $status, $data) = @_; + die $status if $status; + display($chan, $type, $data, 'Old'); + do_put(); +} + +sub do_put { + if ($opt_c) { + $chan->put_callback(\&put_callback, @values); + } else { + $chan->put(@values); + $chan->get_callback(\&new_callback, $type); + } +} + +sub put_callback { + my ($chan, $status) = @_; + die $status if $status; + $chan->get_callback(\&new_callback, $type); +} + +sub new_callback { + my ($chan, $status, $data) = @_; + die $status if $status; + display($chan, $type, $data, 'New'); + $done = 1; +} + +sub format_number { + my ($data, $type) = @_; + if ($type =~ m/_DOUBLE$/) { + return sprintf "%.${opt_e}e", $data if $opt_e; + return sprintf "%.${opt_f}f", $data if $opt_f; + return sprintf "%.${opt_g}g", $data if $opt_g; + } + if ($type =~ m/_LONG$/) { + return sprintf "%lx", $data if $opt_0 eq 'x'; + return sprintf "%lo", $data if $opt_0 eq 'o'; + if ($opt_0 eq 'b') { + my $bin = unpack "B*", pack "l", $data; + $bin =~ s/^0*//; + return $bin; + } + } + return $data; +} + +sub display { + my ($chan, $type, $data, $prefix) = @_; + if (ref $data eq 'ARRAY') { + display($chan, $type, join(' ', @{$data}), $prefix); + } elsif (ref $data eq 'HASH') { + $type = $data->{TYPE}; # Can differ from request + my $value = $data->{value}; + if (ref $value eq 'ARRAY') { + $value = join(' ', map { format_number($_, $type); } @{$value}); + } else { + $value = format_number($value, $type); + } + my $stamp; + if (exists $data->{stamp}) { + my @t = localtime $data->{stamp}; + splice @t, 6; + $t[5] += 1900; + $t[0] += $data->{stamp_fraction}; + $stamp = sprintf "%4d-%02d-%02d %02d:%02d:%09.6f", reverse @t; + } + printf "%-30s %s %s %s %s\n", $chan->name, + $stamp, $value, $data->{status}, $data->{severity}; + } else { + my $value = format_number($data, $type); + if ($opt_t) { + print "$value\n"; + } else { + printf "$prefix : %-30s %s\n", $chan->name, $value; + } + } +} + +sub HELP_MESSAGE { + print STDERR "\nUsage: caput [options] ...\n", + "\n", + " -h: Help: Print this message\n", + "Channel Access options:\n", + " -w : Wait time, specifies longer CA timeout, default is $opt_w second\n", + " -c: Use put_callback to wait for completion\n", + "Format options:\n", + " -t: Terse mode - print only sucessfully written value, without name\n", + " -l: Long mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n", + " -S: Put string as an array of char (long string)\n", + "Enum format:\n", + " Default: Auto - try value as ENUM string, then as index number\n", + " -n: Force interpretation of values as numbers\n", + " -s: Force interpretation of values as strings\n", + "Floating point type format:\n", + " Default: Use %g format\n", + " -e : Use %e format, with a precision of digits\n", + " -f : Use %f format, with a precision of digits\n", + " -g : Use %g format, with a precision of digits\n", + " -s: Get value as string (may honour server-side precision)\n", + "Integer number format:\n", + " Default: Print as decimal number\n", + " -0x: Print as hex number\n", + " -0o: Print as octal number\n", + " -0b: Print as binary number\n", + "\n", + "Examples:\n", + " caput my_channel 1.2\n", + " caput my_waveform 1.2 2.4 3.6 4.8 6.0\n", + "\n"; + exit 1; +} + diff --git a/src/cap5/perlConfig.pl b/src/cap5/perlConfig.pl new file mode 100644 index 000000000..11a524595 --- /dev/null +++ b/src/cap5/perlConfig.pl @@ -0,0 +1,9 @@ +#!/usr/bin/perl + +# This script is used to extract information about the Perl build +# configuration, so the EPICS build system uses the same settings. + +use Config; + +my $arg = shift; +print $Config{$arg}; diff --git a/src/cas/Makefile b/src/cas/Makefile new file mode 100644 index 000000000..7206ff531 --- /dev/null +++ b/src/cas/Makefile @@ -0,0 +1,21 @@ +#************************************************************************* +# 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 + +DIRS = build example + +example_DEPEND_DIRS = build + +include $(TOP)/configure/RULES_DIRS + + diff --git a/src/cas/README b/src/cas/README new file mode 100644 index 000000000..bba9e6b50 --- /dev/null +++ b/src/cas/README @@ -0,0 +1,29 @@ + +Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +README file for the EPICS Channel Access Server + +Author Jeff Hill johill@lanl.gov + + +Directory Structure +------------------- +o example - example server tool +o generic - generic server lib source code +o os - os dependnet server lib source code +o io - io dependnet server lib source code +o build/singleThread - server lib object code for single threaded use +o build/multiThread - server lib object code for multi threaded use + +Internal Source Code Naming Conventions +--------------------------------------- +o API class X will almost always have associated internal (usually + private base) class named XI +o for class X there will + o sometimes be X.h - the class declaration (and simple inlines) + o sometimes be XIL.h - complex inline functions (that will not + compile unless the compiler has seen + the declarations of other classes) + o be X.cc - all other source code for the class + + diff --git a/src/cas/RELEASE_NOTES b/src/cas/RELEASE_NOTES new file mode 100644 index 000000000..7037312b1 --- /dev/null +++ b/src/cas/RELEASE_NOTES @@ -0,0 +1,253 @@ + + + +Changes between epics 3.13 Beta 4 and 3.13 Beta 5 + +**** API Change **** +o The canonical PV name is returned from caServer::pvExistTest() +in the supplied buffer and not in a gdd data descriptor. See +"casdef.h". +old API: + // + // pvExistTest() + // + // The server tool is encouraged to accept multiple PV name + // aliases for the same PV here. However, a unique canonical name + // must be selected for each PV. + // + // returns S_casApp_success and fills in canonicalPVName + // if the PV is in this server tool + // + // returns S_casApp_pvNotFound if the PV does not exist in + // the server tool + // + // The server tool returns the unique canonical name for + // the pv into the gdd. A gdd is used here because this + // operation is allowed to complete asynchronously. + // + virtual caStatus pvExistTest (const casCtx &ctx, const char *pPVName, + gdd &canonicalPVName) = 0; +new API: + // + // pvExistTest() + // + // The server tool is encouraged to accept multiple PV name + // aliases for the same PV here. However, one unique canonical name + // must be selected by the server tool and returned to the + // server lib for each PV. The server will use this canonical + // name to prevent internal duplication of data structures for + // process variables that have multiple aliases. + // + // o returns S_casApp_success and a valid canonical name string + // when the PV is in this server tool + // + // o returns S_casApp_pvNotFound if the PV does not exist in + // the server tool + // + // Examples: + // caServerXXX::pvExistTest(const casCtx &ctx, const char *pPVName) + // { + // return pvExistReturn(S_casApp_success, pPVName); // common + // return pvExistReturn(S_casApp_pvNotFound); // no PV by that name + // + // char pName[9] = "myPVName"; + // return pvExistReturn(S_casApp_success, pName); // also common + // return pvExistReturn(S_casApp_asyncCompletion); // not now + // } + // + virtual pvExistReturn pvExistTest (const casCtx &ctx, + const char *pPVName)=0; + +**** API Change **** +o The server tool must now use one of class casAsyncReadIO, casAsyncWriteIO, or +casAsyncPVExistIO in place of casAsyncIO. See "casdef.h". + +**** API Change **** +o Virtual function prototype change: +Before: "aitEnum casPV::bestExternalType()" +After: "aitEnum casPV::bestExternalType() const" + +**** API Change **** +o The following virtual functions were added to casPV: + // + // Returns the maximum bounding box for all present and + // future data stored within the PV. + // + // The routine "dimension()" returns the maximum + // number of dimensions in the hypercube (0=scaler, + // 1=array, 2=plane, 3=cube ...}. + // + // The routine "maxBound(dimension)" returns the + // maximum length of a particular dimension of the + // hypercube as follows: + // + // dim equal to 0 1 3 ... + // ------------------------------------------- + // hypercube + // type + // --------- + // + // array array + // length + // + // plane x y + // + // cube x y z + // + // . + // . + // . + // + // The default (base) "dimension()" returns zero (scaler). + // The default (base) "maxBound()" returns scaler bounds + // for all dimensions. + // + // Clients will see that the PV's data is scaler if + // these routines are not supplied in the derived class. + // + // If the "dimension" argument to maxBounds() is set to + // zero then the bound on the first dimension is being + // fetched. If the "dimension" argument to maxBound() is + // set to one then the bound on the second dimension + // are being fetched... + // + virtual unsigned maxDimension() const; // return zero if scaler + virtual aitIndex maxBound (unsigned dimension) const; + +The defaults in base class casPV implement identical behavior +to the past if these routines are not supplied by the derived +class. + +Changes between epics 3.13 Beta 6 and 3.13 Beta ???? + +**** API Change **** + +o The member function "casChannel::postEvent()" has been replaced by +"casChannel::postAccessRightsEvent()". An access rights state change +event is now posted to the client each time that +"casChannel::postAccessRightsEvent()" is called. + +o The virtual functions "casChannel::interestRegister()" +and "casChannel::interestDelete()" have been eliminated. + +o The constructor "caServer::caServer()" no-longer has an argument specifying +the maximum PV name length. It also no longer has an argument specifying +the maximum simultaneous IO operations. THIS IS LIKELY TO BREAK YOUR CODE +BECAUSE THE FIRST TWO ARGUMENTS WERE REMOVED AND THERE ARE DEFAULT ARGUMENTS. +This change was made because we would like to remove all limits on +the PV name length (real or perceived). We also felt that if a server +tool wishes to postpone an asynchronious IO operation then it +should return S_casApp_postponeAsyncIO from caServer::pvExistTest() and +caServer::createPV() (instead of relying on the server to keep track of +the number of simultaneous asynchronous IO operations). This provides a +less complex and more flexible API. + +o The member function "casPV::casPV(caServer &cas)" replaces the member +function "casPV::casPV(const casCtx &ctx, const char * const pPVName)". + +o The virtual member function +"caServer::createPV(const casCtx &ctx, const char *pPVName)" +has been replaced by the virtual member function +"pvCreateReturn createPV (const casCtx &ctx, const char *pPVAliasName)" +This change was made in order to allow asynchronous completion of a +PV create operation. + +o The data type (class) pvExistReturn has been changed to an enum - +"enum pvExistReturn {pverExistsHere, pverDoesNotExistHere, + pverAsyncCompletion, pverNoMemoryForAsyncOP}" +This impacts the virtual member function +"pvExistTest (const casCtx &ctx, const char *pPVAliasName)" + +o The server tool is now required to supply the virtual function +"casPV::getName()" so that the server is able to identify the process +variable when diagnostics are printed. + +o The virtual function casPV::maxSimultAsyncOps() has been eliminated +in favor of allowing the server tool to return S_casApp_postponeAsyncIO +from casPV::read() or casPV::write() when there are too many simultaneous +asynchronous IO operations and the server tool would like to postpone +the current (and future) request(s) until one of the outstanding asynchronous +IO operations (read or write) completes. + +o All "show()" virtual member functions in the interface classes +have had the "const" attribute added. + +**** Semantic Change **** + +o IMPORTANT: It is now the responsibility of the server tool to detect attempts +by the server lib to create a 2nd PV with the same canonical name as an +existing PV and avoid this by returning a pointer to the first PV created. +Likewise, if there are several aliases for one canonical PV name then it is +the responsibility of the server tool to return "pvExistsHere" from +"caServerDerived::pvExistTest()" for each of the aliases. Likewise, if there +are several aliases for one canonical PV name then it is the responsibility +of the server tool to return a single PV with the canonical name from +"caServerDerived::createPV()" (even if createPV() is called multiple times +each with a different alias name). This change was made to simplify the API +and to eliminate redundant data structures and labor occurring within the server +tool and the server library. + +o PV creation is now allowed to complete asynchronously + +o It is now the responsibility of the server tool to limit the +number of simultaneous asynchronous IO operations allowed (by returning +S_casApp_postponeAsyncIO). + +Changes between epics 3.13 Beta 11 and 3.13 Beta 12 + +**** API Change **** +o The constructor for class casPV no longer requires a reference to the +server. For backwards compatibility a reference to the server may still be +supplied, but it will not be used. + +Changes after epics 3.13.1 + +**** API Change **** +o The virtual function caServer::createPV() has been deprecated in favor of the new +virtual function caServer::attachPV() which provides exactly the same functionality. +The name was changed so that the purpose of this virtual function is more clear +to server tool designers. To remain backwards compatible the virtual function +caServer::createPV() is called by the base implementation of caServer::attachPV(). +In a future release all calls to the virtual member function caServer::createPV() +will be eliminated. + +o The class pvCreateReturn has been deprecated in favor of the new class +pvAttachReturn which provides exactly the same functionality. The name was +changed so that the purpose of this class is more clear to server tool +designers. To remain backwards compatible the class pvCreateReturn derives from +public base class pvAttachReturn. In a future release the class pvCreateReturn +will be eliminated. + +o The class casAsyncPVCreateIO has been deprecated in favor of the new class +casAsyncPVAttachIO which provides exactly the same functionality. The name was +changed so that the purpose of this class is more clear to server tool +designers. To remain backwards compatible the class casAsyncPVCreateIO derives from +public base class casAsyncPVAttachIO. In a future release the class casAsyncPVCreateIO +will be eliminated. + +o The hash table template class resTable now requires a PV count estimate +argument to its the constructor, and its init(PV count estimate) member function +has been eliminated (server tools frequently implement a string hash table +based on class resTable). + +o The delay argument passed to fileDescriptorManager.process(delay) has been changed +from type osiTime to the C++ primitive type double. + +o The arithmetic operators in class osiTime now return type double, and not type +osiTime. + +o In class osiTimer the use of type osiBool was eliminated in favor of the now universally +implemented ANSI STD C++ "bool" type. + +o In class osiTimer type double, and not type osiTime, is returned from virtual member +function delay(). + +o The public const event mask member data in the caServer class were changed to member +functions in order to avoid problems on some compiler where const event masks cant be +created until the server is fully constructed. The constants have been replaced by the +following member functions. + casEventMask valueEventMask() const; // DBE_VALUE registerEvent("value") + casEventMask logEventMask() const; // DBE_LOG registerEvent("log") + casEventMask alarmEventMask() const; // DBE_ALARM registerEvent("alarm") + + diff --git a/src/cas/build/Makefile b/src/cas/build/Makefile new file mode 100644 index 000000000..ab4a14d99 --- /dev/null +++ b/src/cas/build/Makefile @@ -0,0 +1,94 @@ +#************************************************************************* +# 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 := ../../.. +CAS := $(TOP)/src/cas +SRC := $(CAS)/generic +CA := $(CAS)/../ca +IOSRC := $(CAS)/io/bsdSocket +STSRC := $(SRC)/st + +include $(TOP)/configure/CONFIG + +SRC_DIRS += $(SRC) +SRC_DIRS += $(IOSRC) +SRC_DIRS += $(STSRC) +SRC_DIRS += $(CA) + +#USR_CXXFLAGS = $(USR_CFLAGS) + +INC += casdef.h +INC += casEventMask.h +INC += caNetAddr.h + +LIBSRCS += caServer.cc +LIBSRCS += caServerI.cc +LIBSRCS += casCoreClient.cc +LIBSRCS += casDGClient.cc +LIBSRCS += casStrmClient.cc +LIBSRCS += casPV.cc +LIBSRCS += casPVI.cc +LIBSRCS += casChannel.cc +LIBSRCS += casChannelI.cc +LIBSRCS += casAsyncIOI.cc +LIBSRCS += casAsyncReadIO.cc +LIBSRCS += casAsyncReadIOI.cc +LIBSRCS += casAsyncWriteIO.cc +LIBSRCS += casAsyncWriteIOI.cc +LIBSRCS += casAsyncPVExistIO.cc +LIBSRCS += casAsyncPVExistIOI.cc +LIBSRCS += casAsyncPVAttachIO.cc +LIBSRCS += casAsyncPVAttachIOI.cc +LIBSRCS += casEventSys.cc +LIBSRCS += casMonitor.cc +LIBSRCS += casMonEvent.cc +LIBSRCS += inBuf.cc +LIBSRCS += outBuf.cc +LIBSRCS += casCtx.cc +LIBSRCS += casEventMask.cc +LIBSRCS += ioBlocked.cc +LIBSRCS += casBufferFactory.cc +LIBSRCS += pvExistReturn.cc +LIBSRCS += pvAttachReturn.cc +LIBSRCS += caNetAddr.cc +LIBSRCS += beaconTimer.cc +LIBSRCS += beaconAnomalyGovernor.cc +LIBSRCS += clientBufMemoryManager.cpp +LIBSRCS += chanIntfForPV.cc +LIBSRCS += channelDestroyEvent.cpp + +LIBSRCS += casIntfOS.cc +LIBSRCS += casDGIntfOS.cc +LIBSRCS += casStreamOS.cc + +LIBSRCS += caServerIO.cc +LIBSRCS += casIntfIO.cc +LIBSRCS += casDGIntfIO.cc +LIBSRCS += casStreamIO.cc +LIBSRCS += ipIgnoreEntry.cc + +# There is a bug in some vxWorks compilers that these work around: +ifeq ($(VX_GNU_VERSION), 4.1.2) + casStreamOS_CXXFLAGS_vxWorks-ppc604_altivec = -O0 + casStreamOS_CXXFLAGS_vxWorks-ppc604_long = -O0 + casStreamOS_CXXFLAGS_vxWorks-ppc604 = -O0 +endif + +LIBSRCS_vxWorks += templateInstances.cpp + +LIBRARY = cas +cas_LIBS = ca gdd Com +cas_SYS_LIBS_WIN32 = ws2_32 +cas_RCS = cas.rc + +include $(TOP)/configure/RULES + +clean:: + @$(RM) Templates.DB + diff --git a/src/cas/build/cas.rc b/src/cas/build/cas.rc new file mode 100755 index 000000000..8ad47e360 --- /dev/null +++ b/src/cas/build/cas.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Channel Access Server Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Channel Access Server Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "cas\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "cas.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/cas/example/Makefile b/src/cas/example/Makefile new file mode 100644 index 000000000..8f4cc5100 --- /dev/null +++ b/src/cas/example/Makefile @@ -0,0 +1,19 @@ +#************************************************************************* +# 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 + +DIRS = directoryService + +include $(TOP)/configure/RULES_DIRS + + diff --git a/src/cas/example/README b/src/cas/example/README new file mode 100644 index 000000000..affe6533d --- /dev/null +++ b/src/cas/example/README @@ -0,0 +1,7 @@ +This directory contains a server tool example (which uses the +ca server library): + +directoryService - fully functional pv name resolution server + +The simple example that was provided here has been become the +makeBaseApp template "caServerApp" diff --git a/src/cas/example/directoryService/Makefile b/src/cas/example/directoryService/Makefile new file mode 100644 index 000000000..d6b4aefb7 --- /dev/null +++ b/src/cas/example/directoryService/Makefile @@ -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. +#************************************************************************* + +TOP=../../../.. + +include $(TOP)/configure/CONFIG + +PROD_LIBS := cas ca gdd Com +#cas_DIR = $(INSTALL_LIB) +#ca_DIR = $(INSTALL_LIB) +#gdd_DIR = $(INSTALL_LIB) +#Com_DIR = $(INSTALL_LIB) + +# +# Added winmm user32 for the non-dll build +# +PROD_SYS_LIBS_WIN32 := ws2_32 advapi32 user32 + +SRCS += main.cc +SRCS += directoryServer.cc + +PROD_HOST = caDirServ + +include $(TOP)/configure/RULES + +pcaDirServ: $(PRODUCT_OBJS) $(PROD_RESS) $(PROD_DEPLIBS) + $(PURIFY_$(OS_CLASS)) $(LINK.cpp) + +clean:: + @$(RM) caDirServ + @$(RM) pcaDirServ + @$(RM) Templates.DB + @$(RM) core + diff --git a/src/cas/example/directoryService/README b/src/cas/example/directoryService/README new file mode 100644 index 000000000..73c50df2d --- /dev/null +++ b/src/cas/example/directoryService/README @@ -0,0 +1,13 @@ + +This directory contains an example ca directory (name resolution) server. + +The directory server reads in a file with records as follows: + [] + +The server responds to search requests with the server address of +the PV (not the address of this directory server). + +This server can be used to cause EPICS to locate any PV listed in +the file anywhere on the internet. You must of course set EPICS_CA_ADDR_LIST +for the client so that it points at a running instance of this directory +(name resolution) server. \ No newline at end of file diff --git a/src/cas/example/directoryService/directoryServer.cc b/src/cas/example/directoryService/directoryServer.cc new file mode 100644 index 000000000..45f04b4d6 --- /dev/null +++ b/src/cas/example/directoryService/directoryServer.cc @@ -0,0 +1,169 @@ +/*************************************************************************\ +* 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 directory server +// + +#include "directoryServer.h" +#include "tsMinMax.h" + +const pvInfo *pvInfo::pFirst; + +// +// directoryServer::directoryServer() +// +directoryServer::directoryServer(const char * const pvPrefix, unsigned aliasCount) +{ + unsigned i; + const pvInfo *pPVI; + char pvAlias[256]; + const char * const pNameFmtStr = "%.100s%.20s"; + const char * const pAliasFmtStr = "%.100s%.20s%u"; + + // + // pre-create all of the simple PVs that this server will export + // + for (pPVI = pvInfo::getFirst(); pPVI; pPVI=pPVI->getNext()) { + + // + // Install canonical (root) name + // + sprintf(pvAlias, pNameFmtStr, pvPrefix, pPVI->getName()); + this->installAliasName(*pPVI, pvAlias); + + // + // Install numbered alias names + // + for (i=0u; igetName(), i); + this->installAliasName(*pPVI, pvAlias); + } + } +} + + +// +// directoryServer::~directoryServer() +// +directoryServer::~directoryServer() +{ +} + +// +// directoryServer::installAliasName() +// +void directoryServer::installAliasName(const 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 directoryServer::pvExistTest + ( const casCtx & ctx, const caNetAddr &, const char * pPVName ) +{ + return this->pvExistTest ( ctx, pPVName ); +} + +// +// directoryServer::pvExistTest() +// +pvExistReturn directoryServer::pvExistTest + ( const casCtx & /* ctx */, const char * pPVName ) +{ + // + // lifetime of id is shorter than lifetime of pName + // + char pvNameBuf[512]; + stringId id(pPVName, stringId::refString); + stringId idBuf(pvNameBuf, stringId::refString); + pvEntry *pPVE; + const char *pStr, *pLastStr; + + // + // strip trailing occurrence of ".field" + // (for compatibility with EPICS + // function block database). + // + pLastStr = pPVName; + pStr = strstr (pPVName, "."); + while (pStr) { + pLastStr = pStr; + pStr += 1; + pStr = strstr (pStr, "."); + } + + if (pLastStr==pPVName) { + pPVE = this->stringResTbl.lookup(id); + if (!pPVE) { + return pverDoesNotExistHere; + } + } + else { + size_t diff = pLastStr-pPVName; + diff = tsMin (diff, sizeof(pvNameBuf)-1); + memcpy (pvNameBuf, pPVName, diff); + pvNameBuf[diff] = '\0'; + pLastStr = pvNameBuf; + pPVE = this->stringResTbl.lookup(idBuf); + if (!pPVE) { + // + // look for entire PV name (in case this PV isnt + // associated a function block database) + // + // lifetime of id2 is shorter than lifetime of pName + // + pPVE = this->stringResTbl.lookup(id); + if (!pPVE) { + return pverDoesNotExistHere; + } + } + } + + return pvExistReturn (caNetAddr(pPVE->getInfo().getAddr())); +} + +// +// directoryServer::show() +// +void directoryServer::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); +} + diff --git a/src/cas/example/directoryService/directoryServer.h b/src/cas/example/directoryService/directoryServer.h new file mode 100644 index 000000000..01bc2dbb3 --- /dev/null +++ b/src/cas/example/directoryService/directoryServer.h @@ -0,0 +1,147 @@ +/*************************************************************************\ +* 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 directory server +// +// +// caServer +// | +// directoryServer +// +// + + +// +// ANSI C +// +#include +#include + +// +// EPICS +// +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#define caNetAddrSock +#include "casdef.h" +#include "epicsAssert.h" +#include "resourceLib.h" + + +#ifndef NELEMENTS +# define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) +#endif + +class directoryServer; + +// +// pvInfo +// +class pvInfo { +public: + pvInfo (const char *pNameIn, sockaddr_in addrIn) : + addr(addrIn), pNext(pvInfo::pFirst) + { + pvInfo::pFirst = this; + this->pName = new char [strlen(pNameIn)+1u]; + strcpy(this->pName, pNameIn); + } + + const struct sockaddr_in getAddr() const { return this->addr; } + const char *getName () const { return this->pName; } + const pvInfo *getNext () const { return this->pNext; } + static const pvInfo *getFirst () { return pvInfo::pFirst; } +private: + struct sockaddr_in addr; + char * pName; + const pvInfo * pNext; + static const pvInfo * pFirst; +}; + +// +// 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 { +public: + pvEntry (const pvInfo &infoIn, directoryServer &casIn, + const char *pAliasName) : + stringId(pAliasName), info(infoIn), cas(casIn) + { + assert(this->stringId::resourceName()!=NULL); + } + + inline ~pvEntry(); + + const pvInfo &getInfo() const { return this->info; } + + inline void destroy (); + +private: + const pvInfo &info; + directoryServer &cas; +}; + +// +// directoryServer +// +class directoryServer : public caServer { +public: + directoryServer ( const char * const pvPrefix, unsigned aliasCount ); + ~directoryServer(); + void show ( unsigned level ) const; + + void installAliasName ( const pvInfo &info, const char *pAliasName ); + void removeAliasName ( pvEntry &entry ); + +private: + resTable < pvEntry, stringId > stringResTbl; + + pvExistReturn pvExistTest ( const casCtx&, + const char *pPVName ); + pvExistReturn pvExistTest ( const casCtx&, + const caNetAddr &, const char *pPVName ); +}; + + +// +// directoryServer::removeAliasName() +// +inline void directoryServer::removeAliasName ( pvEntry & entry ) +{ + pvEntry * pE = this->stringResTbl.remove ( entry ); + assert ( pE == & entry ); +} + +// +// pvEntry::~pvEntry() +// +inline pvEntry::~pvEntry() +{ + this->cas.removeAliasName(*this); +} + +// +// pvEntry::destroy() +// +inline void pvEntry::destroy () +{ + // + // always created with new + // + delete this; +} + diff --git a/src/cas/example/directoryService/main.cc b/src/cas/example/directoryService/main.cc new file mode 100644 index 000000000..afd4f813c --- /dev/null +++ b/src/cas/example/directoryService/main.cc @@ -0,0 +1,212 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "errlog.h" + +#include "directoryServer.h" +#include "fdManager.h" + +static int parseDirectoryFile (const char *pFileName); +static int parseDirectoryFP (FILE *pf, const char *pFileName); + +#ifndef INADDR_NONE +#define INADDR_NONE (~0ul) +#endif + +// +// main() +// (example single threaded ca server tool main loop) +// +extern int main ( int argc, const char **argv ) +{ + epicsTime begin ( epicsTime::getCurrent() ); + directoryServer *pCAS; + unsigned debugLevel = 0u; + double executionTime = 0.0; + char pvPrefix[128] = ""; + char fileName[128] = "pvDirectory.txt"; + unsigned aliasCount = 0u; + bool forever = true; + int nPV; + int i; + + for (i=1; i -t -p -c -f]\n", + argv[0]); + + return (1); + } + + nPV = parseDirectoryFile (fileName); + if (nPV<=0) { + fprintf(stderr, +"No PVs in directory file=%s. Exiting because there is no useful work to do.\n", + fileName); + return (-1); + } + + try { + pCAS = new directoryServer(pvPrefix, aliasCount); + if (!pCAS) { + return (-1); + } + } + catch ( ... ) { + errlogPrintf ( "Unable to create a directory service\n" ); + exit ( -1 ); + } + + pCAS->setDebugLevel(debugLevel); + + if (forever) { + // + // loop here forever + // + while (true) { + fileDescriptorManager.process (1000.0); + } + } + else { + double delay = epicsTime::getCurrent() - begin; + // + // loop here untime the specified execution time + // expires + // + while (delay < executionTime) { + fileDescriptorManager.process (delay); + delay = epicsTime::getCurrent() - begin; + } + } + pCAS->show(2u); + delete pCAS; + return (0); +} + +// +// parseDirectoryFile() +// +// PV directory file is expected to have entries of the form: +// [] +// +// +static int parseDirectoryFile (const char *pFileName) +{ + + FILE *pf; + int nPV; + + // + // open a file that contains the PV directory + // + pf = fopen(pFileName, "r"); + if (!pf) { + fprintf(stderr, "Directory file access probems with file=\"%s\" because \"%s\"\n", + pFileName, strerror(errno)); + return (-1); + } + + nPV = parseDirectoryFP (pf, pFileName); + + fclose (pf); + + return nPV; +} + +// +// parseDirectoryFP() +// +// PV directory file is expected to have entries of the form: +// [] +// +// +static int parseDirectoryFP (FILE *pf, const char *pFileName) +{ + pvInfo *pPVI; + char pvNameStr[128]; + struct sockaddr_in ipa; + char hostNameStr[128]; + int nPV=0; + + ipa.sin_family = AF_INET; + while ( true ) { + + // + // parse the PV name entry from the file + // + if (fscanf(pf, " %127s ", pvNameStr) != 1) { + return nPV; // success + } + + // + // parse out server address + // + if (fscanf(pf, " %s ", hostNameStr) == 1) { + int status; + + status = aToIPAddr (hostNameStr, 0u, &ipa); + if (status) { + fprintf (pf, "Unknown host name=\"%s\" (or bad dotted ip addr) in \"%s\" with PV=\"%s\"?\n", + hostNameStr, pFileName, pvNameStr); + return -1; + } + } + else { + fprintf (stderr,"No host name (or dotted ip addr) after PV name in \"%s\" with PV=\"%s\"?\n", + pFileName, pvNameStr); + return -1; + } + + // + // parse out optional IP port number + // + unsigned portNumber; + if (fscanf(pf, " %u ", &portNumber) == 1) { + if (portNumber>0xffff) { + fprintf (stderr,"Port number supplied is to large in \"%s\" with PV=\"%s\" host=\"%s\" port=%u?\n", + pFileName, pvNameStr, hostNameStr, portNumber); + return -1; + } + + ipa.sin_port = htons((aitUint16) portNumber); + } + else { + ipa.sin_port = 0u; // use the default CA server port + } + + pPVI = new pvInfo ( pvNameStr, ipa ); + if ( ! pPVI ) { + fprintf (stderr, "Unable to allocate space for a new PV in \"%s\" with PV=\"%s\" host=\"%s\"\n", + pFileName, pvNameStr, hostNameStr); + return -1; + } + nPV++; + } +#if defined ( __HP_aCC ) && ( _HP_aCC <= 033300 ) + return 0; // Make HP compiler happy +#endif +} diff --git a/src/cas/example/directoryService/pvDirectory.txt b/src/cas/example/directoryService/pvDirectory.txt new file mode 100644 index 000000000..c76b9d023 --- /dev/null +++ b/src/cas/example/directoryService/pvDirectory.txt @@ -0,0 +1,5 @@ + +ca:ai_2000 myIOC.myLAB.org 22000 +ca:ai_2001 1.1.1.1 22000 +ca:ao_2000 myIOC +ca:ao_2001 1.1.1.1 diff --git a/src/cas/example/directoryService/test.adl b/src/cas/example/directoryService/test.adl new file mode 100644 index 000000000..6086a3be5 --- /dev/null +++ b/src/cas/example/directoryService/test.adl @@ -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="" +} +"<>" { + 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" + } +} +"<>" { + 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" + } +} +"<>" { + 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" +} diff --git a/src/cas/example/directoryService/vxEntry.cc b/src/cas/example/directoryService/vxEntry.cc new file mode 100644 index 000000000..7c7791073 --- /dev/null +++ b/src/cas/example/directoryService/vxEntry.cc @@ -0,0 +1,79 @@ +/*************************************************************************\ +* 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 +#include + +#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" + diff --git a/src/cas/generic/README b/src/cas/generic/README new file mode 100644 index 000000000..15366fee5 --- /dev/null +++ b/src/cas/generic/README @@ -0,0 +1,43 @@ + + +This directory contains the generic (os and io independent) source for +the EPICS ca server library + +The sub-diretory "mt" contains multi-threaded specific code and the +sub-directory "st" contains single-threaded specific code. + +Design Notes: +1) When I was preparing a thread-safe version of the server lib I assumed +that all locking required to protect gdd::reference() and gdd::unreference() +will be provided by the gdd library. + +2) The source file "templInst.cc" attempts to provide explicit instantiation +of the template member functions required following the ANSI resolution +r.14.9 in appendix A of Stroustrup's book. The problem is that Sun's +compiler does not recognize this syntax so we must "ifdef" the code until +this matter is resolved. + +Here are some of the inheritance trees used by the server lib: + + casCoreClient + | + | +inBuf outBuf casClient dgInBuf dgOutBuf +| | ________|_______ | | +| | | | | | +|___casStrmClient casDGClient______| + | | + | | + casStreamIO casDGIO + | | + | | + casStreamOS casDGOS + + + casDGIntfIO casIntfIO + | | + | | + casDGIntfOS casIntfOS + + + diff --git a/src/cas/generic/beaconAnomalyGovernor.cc b/src/cas/generic/beaconAnomalyGovernor.cc new file mode 100644 index 000000000..75b780ded --- /dev/null +++ b/src/cas/generic/beaconAnomalyGovernor.cc @@ -0,0 +1,86 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "fdManager.h" + +#define epicsExportSharedSymbols +#include "caServerI.h" +#include "beaconAnomalyGovernor.h" +#include "beaconTimer.h" + +// +// the minimum period between beacon anomalies +// ------------------------------------------- +// +// o Gateways imply a tradeoff towards less traffic while +// paying a price of a less tightly coupled environment. +// Therefore, clients should not expect instant reconnects +// when a server is rebooted on the other side of a gateway. +// +// o In a quiescent system servers should not need to be +// rebooted and network connectivity should be constant. +// +// o Clients will keep trying to reach channels for around +// 15 min after the last beacon anomaly was seen. +// +// o The exponential backoff mechanism in the beacon timer +// ensures that clients will detect beacon anomalies +// continuously for around 30 seconds after each beacon +// anomaly is requested. +// +static const double CAServerMinBeaconAnomalyPeriod = 60.0 * 5.0; // seconds + +beaconAnomalyGovernor::beaconAnomalyGovernor ( caServerI & casIn ) : + timer ( fileDescriptorManager.createTimer () ), cas ( casIn ), + anomalyPending ( false ) +{ +} + +beaconAnomalyGovernor::~beaconAnomalyGovernor() +{ +} + +void beaconAnomalyGovernor::start () +{ + // order of operations here impacts thread safety + this->anomalyPending = true; + epicsTimer::expireInfo expInfo = this->timer.getExpireInfo (); + if ( ! expInfo.active ) { + this->cas.beaconTmr.generateBeaconAnomaly (); + this->timer.start ( *this, CAServerMinBeaconAnomalyPeriod ); + this->anomalyPending = false; + } +} + +epicsTimerNotify::expireStatus beaconAnomalyGovernor::expire ( const epicsTime & ) +{ + if ( this->anomalyPending ) { + this->anomalyPending = false; + this->cas.beaconTmr.generateBeaconAnomaly (); + } + return noRestart; +} + +void beaconAnomalyGovernor::show ( unsigned level ) const +{ + printf ( "beaconAnomalyGovernor: anomalyPending = %s\n", + this->anomalyPending ? "T": "F" ); + if ( level ) { + this->timer.show ( level - 1 ); + } +} diff --git a/src/cas/generic/beaconAnomalyGovernor.h b/src/cas/generic/beaconAnomalyGovernor.h new file mode 100644 index 000000000..e11a94741 --- /dev/null +++ b/src/cas/generic/beaconAnomalyGovernor.h @@ -0,0 +1,55 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef beaconAnomalyGovernorh +#define beaconAnomalyGovernorh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_beaconAnomalyGovernorh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "epicsTimer.h" +#include "epicsMutex.h" + +#ifdef epicsExportSharedSymbols_beaconAnomalyGovernorh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class caServerI; + +class beaconAnomalyGovernor : public epicsTimerNotify { +public: + beaconAnomalyGovernor ( caServerI & ); + virtual ~beaconAnomalyGovernor(); + void start (); + void show ( unsigned level ) const; +private: + // has been checked for thread safety + epicsTimer & timer; + class caServerI & cas; + bool anomalyPending; + expireStatus expire( const epicsTime & currentTime ); + beaconAnomalyGovernor ( const beaconAnomalyGovernor & ); + beaconAnomalyGovernor & operator = ( const beaconAnomalyGovernor & ); +}; + +#endif // ifdef beaconAnomalyGovernorh + diff --git a/src/cas/generic/beaconTimer.cc b/src/cas/generic/beaconTimer.cc new file mode 100644 index 000000000..90e9440bf --- /dev/null +++ b/src/cas/generic/beaconTimer.cc @@ -0,0 +1,91 @@ + +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "fdManager.h" +#include "envDefs.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "caServerI.h" +#include "beaconTimer.h" + +// the maximum beacon period if EPICS_CA_BEACON_PERIOD isnt available +static const double CAServerMaxBeaconPeriod = 15.0; // seconds + +// the initial beacon period +static const double CAServerMinBeaconPeriod = 1.0e-3; // seconds + +beaconTimer::beaconTimer ( caServerI & casIn ) : + timer ( fileDescriptorManager.createTimer() ), + cas ( casIn ), + beaconPeriod ( CAServerMinBeaconPeriod ), + maxBeaconInterval ( CAServerMaxBeaconPeriod ), + beaconCounter ( 0U ) +{ + caStatus status; + double maxPeriod; + + if ( envGetConfigParamPtr ( & EPICS_CAS_BEACON_PERIOD ) ) { + status = envGetDoubleConfigParam ( & EPICS_CAS_BEACON_PERIOD, & maxPeriod ); + } + else { + status = envGetDoubleConfigParam ( & EPICS_CA_BEACON_PERIOD, & maxPeriod ); + } + if ( status || maxPeriod <= 0.0 ) { + errlogPrintf ( + "EPICS \"%s\" float fetch failed\n", EPICS_CAS_BEACON_PERIOD.name ); + errlogPrintf ( + "Setting \"%s\" = %f\n", EPICS_CAS_BEACON_PERIOD.name, + this->maxBeaconInterval); + } + else { + this->maxBeaconInterval = maxPeriod; + } + + this->timer.start ( *this, CAServerMinBeaconPeriod ); +} + +beaconTimer::~beaconTimer () +{ + this->timer.destroy (); +} + +void beaconTimer::generateBeaconAnomaly () +{ + this->beaconPeriod = CAServerMinBeaconPeriod; + this->timer.start ( *this, CAServerMinBeaconPeriod ); +} + + +epicsTimerNotify::expireStatus beaconTimer::expire( const epicsTime & /* currentTime */ ) +{ + this->cas.sendBeacon ( this->beaconCounter ); + + this->beaconCounter++; + + // double the period between beacons (but dont exceed max) + if ( this->beaconPeriod < this->maxBeaconInterval ) { + this->beaconPeriod += this->beaconPeriod; + + if ( this->beaconPeriod >= this->maxBeaconInterval ) { + this->beaconPeriod = this->maxBeaconInterval; + } + } + + return expireStatus ( restart, this->beaconPeriod ); +} + diff --git a/src/cas/generic/beaconTimer.h b/src/cas/generic/beaconTimer.h new file mode 100644 index 000000000..22c06d115 --- /dev/null +++ b/src/cas/generic/beaconTimer.h @@ -0,0 +1,56 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef beaconTimerh +#define beaconTimerh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_beaconTimerh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "epicsTimer.h" +#include "caProto.h" + +#ifdef epicsExportSharedSymbols_beaconTimerh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class caServerI; + +class beaconTimer : public epicsTimerNotify { +public: + beaconTimer ( caServerI & casIn ); + virtual ~beaconTimer (); + void generateBeaconAnomaly (); +private: + // has been checked for thread safety + epicsTimer & timer; + caServerI & cas; + double beaconPeriod; + double maxBeaconInterval; + ca_uint32_t beaconCounter; + expireStatus expire ( const epicsTime & currentTime ); + beaconTimer ( const beaconTimer & ); + beaconTimer & operator = ( const beaconTimer & ); +}; + +#endif // ifdef beaconTimerh + diff --git a/src/cas/generic/caHdrLargeArray.h b/src/cas/generic/caHdrLargeArray.h new file mode 100644 index 000000000..ca2fbebbb --- /dev/null +++ b/src/cas/generic/caHdrLargeArray.h @@ -0,0 +1,49 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef caHdrLargeArrayh +#define caHdrLargeArrayh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_caHdrLargeArrayh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "caProto.h" + +#ifdef epicsExportSharedSymbols_caHdrLargeArrayh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +static const unsigned char CA_MINOR_PROTOCOL_REVISION = 12; + +typedef ca_uint32_t caResId; + +/* a modified ca header with capacity for large arrays */ +struct caHdrLargeArray { + ca_uint32_t m_postsize; /* size of message extension */ + ca_uint32_t m_count; /* operation data count */ + ca_uint32_t m_cid; /* channel identifier */ + ca_uint32_t m_available; /* protocol stub dependent */ + ca_uint16_t m_dataType; /* operation data type */ + ca_uint16_t m_cmmd; /* operation to be performed */ +}; + +#endif // caHdrLargeArrayh diff --git a/src/cas/generic/caNetAddr.cc b/src/cas/generic/caNetAddr.cc new file mode 100644 index 000000000..ab4923b8b --- /dev/null +++ b/src/cas/generic/caNetAddr.cc @@ -0,0 +1,200 @@ + +/*************************************************************************\ +* 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: Jeffrey O. Hill +// johill@lanl.gov +// + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#define caNetAddrSock +#include "caNetAddr.h" + +static class caNetAddrSelfTest { +public: + caNetAddrSelfTest (); +} caNetAddrSelfTestDuringLoad; + +// +// caNetAddr::stringConvert () +// +void caNetAddr::stringConvert ( char *pString, unsigned stringLength ) const +{ + if ( this->type == casnaInet ) { + ipAddrToA (&this->addr.ip, pString, stringLength); + return; + } + if ( stringLength ) { + strncpy ( pString, "", stringLength ); + pString[stringLength-1] = '\n'; + } +} + +// +// caNetAddr::clear() +// +void caNetAddr::clear () +{ + this->type = casnaUDF; +} + +// +// caNetAddr::caNetAddr() +// +caNetAddr::caNetAddr () +{ + this->clear(); +} + +bool caNetAddr::isInet () const +{ + return this->type == casnaInet; +} + +bool caNetAddr::isValid () const +{ + return this->type != casnaUDF; +} + +bool caNetAddr::operator == (const caNetAddr &rhs) const // X aCC 361 +{ + if ( this->type != rhs.type ) { + return false; + } + if ( this->type == casnaInet ) { + return ( this->addr.ip.sin_addr.s_addr == rhs.addr.ip.sin_addr.s_addr ) && + ( this->addr.ip.sin_port == rhs.addr.ip.sin_port ); + } + else { + return false; + } +} + +bool caNetAddr::operator != ( const caNetAddr & rhs ) const +{ + return ! this->operator == (rhs); +} + +// +// This is specified so that compilers will not use one of +// the following assignment operators after converting to a +// sockaddr_in or a sockaddr first. +// +// caNetAddr caNetAddr::operator =(const struct sockaddr_in&) +// caNetAddr caNetAddr::operator =(const struct sockaddr&) +// +caNetAddr caNetAddr::operator = ( const caNetAddr &naIn ) +{ + this->addr = naIn.addr; + this->type = naIn.type; + return *this; +} + +void caNetAddr::setSockIP ( unsigned long inaIn, unsigned short portIn ) +{ + this->type = casnaInet; + this->addr.ip.sin_family = AF_INET; + this->addr.ip.sin_addr.s_addr = inaIn; + this->addr.ip.sin_port = portIn; +} + +void caNetAddr::setSockIP ( const struct sockaddr_in & sockIPIn ) +{ + if ( sockIPIn.sin_family != AF_INET ) { + throw std::logic_error ( "caNetAddr::setSockIP (): address wasnt IP" ); + } + this->type = casnaInet; + this->addr.ip = sockIPIn; +} + +void caNetAddr::setSock ( const struct sockaddr & sock ) +{ + if ( sock.sa_family != AF_INET ) { + throw std::logic_error ( "caNetAddr::setSock (): address wasnt IP" ); + } + this->type = casnaInet; + const struct sockaddr_in *psip = + reinterpret_cast ( & sock ); + this->addr.ip = *psip; +} + +caNetAddr::caNetAddr ( const struct sockaddr_in & sockIPIn ) +{ + this->setSockIP ( sockIPIn ); +} + +caNetAddr caNetAddr::operator = ( const struct sockaddr_in & sockIPIn ) +{ + this->setSockIP ( sockIPIn ); + return *this; +} + +caNetAddr caNetAddr::operator = ( const struct sockaddr & sockIn ) +{ + this->setSock (sockIn); + return *this; +} + +struct sockaddr_in caNetAddr::getSockIP() const +{ + if ( this->type != casnaInet ) { + throw std::logic_error ( "caNetAddr::getSockIP (): address wasnt IP" ); + } + return this->addr.ip; +} + +struct sockaddr caNetAddr::getSock() const +{ + if ( this->type != casnaInet ) { + throw std::logic_error ( "caNetAddr::getSock (): address wasnt IP" ); + } + + struct sockaddr sa; + struct sockaddr_in *psain = reinterpret_cast ( & sa ); + *psain = this->addr.ip; + return sa; +} + +caNetAddr::operator sockaddr_in () const +{ + return this->getSockIP (); +} + +caNetAddr::operator sockaddr () const +{ + return this->getSock (); +} + +void caNetAddr::selfTest () +{ + // the dummy field must be greater than or equal to the size of + // each of the other entries in the union + if ( sizeof ( this->addr ) != sizeof ( this->addr.pad ) ) { + fprintf ( stderr, "caNetAddr::selfTest ():self test failed in %s at line %d\n", + __FILE__, __LINE__ ); + throw std::logic_error ( "caNetAddr::selfTest (): failed self test" ); + } +} + +caNetAddrSelfTest::caNetAddrSelfTest () +{ + caNetAddr tmp; + tmp.selfTest (); +} + + diff --git a/src/cas/generic/caNetAddr.h b/src/cas/generic/caNetAddr.h new file mode 100644 index 000000000..01dc6c3ba --- /dev/null +++ b/src/cas/generic/caNetAddr.h @@ -0,0 +1,71 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// caNetAddr +// +// special cas specific network address class so: +// 1) we dont drag BSD socket headers into the server tool +// 2) we are not prevented from using other networking services +// besides sockets in the future +// + +#ifndef caNetAddrH +#define caNetAddrH + +#ifdef caNetAddrSock +# ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_caNetAddrH +# undef epicsExportSharedSymbols +# endif +# include "osiSock.h" +# ifdef epicsExportSharedSymbols_caNetAddrH +# define epicsExportSharedSymbols +# include "shareLib.h" +# endif +#else +# include "shareLib.h" +#endif + +class epicsShareClass caNetAddr { +public: + void clear (); + caNetAddr (); + bool isValid () const; + bool operator == ( const caNetAddr & rhs ) const; + bool operator != ( const caNetAddr & rhs) const; + caNetAddr operator = ( const caNetAddr & naIn ); + void stringConvert ( char *pString, unsigned stringLength ) const; + void selfTest (); + + // support for socket addresses + // (other address formats may be supported in the future) + bool isInet () const; + void setSockIP ( unsigned long inaIn, unsigned short portIn ); + void setSockIP ( const struct sockaddr_in & sockIPIn ); + void setSock ( const struct sockaddr & sock ); + caNetAddr ( const struct sockaddr_in & sockIPIn ); + caNetAddr operator = ( const struct sockaddr & sockIn ); + caNetAddr operator = ( const struct sockaddr_in & sockIPIn ); + struct sockaddr getSock() const; + struct sockaddr_in getSockIP() const; + operator struct sockaddr () const; + operator struct sockaddr_in () const; + +private: + enum caNetAddrType { casnaUDF, casnaInet } type; + union { + unsigned char pad[16]; +# ifdef caNetAddrSock + struct sockaddr_in ip; +# endif + } addr; +}; + +#endif // caNetAddrH diff --git a/src/cas/generic/caServer.cc b/src/cas/generic/caServer.cc new file mode 100644 index 000000000..c5d2d5679 --- /dev/null +++ b/src/cas/generic/caServer.cc @@ -0,0 +1,174 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "dbMapper.h" // ait to dbr types +#include "gddAppTable.h" // EPICS application type table +#include "fdManager.h" + +#define epicsExportSharedSymbols +#include "casMonitor.h" +#include "caServerI.h" + +caServer::caServer () +{ + static bool init = false; + + if ( ! init ) { + gddMakeMapDBR(gddApplicationTypeTable::app_table); + init = true; + } + + this->pCAS = new caServerI ( *this ); +} + +caServer::~caServer() +{ + if (this->pCAS) { + delete this->pCAS; + this->pCAS = NULL; + } +} + +pvExistReturn caServer::pvExistTest ( const casCtx & ctx, + const caNetAddr & /* clientAddress */, const char * pPVAliasName ) +{ + return this->pvExistTest ( ctx, pPVAliasName ); +} + +pvExistReturn caServer::pvExistTest ( const casCtx &, const char * ) +{ + return pverDoesNotExistHere; +} + +pvCreateReturn caServer::createPV ( const casCtx &, const char * ) +{ + return S_casApp_pvNotFound; +} + +pvAttachReturn caServer::pvAttach ( const casCtx &ctx, const char *pAliasName ) +{ + // remain backwards compatible (call deprecated routine) + return this->createPV ( ctx, pAliasName ); +} + +casEventMask caServer::registerEvent (const char *pName) // X aCC 361 +{ + if (this->pCAS) { + return this->pCAS->registerEvent(pName); + } + else { + casEventMask emptyMask; + printf("caServer:: no server internals attached\n"); + return emptyMask; + } +} + +void caServer::show(unsigned level) const +{ + if (this->pCAS) { + this->pCAS->show(level); + } + else { + printf("caServer:: no server internals attached\n"); + } +} + +void caServer::setDebugLevel (unsigned level) +{ + if (pCAS) { + this->pCAS->setDebugLevel(level); + } + else { + printf("caServer:: no server internals attached\n"); + } +} + +unsigned caServer::getDebugLevel () const // X aCC 361 +{ + if (pCAS) { + return this->pCAS->getDebugLevel(); + } + else { + printf("caServer:: no server internals attached\n"); + return 0u; + } +} + +casEventMask caServer::valueEventMask () const // X aCC 361 +{ + if (pCAS) { + return this->pCAS->valueEventMask(); + } + else { + printf("caServer:: no server internals attached\n"); + return casEventMask(); + } +} + +casEventMask caServer::logEventMask () const // X aCC 361 +{ + if (pCAS) { + return this->pCAS->logEventMask(); + } + else { + printf("caServer:: no server internals attached\n"); + return casEventMask(); + } +} + +casEventMask caServer::alarmEventMask () const // X aCC 361 +{ + if ( pCAS ) { + return this->pCAS->alarmEventMask (); + } + else { + printf ( "caServer:: no server internals attached\n" ); + return casEventMask (); + } +} + +class epicsTimer & caServer::createTimer () +{ + return fileDescriptorManager.createTimer (); +} + +unsigned caServer::subscriptionEventsProcessed () const // X aCC 361 +{ + if ( pCAS ) { + return this->pCAS->subscriptionEventsProcessed (); + } + else { + return 0u; + } +} + +unsigned caServer::subscriptionEventsPosted () const // X aCC 361 +{ + if ( pCAS ) { + return this->pCAS->subscriptionEventsPosted (); + } + else { + return 0u; + } +} + +void caServer::generateBeaconAnomaly () +{ + if ( pCAS ) { + this->pCAS->generateBeaconAnomaly (); + } +} diff --git a/src/cas/generic/caServerDefs.h b/src/cas/generic/caServerDefs.h new file mode 100644 index 000000000..b1799528f --- /dev/null +++ b/src/cas/generic/caServerDefs.h @@ -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. +\*************************************************************************/ + +#ifndef caServerDefsh +#define caServerDefsh + +#ifndef NULL +# define NULL 0 +#endif + +#ifndef NELEMENTS +# define NELEMENTS(array) (sizeof(array)/sizeof((array)[0])) +#endif + +#define invalidResID ( static_cast < ca_uint32_t > ( ~0UL ) ) + +void casVerifyFunc ( const char * pFile, + unsigned line, const char * pExp ); +void serverToolDebugFunc ( const char * pFile, + unsigned line, const char * pComment ); +#define serverToolDebug(COMMENT) \ +{ serverToolDebugFunc(__FILE__, __LINE__, COMMENT); } +#define casVerify(EXP) \ +{ if ((EXP)==0) casVerifyFunc(__FILE__, __LINE__, #EXP); } + +#endif // caServerDefsh diff --git a/src/cas/generic/caServerI.cc b/src/cas/generic/caServerI.cc new file mode 100644 index 000000000..0c69d5b51 --- /dev/null +++ b/src/cas/generic/caServerI.cc @@ -0,0 +1,300 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "epicsGuard.h" +#include "epicsVersion.h" +#include "errlog.h" + +#include "addrList.h" + +#define epicsExportSharedSymbols +#define caServerGlobal +#include "caServerI.h" +#include "beaconTimer.h" +#include "beaconAnomalyGovernor.h" +#include "casStreamOS.h" +#include "casIntfOS.h" + +// include a version string for POSIX systems +static const char pVersionCAS[] = + "@(#) " EPICS_VERSION_STRING + ", CA Portable Server Library " + "Date: Tue 2010-10-05 14:27:37 -0500"; + +caServerI::caServerI ( caServer & tool ) : + adapter (tool), + beaconTmr ( * new beaconTimer ( *this ) ), + beaconAnomalyGov ( * new beaconAnomalyGovernor ( *this ) ), + debugLevel ( 0u ), + nEventsProcessed ( 0u ), + nEventsPosted ( 0u ), + ioInProgressCount ( 0u ) +{ + assert ( & adapter != NULL ); + + // create predefined event types + this->valueEvent = registerEvent ( "value" ); + this->logEvent = registerEvent ( "log" ); + this->alarmEvent = registerEvent ( "alarm" ); + + this->locateInterfaces (); + + if (this->intfList.count()==0u) { + errMessage (S_cas_noInterface, + "- CA server internals init unable to continue"); + throw S_cas_noInterface; + } + + return; +} + +caServerI::~caServerI() +{ + delete & this->beaconAnomalyGov; + delete & this->beaconTmr; + + // delete all clients + while ( casStrmClient * pClient = this->clientList.get() ) { + delete pClient; + } + + casIntfOS *pIF; + while ( ( pIF = this->intfList.get() ) ) { + delete pIF; + } +} + +void caServerI::destroyClient ( casStrmClient & client ) +{ + { + epicsGuard < epicsMutex > locker ( this->mutex ); + this->clientList.remove ( client ); + } + delete & client; +} + +void caServerI::connectCB ( casIntfOS & intf ) +{ + casStreamOS * pClient = intf.newStreamClient ( *this, this->clientBufMemMgr ); + if ( pClient ) { + { + epicsGuard < epicsMutex > locker ( this->mutex ); + this->clientList.add ( *pClient ); + } + pClient->sendVersion (); + pClient->flush (); + } +} + +void casVerifyFunc ( const char * pFile, unsigned line, const char * pExp ) +{ + fprintf(stderr, "the expression \"%s\" didnt evaluate to boolean true \n", + pExp); + fprintf(stderr, + "and therefore internal problems are suspected at line %u in \"%s\"\n", + line, pFile); + fprintf(stderr, + "Please forward above text to johill@lanl.gov - thanks\n"); +} + +void serverToolDebugFunc (const char *pFile, unsigned line, const char *pComment) +{ + fprintf (stderr, +"Bad server tool response detected at line %u in \"%s\" because \"%s\"\n", + line, pFile, pComment); +} + +caStatus caServerI::attachInterface ( const caNetAddr & addrIn, + bool autoBeaconAddr, bool addConfigBeaconAddr) +{ + try { + casIntfOS * pIntf = new casIntfOS ( *this, this->clientBufMemMgr, + addrIn, autoBeaconAddr, addConfigBeaconAddr ); + + { + epicsGuard < epicsMutex > locker ( this->mutex ); + this->intfList.add ( *pIntf ); + } + } + catch ( ... ) { + return S_cas_bindFail; + } + + return S_cas_success; +} + +void caServerI::sendBeacon ( ca_uint32_t beaconNo ) +{ + epicsGuard < epicsMutex > locker ( this->mutex ); + tsDLIter < casIntfOS > iter = this->intfList.firstIter (); + while ( iter.valid () ) { + iter->sendBeacon ( beaconNo ); + iter++; + } +} + +void caServerI::generateBeaconAnomaly () +{ + this->beaconAnomalyGov.start (); +} + +void caServerI::destroyMonitor ( casMonitor & mon ) +{ + mon.~casMonitor (); + this->casMonitorFreeList.release ( & mon ); +} + +void caServerI::updateEventsPostedCounter ( unsigned nNewPosts ) +{ + epicsGuard < epicsMutex > guard ( this->diagnosticCountersMutex ); + this->nEventsPosted += nNewPosts; +} + +unsigned caServerI::subscriptionEventsPosted () const +{ + epicsGuard < epicsMutex > guard ( this->diagnosticCountersMutex ); + return this->nEventsPosted; +} + +void caServerI::incrEventsProcessedCounter () +{ + epicsGuard < epicsMutex > guard ( this->diagnosticCountersMutex ); + this->nEventsProcessed ++; +} + +unsigned caServerI::subscriptionEventsProcessed () const +{ + epicsGuard < epicsMutex > guard ( this->diagnosticCountersMutex ); + return this->nEventsProcessed; +} + +void caServerI::show (unsigned level) const +{ + int bytes_reserved; + + printf ( "Channel Access Server V%s\n", + CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ) ); + printf ( "\trevision %s\n", pVersionCAS ); + + this->mutex.show(level); + + { + epicsGuard < epicsMutex > locker ( this->mutex ); + tsDLIterConst < casStrmClient > iterCl = this->clientList.firstIter (); + while ( iterCl.valid () ) { + iterCl->show ( level ); + ++iterCl; + } + + tsDLIterConst < casIntfOS > iterIF = this->intfList.firstIter (); + while ( iterIF.valid () ) { + iterIF->casIntfOS::show ( level ); + ++iterIF; + } + } + + bytes_reserved = 0u; +#if 0 + bytes_reserved += sizeof(casClient) * + ellCount(&this->freeClientQ); + bytes_reserved += sizeof(casChannel) * + ellCount(&this->freeChanQ); + bytes_reserved += sizeof(casEventBlock) * + ellCount(&this->freeEventQ); + bytes_reserved += sizeof(casAsyncIIO) * + ellCount(&this->freePendingIO); +#endif + if (level>=1) { + printf( + "There are currently %d bytes on the server's free list\n", + bytes_reserved); +#if 0 + printf( + "%d client(s), %d channel(s), %d event(s) (monitors), and %d IO blocks\n", + ellCount(&this->freeClientQ), + ellCount(&this->freeChanQ), + ellCount(&this->freeEventQ), + ellCount(&this->freePendingIO)); +#endif + printf( + "The server's integer resource id conversion table:\n"); + } + + return; +} + +casMonitor & caServerI::casMonitorFactory ( + casChannelI & chan, caResId clientId, + const unsigned long count, const unsigned type, + const casEventMask & mask, + casMonitorCallbackInterface & cb ) +{ + casMonitor * pMon = + new ( this->casMonitorFreeList ) casMonitor + ( clientId, chan, count, type, mask, cb ); + return *pMon; +} + +void caServerI::casMonitorDestroy ( casMonitor & cm ) +{ + cm.~casMonitor (); + this->casMonitorFreeList.release ( & cm ); +} + +// +// caServerI::dumpMsg() +// +// Debug aid - print the header part of a message. +// +// dp arg allowed to be null +// +// +void caServerI::dumpMsg ( const char * pHostName, const char * pUserName, + const caHdrLargeArray * mp, const void * /* dp */, const char * pFormat, ... ) +{ + va_list theArgs; + if ( pFormat ) { + va_start ( theArgs, pFormat ); + errlogPrintf ( "CAS: " ); + errlogVprintf ( pFormat, theArgs ); + va_end ( theArgs ); + } + + fprintf ( stderr, +"CAS Request: %s on %s: cmd=%u cid=%u typ=%u cnt=%u psz=%u avail=%x\n", + pUserName, + pHostName, + mp->m_cmmd, + mp->m_cid, + mp->m_dataType, + mp->m_count, + mp->m_postsize, + mp->m_available); + + //if ( mp->m_cmmd == CA_PROTO_WRITE && mp->m_dataType == DBR_STRING && dp ) { + // errlogPrintf("CAS: The string written: %s \n", (char *)dp); + //} +} + +casEventRegistry::~casEventRegistry() +{ + this->traverse ( &casEventMaskEntry::destroy ); +} + diff --git a/src/cas/generic/caServerI.h b/src/cas/generic/caServerI.h new file mode 100644 index 000000000..5002bcaa0 --- /dev/null +++ b/src/cas/generic/caServerI.h @@ -0,0 +1,162 @@ + +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef caServerIh +#define caServerIh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_caServerIh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "tsFreeList.h" +#include "caProto.h" + +#ifdef epicsExportSharedSymbols_caServerIh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casdef.h" +#include "clientBufMemoryManager.h" +#include "casEventRegistry.h" +#include "caServerIO.h" +#include "ioBlocked.h" +#include "caServerDefs.h" + +class casStrmClient; +class beaconTimer; +class beaconAnomalyGovernor; +class casIntfOS; +class casMonitor; +class casChannelI; + +caStatus convertContainerMemberToAtomic ( class gdd & dd, + aitUint32 appType, aitUint32 elemCount ); + +class caServerI : + public caServerIO, + public ioBlockedList, + public casEventRegistry { +public: + caServerI ( caServer &tool ); + ~caServerI (); + bool roomForNewChannel() const; + unsigned getDebugLevel() const { return debugLevel; } + inline void setDebugLevel ( unsigned debugLevelIn ); + void show ( unsigned level ) const; + void destroyMonitor ( casMonitor & ); + caServer * getAdapter (); + caServer * operator -> (); + void connectCB ( casIntfOS & ); + casEventMask valueEventMask () const; // DBE_VALUE registerEvent("value") + casEventMask logEventMask () const; // DBE_LOG registerEvent("log") + casEventMask alarmEventMask () const; // DBE_ALARM registerEvent("alarm") + unsigned subscriptionEventsProcessed () const; + void incrEventsProcessedCounter (); + unsigned subscriptionEventsPosted () const; + void updateEventsPostedCounter ( unsigned nNewPosts ); + void generateBeaconAnomaly (); + casMonitor & casMonitorFactory ( casChannelI &, + caResId clientId, const unsigned long count, + const unsigned type, const casEventMask &, + class casMonitorCallbackInterface & ); + void casMonitorDestroy ( casMonitor & ); + void destroyClient ( casStrmClient & ); + static void dumpMsg ( + const char * pHostName, const char * pUserName, + const struct caHdrLargeArray * mp, const void * dp, + const char * pFormat, ... ); + bool ioIsPending () const; + void incrementIOInProgCount (); + void decrementIOInProgCount (); +private: + clientBufMemoryManager clientBufMemMgr; + tsFreeList < casMonitor, 1024 > casMonitorFreeList; + tsDLList < casStrmClient > clientList; + tsDLList < casIntfOS > intfList; + mutable epicsMutex mutex; + mutable epicsMutex diagnosticCountersMutex; + caServer & adapter; + beaconTimer & beaconTmr; + beaconAnomalyGovernor & beaconAnomalyGov; + unsigned debugLevel; + unsigned nEventsProcessed; + unsigned nEventsPosted; + unsigned ioInProgressCount; + + casEventMask valueEvent; // DBE_VALUE registerEvent("value") + casEventMask logEvent; // DBE_LOG registerEvent("log") + casEventMask alarmEvent; // DBE_ALARM registerEvent("alarm") + + caStatus attachInterface ( const caNetAddr & addr, bool autoBeaconAddr, + bool addConfigAddr ); + + void sendBeacon ( ca_uint32_t beaconNo ); + + caServerI ( const caServerI & ); + caServerI & operator = ( const caServerI & ); + + friend class beaconAnomalyGovernor; + friend class beaconTimer; +}; + + +inline caServer * caServerI::getAdapter() +{ + return & this->adapter; +} + +inline caServer * caServerI::operator -> () +{ + return this->getAdapter(); +} + +inline void caServerI::setDebugLevel(unsigned debugLevelIn) +{ + this->debugLevel = debugLevelIn; +} + +inline casEventMask caServerI::valueEventMask() const +{ + return this->valueEvent; +} + +inline casEventMask caServerI::logEventMask() const +{ + return this->logEvent; +} + +inline casEventMask caServerI::alarmEventMask() const +{ + return this->alarmEvent; +} + +inline bool caServerI :: ioIsPending () const +{ + return ( ioInProgressCount > 0u ); +} + +inline void caServerI :: incrementIOInProgCount () +{ + assert ( ioInProgressCount < UINT_MAX ); + ioInProgressCount++; +} + +inline void caServerI :: decrementIOInProgCount () +{ + assert ( ioInProgressCount > 0 ); + ioInProgressCount--; + this->ioBlockedList::signal (); +} + +#endif // caServerIh diff --git a/src/cas/generic/casAddr.h b/src/cas/generic/casAddr.h new file mode 100644 index 000000000..93ee6f9d9 --- /dev/null +++ b/src/cas/generic/casAddr.h @@ -0,0 +1,17 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef includeCASAddrH +#define includeCASAddrH + +#include "osiSock.h" + +#endif // includeCASAddrH + diff --git a/src/cas/generic/casAsyncIOI.cc b/src/cas/generic/casAsyncIOI.cc new file mode 100644 index 000000000..95270a02b --- /dev/null +++ b/src/cas/generic/casAsyncIOI.cc @@ -0,0 +1,127 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casAsyncIOI.h" + +casAsyncIOI::casAsyncIOI ( const casCtx & ctx ) : + client ( *ctx.getClient() ), inTheEventQueue ( false ), + posted ( false ), ioComplete ( false ) +{ + // + // catch situation where they create more than one + // async IO object per request + // + if ( ! client.okToStartAsynchIO () ) { + throw std::logic_error ( + "server tool attempted to " + "start duplicate asynchronous IO" ); + } +} + +// +// ways this gets destroyed: +// 1) io completes, this is pulled off the queue, and result +// is sent to the client +// 2) client, channel, or PV is deleted +// 3) server tool deletes the casAsyncXxxxIO obj +// +// Case 1) => normal completion +// +// Case 2) +// If the server deletes the channel or the PV then the +// client will get a disconnect msg for the channel +// involved and this will cause the io call back +// to be called with a disconnect error code. +// Therefore we dont need to force an IO canceled +// response here. +// +// Case 3) +// If for any reason the server tool needs to cancel an IO +// operation then it should post io completion with status +// S_casApp_canceledAsyncIO. Deleting the asyncronous io +// object prior to its being allowed to forward an IO termination +// message to the client will result in NO IO CALL BACK TO THE +// CLIENT PROGRAM (in this situation a warning message will be printed by +// the server lib). +// +casAsyncIOI::~casAsyncIOI() +{ + // + // pulls itself out of the event queue + // if it is installed there. + // + this->client.removeFromEventQueue ( *this, this->inTheEventQueue ); +} + +// +// o called when IO completion event reaches top of event queue +// o clients lock is applied when calling this +// +caStatus casAsyncIOI::cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > & clientGuard, + epicsGuard < evSysMutex > & ) +{ + caStatus status = S_cas_success; + { + this->inTheEventQueue = false; + + status = this->cbFuncAsyncIO ( clientGuard ); + if ( status == S_cas_sendBlocked ) { + // + // causes this op to be pushed back on the queue + // + this->inTheEventQueue = true; + return status; + } + else if ( status != S_cas_success ) { + errMessage ( status, "Asynch IO completion failed" ); + } + + this->ioComplete = true; + } + + // dont use "this" after destroying the object here + delete this; + + return S_cas_success; +} + +caStatus casAsyncIOI::insertEventQueue () +{ + // + // place this event in the event queue + // o this also signals the event consumer + // o clients lock protects list and flag + // + return this->client.addToEventQueue ( *this, this->inTheEventQueue, this->posted ); +} + +caServer *casAsyncIOI::getCAS() const +{ + return this->client.getCAS().getAdapter(); +} + +bool casAsyncIOI::oneShotReadOP() const +{ + return false; +} diff --git a/src/cas/generic/casAsyncIOI.h b/src/cas/generic/casAsyncIOI.h new file mode 100644 index 000000000..8d88e9ee8 --- /dev/null +++ b/src/cas/generic/casAsyncIOI.h @@ -0,0 +1,68 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casAsyncIOIh +#define casAsyncIOIh + +#include "casEvent.h" +#include "caHdrLargeArray.h" +#include "casCoreClient.h" + +class casAsyncIOI : + public tsDLNode < casAsyncIOI >, + public casEvent { +public: + casAsyncIOI ( const casCtx & ctx ); + epicsShareFunc virtual ~casAsyncIOI (); + caServer * getCAS () const; + virtual bool oneShotReadOP () const; + void removeFromEventQueue (); +protected: + caStatus insertEventQueue (); + casCoreClient & client; +private: + bool inTheEventQueue; + bool posted; + bool ioComplete; + + // + // casEvent virtual call back function + // (called when IO completion event reaches top of event queue) + // + epicsShareFunc caStatus cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ); + + // + // derived class specific call back + // (called when IO completion event reaches top of event queue) + // + epicsShareFunc virtual caStatus cbFuncAsyncIO ( + epicsGuard < casClientMutex > & ) = 0; + + casAsyncIOI ( const casAsyncIOI & ); + casAsyncIOI & operator = ( const casAsyncIOI & ); +}; + +inline void casAsyncIOI::removeFromEventQueue () +{ + this->client.removeFromEventQueue ( *this, this->inTheEventQueue ); +} + +#endif // casAsyncIOIh diff --git a/src/cas/generic/casAsyncPVAttachIO.cc b/src/cas/generic/casAsyncPVAttachIO.cc new file mode 100644 index 000000000..df1da8847 --- /dev/null +++ b/src/cas/generic/casAsyncPVAttachIO.cc @@ -0,0 +1,66 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "casdef.h" +#include "casAsyncPVAttachIOI.h" + +casAsyncPVAttachIO::casAsyncPVAttachIO ( const casCtx &ctx ) : + pAsyncPVAttachIOI ( new casAsyncPVAttachIOI ( *this, ctx ) ) +{ +} + +void casAsyncPVAttachIO::serverInitiatedDestroy () +{ + this->pAsyncPVAttachIOI = 0; + this->destroy (); +} + +casAsyncPVAttachIO::~casAsyncPVAttachIO () +{ + if ( this->pAsyncPVAttachIOI ) { + throw std::logic_error ( + "the server library *must* initiate asynchronous IO destroy" ); + } +} + +caStatus casAsyncPVAttachIO::postIOCompletion ( const pvAttachReturn & retValIn ) +{ + if ( this->pAsyncPVAttachIOI ) { + return this->pAsyncPVAttachIOI->postIOCompletion ( retValIn ); + } + else { + return S_cas_redundantPost; + } +} + +void casAsyncPVAttachIO::destroy () +{ + delete this; +} + +casAsyncPVCreateIO::casAsyncPVCreateIO ( const casCtx & ctx ) : + casAsyncPVAttachIO ( ctx ) +{ +} + +casAsyncPVCreateIO::~casAsyncPVCreateIO () +{ +} diff --git a/src/cas/generic/casAsyncPVAttachIOI.cpp b/src/cas/generic/casAsyncPVAttachIOI.cpp new file mode 100644 index 000000000..5b58f696e --- /dev/null +++ b/src/cas/generic/casAsyncPVAttachIOI.cpp @@ -0,0 +1,69 @@ + +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casAsyncPVAttachIOI.h" + +casAsyncPVAttachIOI::casAsyncPVAttachIOI ( + casAsyncPVAttachIO & intf, const casCtx & ctx ) : + casAsyncIOI ( ctx ), msg ( *ctx.getMsg() ), + asyncPVAttachIO ( intf ), retVal ( S_cas_badParameter ) +{ + ctx.getServer()->incrementIOInProgCount (); + ctx.getClient()->installAsynchIO ( *this ); +} + +caStatus casAsyncPVAttachIOI::postIOCompletion ( const pvAttachReturn & retValIn ) +{ + this->retVal = retValIn; + return this->insertEventQueue (); +} + +caStatus casAsyncPVAttachIOI::cbFuncAsyncIO ( + epicsGuard < casClientMutex > & guard ) +{ + caStatus status; + + // uninstall here in case the channel is deleted + // further down the call stack + this->client.uninstallAsynchIO ( *this ); + this->client.getCAS().decrementIOInProgCount (); + + if ( this->msg.m_cmmd == CA_PROTO_CREATE_CHAN ) { + casCtx tmpCtx; + tmpCtx.setMsg ( this->msg, 0 ); + tmpCtx.setServer ( & this->client.getCAS() ); + tmpCtx.setClient ( & this->client ); + status = this->client.createChanResponse ( guard, + tmpCtx, this->retVal ); + } + else { + errPrintf ( S_cas_invalidAsynchIO, __FILE__, __LINE__, + " - client request type = %u", this->msg.m_cmmd ); + status = S_cas_invalidAsynchIO; + } + + if ( status == S_cas_sendBlocked ) { + this->client.getCAS().incrementIOInProgCount (); + this->client.installAsynchIO ( *this ); + } + + return status; +} + diff --git a/src/cas/generic/casAsyncPVAttachIOI.h b/src/cas/generic/casAsyncPVAttachIOI.h new file mode 100644 index 000000000..3425ec68e --- /dev/null +++ b/src/cas/generic/casAsyncPVAttachIOI.h @@ -0,0 +1,45 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casAsyncPVAttachIOIh +#define casAsyncPVAttachIOIh + +#include "casAsyncIOI.h" + +class casAsyncPVAttachIOI : public casAsyncIOI { +public: + casAsyncPVAttachIOI ( casAsyncPVAttachIO &, const casCtx & ctx ); + ~casAsyncPVAttachIOI (); + caStatus postIOCompletion ( const pvAttachReturn & retValIn ); + caServer *getCAS () const; +private: + caHdrLargeArray const msg; + class casAsyncPVAttachIO & asyncPVAttachIO; + pvAttachReturn retVal; + + caStatus cbFuncAsyncIO ( epicsGuard < casClientMutex > & ); + casAsyncPVAttachIOI ( const casAsyncPVAttachIOI & ); + casAsyncPVAttachIOI & operator = ( const casAsyncPVAttachIOI & ); +}; + +inline casAsyncPVAttachIOI::~casAsyncPVAttachIOI () +{ + this->asyncPVAttachIO.serverInitiatedDestroy (); +} + +#endif // casAsyncPVAttachIOIh diff --git a/src/cas/generic/casAsyncPVExistIO.cc b/src/cas/generic/casAsyncPVExistIO.cc new file mode 100644 index 000000000..8d92b85e0 --- /dev/null +++ b/src/cas/generic/casAsyncPVExistIO.cc @@ -0,0 +1,56 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "casdef.h" +#include "casAsyncPVExistIOI.h" + +casAsyncPVExistIO::casAsyncPVExistIO ( const casCtx & ctx ) : + pAsyncPVExistIOI ( new casAsyncPVExistIOI ( *this, ctx ) ) {} + +void casAsyncPVExistIO::serverInitiatedDestroy () +{ + this->pAsyncPVExistIOI = 0; + this->destroy (); +} + +casAsyncPVExistIO::~casAsyncPVExistIO () +{ + if ( this->pAsyncPVExistIOI ) { + throw std::logic_error ( + "the server library *must* initiate asynchronous IO destroy" ); + } +} + +caStatus casAsyncPVExistIO::postIOCompletion ( const pvExistReturn & retValIn ) +{ + if ( this->pAsyncPVExistIOI ) { + return this->pAsyncPVExistIOI->postIOCompletion ( retValIn ); + } + else { + return S_cas_redundantPost; + } +} + +void casAsyncPVExistIO::destroy () +{ + delete this; +} + diff --git a/src/cas/generic/casAsyncPVExistIOI.cpp b/src/cas/generic/casAsyncPVExistIOI.cpp new file mode 100644 index 000000000..423f9b317 --- /dev/null +++ b/src/cas/generic/casAsyncPVExistIOI.cpp @@ -0,0 +1,70 @@ + +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casAsyncPVExistIOI.h" + +casAsyncPVExistIOI::casAsyncPVExistIOI ( + casAsyncPVExistIO & intf, const casCtx & ctx ) : + casAsyncIOI ( ctx ), + msg ( *ctx.getMsg() ), + asyncPVExistIO ( intf ), + retVal ( pverDoesNotExistHere ), + dgOutAddr ( ctx.getClient()->fetchLastRecvAddr () ), + protocolRevision ( ctx.getClient()->protocolRevision () ), + sequenceNumber ( ctx.getClient()->datagramSequenceNumber () ) +{ + ctx.getServer()->incrementIOInProgCount (); + ctx.getClient()->installAsynchIO ( *this ); +} + +caStatus casAsyncPVExistIOI::postIOCompletion ( + const pvExistReturn & retValIn ) +{ + this->retVal = retValIn; + return this->insertEventQueue (); +} + +caStatus casAsyncPVExistIOI::cbFuncAsyncIO ( + epicsGuard < casClientMutex > & guard ) +{ + caStatus status; + + if ( this->msg.m_cmmd == CA_PROTO_SEARCH ) { + // + // pass output DG address parameters + // + status = this->client.asyncSearchResponse ( + guard, this->dgOutAddr, this->msg, this->retVal, + this->protocolRevision, this->sequenceNumber ); + } + else { + errPrintf ( S_cas_invalidAsynchIO, __FILE__, __LINE__, + " - client request type = %u", this->msg.m_cmmd ); + status = S_cas_invalidAsynchIO; + } + + if ( status != S_cas_sendBlocked ) { + this->client.uninstallAsynchIO ( *this ); + this->client.getCAS().decrementIOInProgCount (); + } + + return status; +} + diff --git a/src/cas/generic/casAsyncPVExistIOI.h b/src/cas/generic/casAsyncPVExistIOI.h new file mode 100644 index 000000000..ed9bfcae8 --- /dev/null +++ b/src/cas/generic/casAsyncPVExistIOI.h @@ -0,0 +1,48 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casAsyncPVExistIOIh +#define casAsyncPVExistIOIh + +#include "casAsyncIOI.h" + +class casAsyncPVExistIOI : public casAsyncIOI { +public: + casAsyncPVExistIOI ( casAsyncPVExistIO &, const casCtx & ctx ); + ~casAsyncPVExistIOI (); + caStatus postIOCompletion ( const pvExistReturn & retValIn ); + caServer * getCAS() const; +private: + caHdrLargeArray const msg; + class casAsyncPVExistIO & asyncPVExistIO; + pvExistReturn retVal; + const caNetAddr dgOutAddr; + const ca_uint16_t protocolRevision; + const ca_uint32_t sequenceNumber; + + caStatus cbFuncAsyncIO ( epicsGuard < casClientMutex > & ); + casAsyncPVExistIOI ( const casAsyncPVExistIOI & ); + casAsyncPVExistIOI & operator = ( const casAsyncPVExistIOI & ); +}; + +inline casAsyncPVExistIOI::~casAsyncPVExistIOI () +{ + this->asyncPVExistIO.serverInitiatedDestroy (); +} + +#endif // casAsyncPVExistIOIh diff --git a/src/cas/generic/casAsyncReadIO.cc b/src/cas/generic/casAsyncReadIO.cc new file mode 100644 index 000000000..d3b0598a2 --- /dev/null +++ b/src/cas/generic/casAsyncReadIO.cc @@ -0,0 +1,60 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "casdef.h" +#include "casAsyncReadIOI.h" + +casAsyncReadIO::casAsyncReadIO ( const casCtx & ctx ) : + pAsyncReadIOI ( new casAsyncReadIOI ( *this, ctx ) ) {} + +void casAsyncReadIO::serverInitiatedDestroy () +{ + this->pAsyncReadIOI = 0; + this->destroy (); +} + +casAsyncReadIO::~casAsyncReadIO () +{ + if ( this->pAsyncReadIOI ) { + throw std::logic_error ( + "the server library *must* initiate asynchronous IO destroy" ); + } +} + +caStatus casAsyncReadIO::postIOCompletion ( + caStatus completionStatusIn, const gdd & valueRead ) +{ + if ( this->pAsyncReadIOI ) { + return this->pAsyncReadIOI->postIOCompletion ( + completionStatusIn, valueRead ); + } + else { + return S_cas_redundantPost; + } +} + +void casAsyncReadIO::destroy () +{ + delete this; +} + + + diff --git a/src/cas/generic/casAsyncReadIOI.cc b/src/cas/generic/casAsyncReadIOI.cc new file mode 100644 index 000000000..4c36434f8 --- /dev/null +++ b/src/cas/generic/casAsyncReadIOI.cc @@ -0,0 +1,109 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casAsyncReadIOI.h" +#include "casChannelI.h" + +casAsyncReadIOI::casAsyncReadIOI ( + casAsyncReadIO & intf, const casCtx & ctx ) : + casAsyncIOI ( ctx ), msg ( *ctx.getMsg() ), + asyncReadIO ( intf ), chan ( *ctx.getChannel () ), + pDD ( NULL ), completionStatus ( S_cas_internal ) +{ + this->chan.installIO ( *this ); +} + +casAsyncReadIOI::~casAsyncReadIOI () +{ + this->asyncReadIO.serverInitiatedDestroy (); +} + +caStatus casAsyncReadIOI::postIOCompletion ( + caStatus completionStatusIn, const gdd & valueRead ) +{ + this->pDD = & valueRead; + this->completionStatus = completionStatusIn; + return this->insertEventQueue (); +} + +bool casAsyncReadIOI::oneShotReadOP () const +{ + return true; // it is a read op +} + +caStatus casAsyncReadIOI::cbFuncAsyncIO ( + epicsGuard < casClientMutex > & guard ) +{ + caStatus status; + + // uninstall the io early on to prevent a channel delete from + // destroying this object twice + this->chan.uninstallIO ( *this ); + + switch ( this->msg.m_cmmd ) { + case CA_PROTO_READ: + status = client.readResponse ( + guard, & this->chan, this->msg, + * this->pDD, this->completionStatus ); + break; + + case CA_PROTO_READ_NOTIFY: + status = client.readNotifyResponse ( + guard, & this->chan, this->msg, * this->pDD, + this->completionStatus ); + break; + + case CA_PROTO_EVENT_ADD: + status = client.monitorResponse ( + guard, this->chan, this->msg, * this->pDD, + this->completionStatus ); + break; + + case CA_PROTO_CREATE_CHAN: + // we end up here if the channel claim protocol response is delayed + // by an asynchronous enum string table fetch response + status = client.enumPostponedCreateChanResponse ( + guard, this->chan, this->msg ); + if ( status == S_cas_success ) { + if ( this->completionStatus == S_cas_success && this->pDD.valid() ) { + this->chan.getPVI().updateEnumStringTableAsyncCompletion ( *this->pDD ); + } + else { + errMessage ( this->completionStatus, + "unable to read application type \"enums\" string" + " conversion table for enumerated PV" ); + } + } + break; + + default: + errPrintf ( S_cas_invalidAsynchIO, __FILE__, __LINE__, + " - client request type = %u", this->msg.m_cmmd ); + status = S_cas_invalidAsynchIO; + break; + } + + if ( status == S_cas_sendBlocked ) { + this->chan.installIO ( *this ); + } + + return status; +} + + diff --git a/src/cas/generic/casAsyncReadIOI.h b/src/cas/generic/casAsyncReadIOI.h new file mode 100644 index 000000000..b97b5f380 --- /dev/null +++ b/src/cas/generic/casAsyncReadIOI.h @@ -0,0 +1,47 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casAsyncReadIOIh +#define casAsyncReadIOIh + +#include "casAsyncIOI.h" +#include "casdef.h" + +class gdd; + +class casAsyncReadIOI : public casAsyncIOI { +public: + casAsyncReadIOI ( casAsyncReadIO &, const casCtx & ctx ); + ~casAsyncReadIOI (); + caStatus postIOCompletion ( + caStatus completionStatusIn, const gdd &valueRead ); + caServer *getCAS () const; +private: + caHdrLargeArray const msg; + class casAsyncReadIO & asyncReadIO; + class casChannelI & chan; + smartConstGDDPointer pDD; + caStatus completionStatus; + epicsShareFunc bool oneShotReadOP () const; + epicsShareFunc caStatus cbFuncAsyncIO ( + epicsGuard < casClientMutex > & ); + casAsyncReadIOI ( const casAsyncReadIOI & ); + casAsyncReadIOI & operator = ( const casAsyncReadIOI & ); +}; + +#endif // casAsyncReadIOIh diff --git a/src/cas/generic/casAsyncWriteIO.cc b/src/cas/generic/casAsyncWriteIO.cc new file mode 100644 index 000000000..48512934d --- /dev/null +++ b/src/cas/generic/casAsyncWriteIO.cc @@ -0,0 +1,57 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "casdef.h" +#include "casAsyncWriteIOI.h" + +casAsyncWriteIO::casAsyncWriteIO ( const casCtx & ctx ) : + pAsyncWriteIOI ( new casAsyncWriteIOI ( *this, ctx ) ) +{ +} + +void casAsyncWriteIO::serverInitiatedDestroy () +{ + this->pAsyncWriteIOI = 0; + this->destroy (); +} + +casAsyncWriteIO::~casAsyncWriteIO() +{ + if ( this->pAsyncWriteIOI ) { + throw std::logic_error ( + "the server library *must* initiate asynchronous IO destroy" ); + } +} + +caStatus casAsyncWriteIO::postIOCompletion ( caStatus completionStatusIn ) +{ + if ( this->pAsyncWriteIOI ) { + return this->pAsyncWriteIOI->postIOCompletion ( completionStatusIn ); + } + else { + return S_cas_redundantPost; + } +} + +void casAsyncWriteIO::destroy () +{ + delete this; +} diff --git a/src/cas/generic/casAsyncWriteIOI.cpp b/src/cas/generic/casAsyncWriteIOI.cpp new file mode 100644 index 000000000..ce6256274 --- /dev/null +++ b/src/cas/generic/casAsyncWriteIOI.cpp @@ -0,0 +1,71 @@ + +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casAsyncWriteIOI.h" +#include "casChannelI.h" + +casAsyncWriteIOI::casAsyncWriteIOI ( + casAsyncWriteIO & intf, const casCtx & ctx ) : + casAsyncIOI ( ctx ), + msg ( *ctx.getMsg() ), + asyncWriteIO ( intf ), + chan ( *ctx.getChannel() ), + completionStatus ( S_cas_internal ) +{ + this->chan.installIO ( *this ); +} + +caStatus casAsyncWriteIOI::postIOCompletion ( caStatus completionStatusIn ) +{ + this->completionStatus = completionStatusIn; + return this->insertEventQueue (); +} + +caStatus casAsyncWriteIOI::cbFuncAsyncIO ( + epicsGuard < casClientMutex > & guard ) +{ + caStatus status; + + switch ( this->msg.m_cmmd ) { + case CA_PROTO_WRITE: + status = client.writeResponse ( guard, this->chan, + this->msg, this->completionStatus ); + break; + + case CA_PROTO_WRITE_NOTIFY: + status = client.writeNotifyResponse ( guard, this->chan, + this->msg, this->completionStatus ); + break; + + default: + errPrintf ( S_cas_invalidAsynchIO, __FILE__, __LINE__, + " - client request type = %u", this->msg.m_cmmd ); + status = S_cas_invalidAsynchIO; + break; + } + + if ( status != S_cas_sendBlocked ) { + this->chan.uninstallIO ( *this ); + } + + return status; +} + + diff --git a/src/cas/generic/casAsyncWriteIOI.h b/src/cas/generic/casAsyncWriteIOI.h new file mode 100644 index 000000000..aabcd25ba --- /dev/null +++ b/src/cas/generic/casAsyncWriteIOI.h @@ -0,0 +1,46 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casAsyncWriteIOIh +#define casAsyncWriteIOIh + +#include "casAsyncIOI.h" + +class casAsyncWriteIOI : public casAsyncIOI { +public: + casAsyncWriteIOI ( casAsyncWriteIO &, const casCtx & ctx ); + virtual ~casAsyncWriteIOI (); + caStatus postIOCompletion ( caStatus completionStatusIn ); + caServer * getCAS () const; +private: + caHdrLargeArray const msg; + class casAsyncWriteIO & asyncWriteIO; + class casChannelI & chan; + caStatus completionStatus; + caStatus cbFuncAsyncIO ( + epicsGuard < casClientMutex > & ); + casAsyncWriteIOI ( const casAsyncWriteIOI & ); + casAsyncWriteIOI & operator = ( const casAsyncWriteIOI & ); +}; + +inline casAsyncWriteIOI::~casAsyncWriteIOI () +{ + this->asyncWriteIO.serverInitiatedDestroy (); +} + +#endif // casAsyncWriteIOIh diff --git a/src/cas/generic/casBufferFactory.cpp b/src/cas/generic/casBufferFactory.cpp new file mode 100644 index 000000000..8f6a3b64f --- /dev/null +++ b/src/cas/generic/casBufferFactory.cpp @@ -0,0 +1,106 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#include "envDefs.h" +#include "freeList.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "clientBufMemoryManager.h" +#include "caProto.h" + +casBufferFactory::casBufferFactory () : + smallBufFreeList ( 0 ), largeBufFreeList ( 0 ), largeBufferSizePriv ( 0u ) +{ + long maxBytesAsALong; + long status = envGetLongConfigParam ( & EPICS_CA_MAX_ARRAY_BYTES, & maxBytesAsALong ); + if ( status || maxBytesAsALong < 0 ) { + errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); + this->largeBufferSizePriv = MAX_TCP; + } + else { + /* allow room for the protocol header so that they get the array size they requested */ + static const unsigned headerSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); + ca_uint32_t maxBytes = ( unsigned ) maxBytesAsALong; + if ( maxBytes < 0xffffffff - headerSize ) { + maxBytes += headerSize; + } + else { + maxBytes = 0xffffffff; + } + if ( maxBytes < MAX_TCP ) { + errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); + this->largeBufferSizePriv = MAX_TCP; + } + else { + this->largeBufferSizePriv = maxBytes; + } + } + + freeListInitPvt ( & this->smallBufFreeList, MAX_MSG_SIZE, 8 ); + freeListInitPvt ( & this->largeBufFreeList, this->largeBufferSizePriv, 1 ); +} + +casBufferFactory::~casBufferFactory () +{ + freeListCleanup ( this->smallBufFreeList ); + freeListCleanup ( this->largeBufFreeList ); +} + +unsigned casBufferFactory::smallBufferSize () const +{ + return MAX_MSG_SIZE; +} + +char * casBufferFactory::newSmallBuffer () +{ + void * pBuf = freeListCalloc ( this->smallBufFreeList ); + if ( ! pBuf ) { + throw std::bad_alloc(); + } + return static_cast < char * > ( pBuf ); +} + +void casBufferFactory::destroySmallBuffer ( char * pBuf ) +{ + if ( pBuf ) { + freeListFree ( this->smallBufFreeList, pBuf ); + } +} + +unsigned casBufferFactory::largeBufferSize () const +{ + return this->largeBufferSizePriv; +} + +char * casBufferFactory::newLargeBuffer () +{ + void * pBuf = freeListCalloc ( this->largeBufFreeList ); + if ( ! pBuf ) { + throw std::bad_alloc(); + } + return static_cast < char * > ( pBuf ); +} + +void casBufferFactory::destroyLargeBuffer ( char * pBuf ) +{ + if ( pBuf ) { + freeListFree ( this->largeBufFreeList, pBuf ); + } +} diff --git a/src/cas/generic/casChannel.cc b/src/cas/generic/casChannel.cc new file mode 100644 index 000000000..45b0a9553 --- /dev/null +++ b/src/cas/generic/casChannel.cc @@ -0,0 +1,121 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#define epicsExportSharedSymbols +#include "casdef.h" +#include "casChannelI.h" + +casChannel::casChannel ( const casCtx & /* ctx */ ) : + pChanI ( 0 ) +{ +} + +casChannel::~casChannel () +{ + if ( this->pChanI ) { + this->pChanI->casChannelDestroyFromInterfaceNotify (); + } +} + +void casChannel::destroyRequest () +{ + this->pChanI = 0; + this->destroy (); +} + +casPV * casChannel::getPV () // X aCC 361 +{ + if ( this->pChanI ) { + casPVI & pvi = this->pChanI->getPVI (); + return pvi.apiPointer (); + } + else { + return 0; + } +} + +void casChannel::setOwner ( const char * const /* pUserName */, + const char * const /* pHostName */ ) +{ + // + // NOOP + // +} + +bool casChannel::readAccess () const +{ + return true; +} + +bool casChannel::writeAccess () const +{ + return true; +} + +bool casChannel::confirmationRequested () const +{ + return false; +} + +caStatus casChannel::beginTransaction () +{ + return S_casApp_success; +} + +void casChannel::endTransaction () +{ +} + +caStatus casChannel::read ( const casCtx & ctx, gdd & prototype ) +{ + return ctx.getPV()->read ( ctx, prototype ); +} + +caStatus casChannel::write ( const casCtx & ctx, const gdd & value ) +{ + return ctx.getPV()->write ( ctx, value ); +} + +caStatus casChannel::writeNotify ( const casCtx & ctx, const gdd & value ) +{ + return ctx.getPV()->writeNotify ( ctx, value ); +} + +void casChannel::show ( unsigned level ) const +{ + if ( level > 2u ) { + printf ( "casChannel: read access = %d\n", + this->readAccess() ); + printf ( "casChannel: write access = %d\n", + this->writeAccess() ); + printf ( "casChannel: confirmation requested = %d\n", + this->confirmationRequested() ); + } +} + +void casChannel::destroy () +{ + delete this; +} + +void casChannel::postAccessRightsEvent () +{ + if ( this->pChanI ) { + this->pChanI->postAccessRightsEvent (); + } +} + diff --git a/src/cas/generic/casChannelI.cc b/src/cas/generic/casChannelI.cc new file mode 100644 index 000000000..f9e3960ce --- /dev/null +++ b/src/cas/generic/casChannelI.cc @@ -0,0 +1,121 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#define epicsExportSharedSymbols +#include "casChannelI.h" +#include "casAsyncIOI.h" + +casChannelI::casChannelI ( casCoreClient & clientIn, + casChannel & chanIn, casPVI & pvIn, ca_uint32_t cidIn ) : + privateForPV ( clientIn, *this ), + pv ( pvIn ), + chan ( chanIn ), + cid ( cidIn ), + serverDeletePending ( false ), + accessRightsEvPending ( false ) +{ +} + +casChannelI::~casChannelI () +{ + this->privateForPV.client().removeFromEventQueue ( + *this, this->accessRightsEvPending ); + + this->pv.destroyAllIO ( this->ioList ); + + this->serverDeletePending = true; + this->chan.destroyRequest (); + + // force PV delete if this is the last channel attached + this->pv.deleteSignal (); +} + +void casChannelI::uninstallFromPV ( casEventSys & eventSys ) +{ + tsDLList < casMonitor > dest; + this->privateForPV.removeSelfFromPV ( this->pv, dest ); + while ( casMonitor * pMon = dest.get () ) { + eventSys.prepareMonitorForDestroy ( *pMon ); + } +} + +void casChannelI::show ( unsigned level ) const +{ + printf ( "casChannelI: client id %u PV %s\n", + this->cid, this->pv.getName() ); + if ( level > 0 ) { + this->privateForPV.show ( level - 1 ); + this->chan.show ( level - 1 ); + } +} + +caStatus casChannelI::cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > & clientGuard, + epicsGuard < evSysMutex > & ) +{ + caStatus stat = S_cas_success; + { + stat = this->privateForPV.client().accessRightsResponse ( + clientGuard, this ); + } + if ( stat == S_cas_success ) { + this->accessRightsEvPending = false; + } + return stat; +} + +caStatus casChannelI::read ( const casCtx & ctx, gdd & prototype ) +{ + caStatus status = this->chan.beginTransaction (); + if ( status != S_casApp_success ) { + return status; + } + status = this->chan.read ( ctx, prototype ); + this->chan.endTransaction (); + return status; +} + +caStatus casChannelI::write ( const casCtx & ctx, const gdd & value ) +{ + caStatus status = this->chan.beginTransaction (); + if ( status != S_casApp_success ) { + return status; + } + status = this->chan.write ( ctx, value ); + this->chan.endTransaction (); + return status; +} + +caStatus casChannelI::writeNotify ( const casCtx & ctx, const gdd & value ) +{ + caStatus status = this->chan.beginTransaction (); + if ( status != S_casApp_success ) { + return status; + } + status = this->chan.writeNotify ( ctx, value ); + this->chan.endTransaction (); + return status; +} + +void casChannelI::postDestroyEvent () +{ + if ( ! this->serverDeletePending ) { + this->privateForPV.client().casChannelDestroyFromInterfaceNotify ( + *this, false ); + } +} diff --git a/src/cas/generic/casChannelI.h b/src/cas/generic/casChannelI.h new file mode 100644 index 000000000..1212ca8c5 --- /dev/null +++ b/src/cas/generic/casChannelI.h @@ -0,0 +1,162 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casChannelIh +#define casChannelIh + +#include "casPVI.h" +#include "casEvent.h" +#include "chanIntfForPV.h" +#include "casCoreClient.h" + +class casMonitor; +class casAsyncIOI; + +class casChannelI : public tsDLNode < casChannelI >, + public chronIntIdRes < casChannelI >, public casEvent, + private casChannelDestroyFromPV { +public: + casChannelI ( casCoreClient & clientIn, casChannel & chanIn, + casPVI & pvIn, ca_uint32_t cidIn ); + ~casChannelI (); + void casChannelDestroyFromInterfaceNotify (); + const caResId getCID (); + const caResId getSID (); + void uninstallFromPV ( casEventSys & eventSys ); + void installIntoPV (); + void installIO ( casAsyncIOI & ); + void uninstallIO ( casAsyncIOI & ); + void installMonitor ( casMonitor & mon ); + casMonitor * removeMonitor ( ca_uint32_t clientIdIn ); + casPVI & getPVI () const; + void clearOutstandingReads (); + void postAccessRightsEvent (); + const gddEnumStringTable & enumStringTable () const; + void setOwner ( const char * const pUserName, + const char * const pHostName ); + bool readAccess () const; + bool writeAccess () const; + bool confirmationRequested () const; + caStatus read ( const casCtx & ctx, gdd & prototype ); + caStatus write ( const casCtx & ctx, const gdd & value ); + caStatus writeNotify ( const casCtx & ctx, const gdd & value ); + void show ( unsigned level ) const; +private: + chanIntfForPV privateForPV; + tsDLList < casAsyncIOI > ioList; + casPVI & pv; + casChannel & chan; + caResId cid; // client id + bool serverDeletePending; + bool accessRightsEvPending; + //epicsShareFunc virtual void destroy (); + caStatus cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ); + void postDestroyEvent (); + casChannelI ( const casChannelI & ); + casChannelI & operator = ( const casChannelI & ); +}; + +inline casPVI & casChannelI::getPVI () const +{ + return this->pv; +} + +inline const caResId casChannelI::getCID () +{ + return this->cid; +} + +inline const caResId casChannelI::getSID () +{ + return this->chronIntIdRes < casChannelI >::getId (); +} + +inline void casChannelI::postAccessRightsEvent () +{ + this->privateForPV.client().addToEventQueue ( *this, this->accessRightsEvPending ); +} + +inline const gddEnumStringTable & casChannelI::enumStringTable () const +{ + return this->pv.enumStringTable (); +} + +inline void casChannelI::installIntoPV () +{ + this->pv.installChannel ( this->privateForPV ); +} + +inline void casChannelI::clearOutstandingReads () +{ + this->pv.clearOutstandingReads ( this->ioList ); +} + +inline void casChannelI::setOwner ( const char * const pUserName, + const char * const pHostName ) +{ + this->chan.setOwner ( pUserName, pHostName ); +} + +inline bool casChannelI::readAccess () const +{ + return this->chan.readAccess (); +} + +inline bool casChannelI::writeAccess () const +{ + return this->chan.writeAccess (); +} + +inline bool casChannelI::confirmationRequested () const +{ + return this->chan.confirmationRequested (); +} + +inline void casChannelI::installIO ( casAsyncIOI & io ) +{ + this->pv.installIO ( this->ioList, io ); +} + +inline void casChannelI::uninstallIO ( casAsyncIOI & io ) +{ + this->pv.uninstallIO ( this->ioList, io ); +} + +inline void casChannelI::casChannelDestroyFromInterfaceNotify () +{ + if ( ! this->serverDeletePending ) { + this->privateForPV.client().casChannelDestroyFromInterfaceNotify ( + *this, true ); + } +} + +inline void casChannelI::installMonitor ( casMonitor & mon ) +{ + this->privateForPV.installMonitor ( this->pv, mon ); +} + +inline casMonitor * casChannelI::removeMonitor ( + ca_uint32_t clientIdIn ) +{ + return this->privateForPV.removeMonitor ( this->pv, clientIdIn ); +} + +#endif // casChannelIh diff --git a/src/cas/generic/casCoreClient.cc b/src/cas/generic/casCoreClient.cc new file mode 100644 index 000000000..4aa73bb87 --- /dev/null +++ b/src/cas/generic/casCoreClient.cc @@ -0,0 +1,211 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casCoreClient.h" +#include "casAsyncPVExistIOI.h" +#include "casAsyncPVAttachIOI.h" +#include "casChannelI.h" + +casCoreClient::casCoreClient ( caServerI & serverInternal ) : + eventSys ( *this ) +{ + assert ( & serverInternal ); + ctx.setServer ( & serverInternal ); + ctx.setClient ( this ); +} + +casCoreClient::~casCoreClient() +{ + // only used by io that does not have a channel + while ( casAsyncIOI * pIO = this->ioList.get() ) { + pIO->removeFromEventQueue (); + delete pIO; + } + if ( this->ctx.getServer()->getDebugLevel() > 0u ) { + errlogPrintf ( "CAS: Connection Terminated\n" ); + } + + // this will clean up the event queue because all + // channels have been deleted and any events left on + // the queue are there because they are going to + // execute a subscription delete + { + epicsGuard < casClientMutex > guard ( this->mutex ); + this->eventSys.process ( guard ); + } +} + +void casCoreClient::show ( unsigned level ) const +{ + printf ( "Core client\n" ); + epicsGuard < epicsMutex > guard ( this->mutex ); + this->eventSys.show ( level ); + this->ctx.show ( level ); + this->mutex.show ( level ); +} + +// +// one of these for each CA request type that has +// asynchronous completion +// +caStatus casCoreClient::asyncSearchResponse ( + epicsGuard < casClientMutex > &, const caNetAddr &, + const caHdrLargeArray &, const pvExistReturn &, + ca_uint16_t, ca_uint32_t ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::createChanResponse ( + epicsGuard < casClientMutex > &, + casCtx &, const pvAttachReturn & ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::readResponse ( + epicsGuard < casClientMutex > &, casChannelI *, + const caHdrLargeArray &, const gdd &, const caStatus ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::readNotifyResponse ( + epicsGuard < casClientMutex > &, casChannelI *, + const caHdrLargeArray &, const gdd &, const caStatus ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::writeResponse ( + epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray &, const caStatus ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::writeNotifyResponse ( + epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray &, const caStatus ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::monitorResponse ( + epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray &, const gdd &, const caStatus ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::accessRightsResponse ( + epicsGuard < casClientMutex > &, casChannelI * ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::enumPostponedCreateChanResponse ( + epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray & ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::channelCreateFailedResp ( + epicsGuard < casClientMutex > &, const caHdrLargeArray &, + const caStatus ) +{ + return S_casApp_noSupport; +} +caStatus casCoreClient::channelDestroyEventNotify ( + epicsGuard < casClientMutex > &, + casChannelI * const pChan, ca_uint32_t ) +{ + delete pChan; + return S_casApp_success; +} + +void casCoreClient::casChannelDestroyFromInterfaceNotify ( + casChannelI &, bool /* immediateDestroyNeeded */ ) +{ + assert ( 0 ); +} + +caNetAddr casCoreClient::fetchLastRecvAddr () const +{ + return caNetAddr(); // sets addr type to UDF +} + +ca_uint32_t casCoreClient::datagramSequenceNumber () const +{ + return 0; +} + +ca_uint16_t casCoreClient::protocolRevision() const +{ + return 0; +} + +// we need a noop to be called if they post events when a channel +// is being destroyed when we are in the casStrmClient destructor +void casCoreClient::eventSignal() +{ +} + +caStatus casCoreClient::casMonitorCallBack ( + epicsGuard < casClientMutex > &, casMonitor &, const gdd & ) +{ + return S_cas_internal; +} + +casMonitor & casCoreClient::monitorFactory ( + casChannelI & chan, + caResId clientId, + const unsigned long count, + const unsigned type, + const casEventMask & mask ) +{ + casMonitor & mon = this->ctx.getServer()->casMonitorFactory ( + chan, clientId, count, type, mask, *this ); + this->eventSys.installMonitor (); + return mon; +} + +void casCoreClient::destroyMonitor ( casMonitor & mon ) +{ + this->eventSys.removeMonitor (); + assert ( mon.numEventsQueued() == 0 ); + this->ctx.getServer()->casMonitorDestroy ( mon ); +} + +void casCoreClient::installAsynchIO ( casAsyncPVAttachIOI & io ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->ioList.add ( io ); +} + +void casCoreClient::uninstallAsynchIO ( casAsyncPVAttachIOI & io ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->ioList.remove ( io ); +} + +void casCoreClient::installAsynchIO ( casAsyncPVExistIOI & io ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->ioList.add ( io ); +} + +void casCoreClient::uninstallAsynchIO ( casAsyncPVExistIOI & io ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->ioList.remove ( io ); +} diff --git a/src/cas/generic/casCoreClient.h b/src/cas/generic/casCoreClient.h new file mode 100644 index 000000000..dcba5fe88 --- /dev/null +++ b/src/cas/generic/casCoreClient.h @@ -0,0 +1,251 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casCoreClienth +#define casCoreClienth + +#include "caServerI.h" +#include "ioBlocked.h" +#include "casMonitor.h" +#include "casEventSys.h" +#include "casCtx.h" + +class casClientMutex : public epicsMutex { +}; + +// +// casCoreClient +// (this will eventually support direct communication +// between the client lib and the server lib) +// +class casCoreClient : public ioBlocked, + private casMonitorCallbackInterface { +public: + casCoreClient ( caServerI & serverInternal ); + virtual ~casCoreClient (); + virtual void show ( unsigned level ) const; + + void installAsynchIO ( class casAsyncPVAttachIOI & io ); + void uninstallAsynchIO ( class casAsyncPVAttachIOI & io ); + void installAsynchIO ( class casAsyncPVExistIOI & io ); + void uninstallAsynchIO ( class casAsyncPVExistIOI & io ); + + caServerI & getCAS () const; + + // + // one virtual function for each CA request type that has + // asynchronous completion + // + virtual caStatus asyncSearchResponse ( + epicsGuard < casClientMutex > &, const caNetAddr & outAddr, + const caHdrLargeArray &, const pvExistReturn &, + ca_uint16_t protocolRevision, ca_uint32_t sequenceNumber ); + virtual caStatus createChanResponse ( + epicsGuard < casClientMutex > &, + casCtx &, const pvAttachReturn &); + virtual caStatus readResponse ( + epicsGuard < casClientMutex > &, + casChannelI *, const caHdrLargeArray &, + const gdd &, const caStatus ); + virtual caStatus readNotifyResponse ( + epicsGuard < casClientMutex > &, + casChannelI *, const caHdrLargeArray &, + const gdd &, const caStatus ); + virtual caStatus writeResponse ( + epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray &, const caStatus ); + virtual caStatus writeNotifyResponse ( + epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray &, const caStatus ); + virtual caStatus monitorResponse ( + epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray &, const gdd &, + const caStatus status ); + virtual caStatus accessRightsResponse ( + epicsGuard < casClientMutex > &, casChannelI * ); + virtual caStatus enumPostponedCreateChanResponse ( + epicsGuard < casClientMutex > &, + casChannelI &, const caHdrLargeArray & ); + virtual caStatus channelCreateFailedResp ( + epicsGuard < casClientMutex > &, + const caHdrLargeArray &, const caStatus createStatus ); + virtual caStatus channelDestroyEventNotify ( + epicsGuard < casClientMutex > & guard, + casChannelI * const pChan, ca_uint32_t sid ); + virtual void casChannelDestroyFromInterfaceNotify ( + casChannelI & chan, bool immediateDestroyNeeded ); + + virtual ca_uint16_t protocolRevision () const = 0; + + // used only with DG clients + virtual caNetAddr fetchLastRecvAddr () const; + virtual ca_uint32_t datagramSequenceNumber () const; + + bool okToStartAsynchIO (); + void setDestroyPending (); + + casProcCond eventSysProcess(); + + caStatus addToEventQueue ( casAsyncIOI &, + bool & onTheQueue, bool & posted ); + void removeFromEventQueue ( casAsyncIOI &, + bool & onTheEventQueue ); + void addToEventQueue ( + casChannelI &, bool & inTheEventQueue ); + void removeFromEventQueue ( class casChannelI &, + bool & inTheEventQueue ); + void addToEventQueue ( class channelDestroyEvent & ev ); + void enableEvents (); + void disableEvents (); + caStatus casMonitorCallBack ( + epicsGuard < casClientMutex > &, + casMonitor &, const gdd & ); + void postEvent ( tsDLList &, + const casEventMask &select, const gdd &event ); + + casMonitor & monitorFactory ( + casChannelI & , + caResId clientId, + const unsigned long count, + const unsigned type, + const casEventMask & ); + void destroyMonitor ( casMonitor & mon ); + + void casMonEventDestroy ( + casMonEvent &, epicsGuard < evSysMutex > & ); + +protected: + casEventSys eventSys; + mutable casClientMutex mutex; + casCtx ctx; + bool userStartedAsyncIO; + +private: + // for io that does not have a channel + tsDLList < casAsyncIOI > ioList; + + // not pure because the base class noop must be called + // when in the destructor + virtual void eventSignal (); + + casCoreClient ( const casCoreClient & ); + casCoreClient & operator = ( const casCoreClient & ); +}; + +inline caServerI & casCoreClient::getCAS() const +{ + return *this->ctx.getServer(); +} + +inline bool casCoreClient::okToStartAsynchIO () +{ + if ( ! this->userStartedAsyncIO ) { + this->userStartedAsyncIO = true; + return true; + } + return false; +} + +inline void casCoreClient::postEvent ( + tsDLList < casMonitor > & monitorList, + const casEventMask & select, const gdd & event ) +{ + bool signalNeeded = + this->eventSys.postEvent ( monitorList, select, event ); + if ( signalNeeded ) { + this->eventSignal (); + } +} + +inline casProcCond casCoreClient :: eventSysProcess () +{ + epicsGuard < casClientMutex > guard ( this->mutex ); + return this->eventSys.process ( guard ); +} + +inline caStatus casCoreClient::addToEventQueue ( casAsyncIOI & io, + bool & onTheQueue, bool & posted ) +{ + bool wakeupNeeded; + caStatus status = this->eventSys.addToEventQueue ( io, + onTheQueue, posted, wakeupNeeded ); + if ( wakeupNeeded ) { + this->eventSignal (); + } + return status; +} + +inline void casCoreClient::removeFromEventQueue ( + casAsyncIOI & io, bool & onTheEventQueue ) +{ + this->eventSys.removeFromEventQueue ( io, onTheEventQueue ); +} + +inline void casCoreClient::addToEventQueue ( + casChannelI & ev, bool & inTheEventQueue ) +{ + bool signalNeeded = + this->eventSys.addToEventQueue ( ev, inTheEventQueue ); + if ( signalNeeded ) { + this->eventSignal (); + } +} + +inline void casCoreClient::removeFromEventQueue ( class casChannelI & io, + bool & inTheEventQueue ) +{ + this->eventSys.removeFromEventQueue ( io, inTheEventQueue ); +} + +inline void casCoreClient::addToEventQueue ( class channelDestroyEvent & ev ) +{ + bool wakeUpNeeded = this->eventSys.addToEventQueue ( ev ); + if ( wakeUpNeeded ) { + this->eventSignal (); + } +} + +inline void casCoreClient::enableEvents () +{ + this->eventSys.eventsOn (); + this->eventSignal (); // wake up the event queue consumer +} + +inline void casCoreClient::disableEvents () +{ + bool signalNeeded = + this->eventSys.eventsOff (); + if ( signalNeeded ) { + this->eventSignal (); + } +} + +inline void casCoreClient::setDestroyPending () +{ + this->eventSys.setDestroyPending (); + this->eventSignal (); +} + +inline void casCoreClient::casMonEventDestroy ( + casMonEvent & ev, epicsGuard < evSysMutex > & guard ) +{ + this->eventSys.casMonEventDestroy ( ev, guard ); +} + +#endif // casCoreClienth + diff --git a/src/cas/generic/casCtx.cc b/src/cas/generic/casCtx.cc new file mode 100644 index 000000000..c833e9b21 --- /dev/null +++ b/src/cas/generic/casCtx.cc @@ -0,0 +1,46 @@ +/*************************************************************************\ +* 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 +#include + +#define epicsExportSharedSymbols +#include "casCtx.h" +#include "caServerI.h" +#include "casCoreClient.h" +#include "casChannelI.h" + +casCtx::casCtx() : + pData ( NULL ), pCAS ( NULL ), pClient ( NULL ), + pChannel ( NULL ), pPV ( NULL ), nAsyncIO ( 0u ) +{ + memset(&this->msg, 0, sizeof(this->msg)); +} + +void casCtx::show ( unsigned level ) const +{ + printf ( "casCtx at %p\n", + static_cast ( this ) ); + if (level >= 3u) { + printf ("\tpMsg = %p\n", + static_cast ( &this->msg ) ); + printf ("\tpData = %p\n", + static_cast ( pData ) ); + printf ("\tpCAS = %p\n", + static_cast ( pCAS ) ); + printf ("\tpClient = %p\n", + static_cast ( pClient ) ); + printf ("\tpChannel = %p\n", + static_cast ( pChannel ) ); + printf ("\tpPV = %p\n", + static_cast ( pPV ) ); + } +} + diff --git a/src/cas/generic/casCtx.h b/src/cas/generic/casCtx.h new file mode 100644 index 000000000..090124634 --- /dev/null +++ b/src/cas/generic/casCtx.h @@ -0,0 +1,106 @@ + + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casCtxh +#define casCtxh + +#include "caHdrLargeArray.h" + +class casCtx { +public: + casCtx(); + const caHdrLargeArray * getMsg () const; + void * getData () const; + class caServerI * getServer () const; + class casCoreClient * getClient () const; + class casPVI * getPV () const; + class casChannelI * getChannel () const; + void setMsg ( const caHdrLargeArray &, void * pBody ); + void setServer ( class caServerI * p ); + void setClient ( class casCoreClient * p ); + void setPV ( class casPVI * p ); + void setChannel ( class casChannelI * p ); + void show ( unsigned level ) const; +private: + caHdrLargeArray msg; // ca message header + void * pData; // pointer to data following header + caServerI * pCAS; + casCoreClient * pClient; + casChannelI * pChannel; + casPVI * pPV; + unsigned nAsyncIO; // checks for improper use of async io +}; + +inline const caHdrLargeArray * casCtx::getMsg() const +{ + return & this->msg; +} + +inline void * casCtx::getData() const +{ + return this->pData; +} + +inline caServerI * casCtx::getServer() const +{ + return this->pCAS; +} + +inline casCoreClient * casCtx::getClient() const +{ + return this->pClient; +} + +inline casPVI * casCtx::getPV() const +{ + return this->pPV; +} + +inline casChannelI * casCtx::getChannel() const +{ + return this->pChannel; +} + +inline void casCtx::setMsg ( const caHdrLargeArray & msgIn, void * pBody ) +{ + this->msg = msgIn; + this->pData = pBody; +} + +inline void casCtx::setServer(caServerI *p) +{ + this->pCAS = p; +} + +inline void casCtx::setClient(casCoreClient *p) +{ + this->pClient = p; +} + +inline void casCtx::setPV(casPVI *p) +{ + this->pPV = p; +} + +inline void casCtx::setChannel(casChannelI *p) +{ + this->pChannel = p; +} + +#endif // casCtxh diff --git a/src/cas/generic/casCtxIL.h b/src/cas/generic/casCtxIL.h new file mode 100644 index 000000000..b0c89fe98 --- /dev/null +++ b/src/cas/generic/casCtxIL.h @@ -0,0 +1,116 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef casCtxILh +#define casCtxILh + +#include "osiWireFormat.h" + +// +// casCtx::casCtx() +// +inline casCtx::casCtx() : + pData(NULL), pCAS(NULL), pClient(NULL), + pChannel(NULL), pPV(NULL), nAsyncIO(0u) +{ + memset(&this->msg, 0, sizeof(this->msg)); +} + +// +// casCtx::getMsg() +// +inline const caHdrLargeArray * casCtx::getMsg() const +{ + return & this->msg; +} + +// +// casCtx::getData() +// +inline void * casCtx::getData() const +{ + return this->pData; +} + +// +// casCtx::getServer() +// +inline caServerI * casCtx::getServer() const +{ + return this->pCAS; +} + +// +// casCtx::getClient() +// +inline casCoreClient * casCtx::getClient() const +{ + return this->pClient; +} + +// +// casCtx::getPV() +// +inline casPVI * casCtx::getPV() const +{ + return this->pPV; +} + +// +// casCtx::getChannel() +// +inline casChannelI * casCtx::getChannel() const +{ + return this->pChannel; +} + +// +// casCtx::setMsg() +// +inline void casCtx::setMsg ( const caHdrLargeArray & msgIn, void * pBody ) +{ + this->msg = msgIn; + this->pData = pBody; +} + +// +// casCtx::setServer() +// +inline void casCtx::setServer(caServerI *p) +{ + this->pCAS = p; +} + +// +// casCtx::setClient() +// +inline void casCtx::setClient(casCoreClient *p) +{ + this->pClient = p; +} + +// +// casCtx::setPV() +// +inline void casCtx::setPV(casPVI *p) +{ + this->pPV = p; +} + +// +// casCtx::setChannel() +// +inline void casCtx::setChannel(casChannelI *p) +{ + this->pChannel = p; +} + +#endif // casCtxILh + diff --git a/src/cas/generic/casDGClient.cc b/src/cas/generic/casDGClient.cc new file mode 100644 index 000000000..c2fd31b3a --- /dev/null +++ b/src/cas/generic/casDGClient.cc @@ -0,0 +1,956 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "gddApps.h" +#include "caerr.h" +#include "osiWireFormat.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casDGClient.h" +#include "osiPoolStatus.h" // osi pool monitoring functions + +casDGClient::pCASMsgHandler const casDGClient::msgHandlers[] = +{ + & casDGClient::versionAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + + & casDGClient::uknownMessageAction, + & casDGClient::searchAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::echoAction, + & casDGClient::uknownMessageAction, + + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction, + & casDGClient::uknownMessageAction +}; + +// +// casDGClient::casDGClient() +// +casDGClient::casDGClient ( caServerI & serverIn, clientBufMemoryManager & mgrIn ) : + casCoreClient ( serverIn ), + in ( *this, mgrIn, MAX_UDP_RECV + sizeof ( cadg ) ), + out ( *this, mgrIn ), + seqNoOfReq ( 0 ), + minor_version_number ( 0 ) +{ +} + +// +// casDGClient::~casDGClient() +// (virtual destructor) +// +casDGClient::~casDGClient() +{ +} + +// +// casDGClient::destroy() +// +void casDGClient::destroy() +{ + printf("Attempt to destroy the DG client was ignored\n"); +} + +// +// casDGClient::show() +// +void casDGClient::show (unsigned level) const +{ + printf ( "casDGClient at %p\n", + static_cast ( this ) ); + if (level>=1u) { + char buf[64]; + this->hostName (buf, sizeof(buf)); + printf ("Client Host=%s\n", buf); + this->casCoreClient::show ( level - 1u ); + this->in.show ( level - 1u ); + this->out.show ( level - 1u ); + } +} + +// +// casDGClient::uknownMessageAction() +// +caStatus casDGClient::uknownMessageAction () +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + + char pHostName[64u]; + this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); + + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + "bad request code=%u in DG\n", mp->m_cmmd ); + + return S_cas_badProtocol; +} + +// +// casDGClient::searchAction() +// +caStatus casDGClient::searchAction() +{ + const caHdrLargeArray *mp = this->ctx.getMsg(); + const char *pChanName = static_cast ( this->ctx.getData() ); + caStatus status; + + // + // check the sanity of the message + // + if ( mp->m_postsize <= 1 ) { + char pHostName[64u]; + this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + "empty PV name extension in UDP search request?\n" ); + return S_cas_success; + } + + if ( pChanName[0] == '\0' ) { + char pHostName[64u]; + this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + "zero length PV name in UDP search request?\n" ); + return S_cas_success; + } + + // check for an unterminated string before calling server tool + // by searching backwards through the string (some early versions + // of the client library might not be setting the pad bytes to nill) + for ( unsigned i = mp->m_postsize-1; pChanName[i] != '\0'; i-- ) { + if ( i <= 1 ) { + char pHostName[64u]; + this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + "unterminated PV name in UDP search request?\n" ); + return S_cas_success; + } + } + + if ( this->getCAS().getDebugLevel() > 6u ) { + char pHostName[64u]; + this->hostName ( pHostName, sizeof ( pHostName ) ); + printf ( "\"%s\" is searching for \"%s\"\n", + pHostName, pChanName ); + } + + // + // verify that we have sufficent memory for a PV and a + // monitor prior to calling PV exist test so that when + // the server runs out of memory we dont reply to + // search requests, and therefore dont thrash through + // caServer::pvExistTest() and casCreatePV::pvAttach() + // + if ( ! osiSufficentSpaceInPool ( 0 ) ) { + return S_cas_success; + } + + // + // ask the server tool if this PV exists + // + this->userStartedAsyncIO = false; + pvExistReturn pver = + this->getCAS()->pvExistTest ( this->ctx, this->lastRecvAddr, pChanName ); + + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + if ( this->userStartedAsyncIO ) { + if ( pver.getStatus() != pverAsyncCompletion ) { + errMessage (S_cas_badParameter, + "- assuming asynch IO status from caServer::pvExistTest()"); + } + status = S_cas_success; + } + else { + // + // otherwise we assume sync IO operation was initiated + // + switch ( pver.getStatus() ) { + case pverExistsHere: + status = this->searchResponse (*mp, pver); + break; + + case pverDoesNotExistHere: + status = S_cas_success; + break; + + case pverAsyncCompletion: + errMessage (S_cas_badParameter, + "- unexpected asynch IO status from caServer::pvExistTest() ignored"); + status = S_cas_success; + break; + + default: + errMessage (S_cas_badParameter, + "- invalid return from caServer::pvExistTest() ignored"); + status = S_cas_success; + break; + } + } + return status; +} + +// +// caStatus casDGClient::searchResponse() +// +caStatus casDGClient::searchResponse ( const caHdrLargeArray & msg, + const pvExistReturn & retVal ) +{ + caStatus status; + + if ( retVal.getStatus() != pverExistsHere ) { + return S_cas_success; + } + + // + // starting with V4.1 the count field is used (abused) + // by the client to store the minor version number of + // the client. + // + // Old versions expect alloc of channel in response + // to a search request. This is no longer supported. + // + if ( !CA_V44(msg.m_count) ) { + char pName[64u]; + this->hostName (pName, sizeof (pName)); + errlogPrintf ( + "client \"%s\" using EPICS R3.11 CA connect protocol was ignored\n", + pName); + // + // old connect protocol was dropped when the + // new API was added to the server (they must + // now use clients at EPICS 3.12 or higher) + // + status = this->sendErr ( &msg, ECA_DEFUNCT, invalidResID, + "R3.11 connect sequence from old client was ignored" ); + return status; + } + + // + // cid field is abused to carry the IP + // address in CA_V48 or higher + // (this allows a CA servers to serve + // as a directory service) + // + // data type field is abused to carry the IP + // port number here CA_V44 or higher + // (this allows multiple CA servers on one + // host) + // + ca_uint32_t serverAddr; + ca_uint16_t serverPort; + if ( CA_V48( msg.m_count ) ) { + struct sockaddr_in ina; + if ( retVal.addrIsValid() ) { + caNetAddr addr = retVal.getAddr(); + ina = addr.getSockIP(); + // + // If they dont specify a port number then the default + // CA server port is assumed when it it is a server + // address redirect (it is never correct to use this + // server's port when it is a redirect). + // + if (ina.sin_port==0u) { + ina.sin_port = htons ( CA_SERVER_PORT ); + } + } + else { + caNetAddr addr = this->serverAddress (); + ina = addr.getSockIP(); + // + // We dont fill in the servers address here + // because the server was not bound to a particular + // interface, and we would need to waste CPU performing + // the following steps to determine the interface that + // will be used: + // + // o connect UDP socket to the destination IP + // o perform a getsockname() call + // o disconnect UDP socket from the destination IP + // + if ( ina.sin_addr.s_addr == INADDR_ANY ) { + ina.sin_addr.s_addr = htonl ( ~0U ); + } + } + serverAddr = ntohl ( ina.sin_addr.s_addr ); + serverPort = ntohs ( ina.sin_port ); + } + else { + caNetAddr addr = this->serverAddress (); + struct sockaddr_in inetAddr = addr.getSockIP(); + serverAddr = ~0U; + serverPort = ntohs ( inetAddr.sin_port ); + } + + ca_uint16_t * pMinorVersion; + epicsGuard < epicsMutex > guard ( this->mutex ); + status = this->out.copyInHeader ( CA_PROTO_SEARCH, + sizeof ( *pMinorVersion ), serverPort, 0, + serverAddr, msg.m_available, + reinterpret_cast ( &pMinorVersion ) ); + // + // Starting with CA V4.1 the minor version number + // is appended to the end of each search reply. + // This value is ignored by earlier clients. + // + if ( status == S_cas_success ) { + AlignedWireRef < epicsUInt16 > tmp ( *pMinorVersion ); + tmp = CA_MINOR_PROTOCOL_REVISION; + this->out.commitMsg (); + } + + return status; +} + +// +// casDGClient::searchFailResponse() +// (only when requested by the client +// - when it isnt a reply to a broadcast) +// +caStatus casDGClient::searchFailResponse ( const caHdrLargeArray * mp ) +{ + int status; + + epicsGuard < epicsMutex > guard ( this->mutex ); + status = this->out.copyInHeader ( CA_PROTO_NOT_FOUND, 0, + mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, 0 ); + + this->out.commitMsg (); + + return S_cas_success; +} + +/* + * casDGClient::versionAction() + */ +caStatus casDGClient::versionAction () +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + + if ( mp->m_count != 0 ) { + this->minor_version_number = static_cast ( mp->m_count ); + if ( CA_V411 ( mp->m_count ) ) { + this->seqNoOfReq = mp->m_cid; + } + else { + this->seqNoOfReq = 0; + } + } + return S_cas_success; +} + +// +// casDGClient::sendBeacon() +// (implemented here because this has knowledge of the protocol) +// +void casDGClient::sendBeacon ( ca_uint32_t beaconNumber ) +{ + union { + caHdr msg; + char buf; + }; + + // + // create the message + // + memset ( & buf, 0, sizeof ( msg ) ); + AlignedWireRef < epicsUInt16 > ( msg.m_cmmd ) = CA_PROTO_RSRV_IS_UP; + AlignedWireRef < epicsUInt16 > ( msg.m_dataType ) = CA_MINOR_PROTOCOL_REVISION; + AlignedWireRef < epicsUInt32 > ( msg.m_cid ) = beaconNumber; + + // + // send it to all addresses on the beacon list, + // but let the IO specific code set the address + // field and the port field + // + this->sendBeaconIO ( buf, sizeof (msg), msg.m_count, msg.m_available ); +} + +// +// casDGClient::xSend() +// +outBufClient::flushCondition casDGClient::xSend ( char *pBufIn, + bufSizeT nBytesToSend, bufSizeT & nBytesSent ) +{ + bufSizeT totalBytes = 0; + while ( totalBytes < nBytesToSend ) { + cadg *pHdr = reinterpret_cast < cadg * > ( & pBufIn[totalBytes] ); + + assert ( totalBytes <= bufSizeT_MAX - pHdr->cadg_nBytes ); + assert ( totalBytes + pHdr->cadg_nBytes <= nBytesToSend ); + + char * pDG = reinterpret_cast < char * > ( pHdr + 1 ); + unsigned sizeDG = pHdr->cadg_nBytes - sizeof ( *pHdr ); + + if ( pHdr->cadg_addr.isValid() ) { + outBufClient::flushCondition stat = + this->osdSend ( pDG, sizeDG, pHdr->cadg_addr ); + if ( stat != outBufClient::flushProgress ) { + break; + } + } + + totalBytes += pHdr->cadg_nBytes; + } + + if ( totalBytes ) { + // + // !! this time fetch may be slowing things down !! + // + //this->lastSendTS = epicsTime::getCurrent(); + nBytesSent = totalBytes; + return outBufClient::flushProgress; + } + else { + return outBufClient::flushNone; + } +} + +// +// casDGClient::xRecv () +// +inBufClient::fillCondition casDGClient::xRecv (char *pBufIn, bufSizeT nBytesToRecv, // X aCC 361 + fillParameter parm, bufSizeT &nByesRecv) +{ + const char *pAfter = pBufIn + nBytesToRecv; + char *pCurBuf = pBufIn; + bufSizeT nDGBytesRecv; + inBufClient::fillCondition stat; + cadg *pHdr; + + while (pAfter-pCurBuf >= static_cast(MAX_UDP_RECV+sizeof(cadg))) { + pHdr = reinterpret_cast < cadg * > ( pCurBuf ); + stat = this->osdRecv ( reinterpret_cast < char * > ( pHdr + 1 ), + MAX_UDP_RECV, parm, nDGBytesRecv, pHdr->cadg_addr); + if (stat==casFillProgress) { + pHdr->cadg_nBytes = nDGBytesRecv + sizeof(*pHdr); + pCurBuf += pHdr->cadg_nBytes; + // + // !! this time fetch may be slowing things down !! + // + //this->lastRecvTS = epicsTime::getCurrent(); + } + else { + break; + } + } + + nDGBytesRecv = pCurBuf - pBufIn; + if (nDGBytesRecv) { + nByesRecv = nDGBytesRecv; + return casFillProgress; + } + else { + return casFillNone; + } +} + +// +// casDGClient::asyncSearchResp() +// +// +// this results in many small UDP frames which unfortunately +// isnt particularly efficient +// +caStatus casDGClient::asyncSearchResponse ( + epicsGuard < casClientMutex > &, const caNetAddr & outAddr, + const caHdrLargeArray & msg, const pvExistReturn & retVal, + ca_uint16_t protocolRevision, ca_uint32_t sequenceNumber ) +{ + if ( retVal.getStatus() != pverExistsHere ) { + return S_cas_success; + } + + void * pRaw; + const outBufCtx outctx = this->out.pushCtx + ( sizeof(cadg), MAX_UDP_SEND, pRaw ); + if ( outctx.pushResult() != outBufCtx::pushCtxSuccess ) { + return S_cas_sendBlocked; + } + + cadg * pRespHdr = static_cast < cadg * > ( pRaw ); + + // insert version header at the start of the reply message + this->sendVersion (); + + caHdr * pMsg = reinterpret_cast < caHdr * > ( pRespHdr + 1 ); + assert ( ntohs ( pMsg->m_cmmd ) == CA_PROTO_VERSION ); + if ( CA_V411 ( protocolRevision ) ) { + pMsg->m_cid = htonl ( sequenceNumber ); + pMsg->m_dataType = htons ( sequenceNoIsValid ); + } + + caStatus stat = this->searchResponse ( msg, retVal ); + + pRespHdr->cadg_nBytes = this->out.popCtx (outctx) + sizeof ( *pRespHdr ); + if ( pRespHdr->cadg_nBytes > sizeof ( *pRespHdr ) + sizeof (caHdr) ) { + pRespHdr->cadg_addr = outAddr; + this->out.commitRawMsg ( pRespHdr->cadg_nBytes ); + } + + return stat; +} + +// +// casDGClient::processDG () +// +caStatus casDGClient::processDG () +{ + bufSizeT bytesLeft; + caStatus status; + + status = S_cas_success; + while ( ( bytesLeft = this->in.bytesPresent() ) ) { + bufSizeT dgInBytesConsumed; + const cadg * pReqHdr = reinterpret_cast < cadg * > ( this->in.msgPtr () ); + + if (bytesLeftin.removeMsg (bytesLeft); + errlogPrintf ("casDGClient::processMsg: incomplete DG header?"); + status = S_cas_internal; + break; + } + + epicsGuard < epicsMutex > guard ( this->mutex ); + + // + // start a DG context in the output protocol stream + // and grab the send lock + // + void *pRaw; + const outBufCtx outctx = this->out.pushCtx ( sizeof ( cadg ), MAX_UDP_SEND, pRaw ); + if ( outctx.pushResult() != outBufCtx::pushCtxSuccess ) { + status = S_cas_sendBlocked; + break; + } + + // insert version header at the start of the reply message + this->sendVersion (); + + cadg * pRespHdr = static_cast < cadg * > ( pRaw ); + + // + // select the next DG in the input stream and start processing it + // + const bufSizeT reqBodySize = pReqHdr->cadg_nBytes - sizeof (*pReqHdr); + + const inBufCtx inctx = this->in.pushCtx ( sizeof (*pReqHdr), reqBodySize); + if ( inctx.pushResult() != inBufCtx::pushCtxSuccess ) { + this->in.removeMsg ( bytesLeft ); + this->out.popCtx ( outctx ); + errlogPrintf ("casDGClient::processMsg: incomplete DG?\n"); + status = S_cas_internal; + break; + } + + this->lastRecvAddr = pReqHdr->cadg_addr; + this->seqNoOfReq = 0; + this->minor_version_number = 0; + + status = this->processMsg (); + pRespHdr->cadg_nBytes = this->out.popCtx ( outctx ) + sizeof ( *pRespHdr ); + dgInBytesConsumed = this->in.popCtx ( inctx ); + + if ( dgInBytesConsumed > 0 ) { + + // + // at this point processMsg() bailed out because: + // a) it used all of the incoming DG or + // b) it used all of the outgoing DG + // + // In either case commit the DG to the protocol stream and + // release the send lock + // + // if there are not additional messages passed the version header + // then discard the message + if ( pRespHdr->cadg_nBytes > sizeof ( *pRespHdr ) + sizeof (caHdr) ) { + pRespHdr->cadg_addr = pReqHdr->cadg_addr; + + caHdr * pMsg = reinterpret_cast < caHdr * > ( pRespHdr + 1 ); + assert ( ntohs ( pMsg->m_cmmd ) == CA_PROTO_VERSION ); + + if ( CA_V411 ( this->minor_version_number ) ) { + pMsg->m_cid = htonl ( this->seqNoOfReq ); + pMsg->m_dataType = htons ( sequenceNoIsValid ); + } + + this->out.commitRawMsg ( pRespHdr->cadg_nBytes ); + } + + // + // check to see that all of the incoming UDP frame was used + // + if ( dgInBytesConsumed < reqBodySize ) { + // + // remove the bytes in the body that were consumed, + // but _not_ the header bytes + // + this->in.removeMsg (dgInBytesConsumed); + + // + // slide the UDP header forward and correct the byte count + // + { + cadg *pReqHdrMove; + cadg copy = *pReqHdr; + pReqHdrMove = reinterpret_cast < cadg * > ( this->in.msgPtr () ); + pReqHdrMove->cadg_addr = copy.cadg_addr; + pReqHdrMove->cadg_nBytes = copy.cadg_nBytes - dgInBytesConsumed; + } + } + else { + // + // remove the header and all of the body + // + this->in.removeMsg ( pReqHdr->cadg_nBytes ); + } + } + + if ( status != S_cas_success ) { + break; + } + } + return status; +} + +// +// casDGClient::getDebugLevel() +// +unsigned casDGClient::getDebugLevel() const +{ + return this->getCAS().getDebugLevel(); +} + +// +// casDGClient::fetchLastRecvAddr () +// +caNetAddr casDGClient::fetchLastRecvAddr () const +{ + return this->lastRecvAddr; +} + +// +// casDGClient::datagramSequenceNumber () +// +ca_uint32_t casDGClient::datagramSequenceNumber () const +{ + return this->seqNoOfReq; +} + +// +// casDGClient::hostName() +// +void casDGClient::hostName ( char *pBufIn, unsigned bufSizeIn ) const +{ + this->lastRecvAddr.stringConvert ( pBufIn, bufSizeIn ); +} + +// send minor protocol revision to the client +void casDGClient::sendVersion () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + caStatus status = this->out.copyInHeader ( CA_PROTO_VERSION, 0, + 0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 ); + if ( ! status ) { + this->out.commitMsg (); + } +} + +bool casDGClient::inBufFull () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->in.full (); +} + +void casDGClient::inBufFill ( inBufClient::fillParameter parm ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->in.fill ( parm ); +} + +bufSizeT casDGClient :: + inBufBytesPending () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->in.bytesPresent (); +} + +bufSizeT casDGClient :: + outBufBytesPending () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->out.bytesPresent (); +} + +outBufClient::flushCondition casDGClient::flush () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->out.flush (); +} + +// +// casDGClient::processMsg () +// process any messages in the in buffer +// +caStatus casDGClient::processMsg () +{ + int status = S_cas_success; + + try { + unsigned bytesLeft; + while ( ( bytesLeft = this->in.bytesPresent() ) ) { + caHdrLargeArray msgTmp; + unsigned msgSize; + ca_uint32_t hdrSize; + char * rawMP; + { + // + // copy as raw bytes in order to avoid + // alignment problems + // + caHdr smallHdr; + if ( bytesLeft < sizeof ( smallHdr ) ) { + break; + } + + rawMP = this->in.msgPtr (); + memcpy ( & smallHdr, rawMP, sizeof ( smallHdr ) ); + + ca_uint32_t payloadSize = AlignedWireRef < epicsUInt16 > ( smallHdr.m_postsize ); + ca_uint32_t nElem = AlignedWireRef < epicsUInt16 > ( smallHdr.m_count ); + if ( payloadSize != 0xffff && nElem != 0xffff ) { + hdrSize = sizeof ( smallHdr ); + } + else { + ca_uint32_t LWA[2]; + hdrSize = sizeof ( smallHdr ) + sizeof ( LWA ); + if ( bytesLeft < hdrSize ) { + break; + } + // + // copy as raw bytes in order to avoid + // alignment problems + // + memcpy ( LWA, rawMP + sizeof ( caHdr ), sizeof( LWA ) ); + payloadSize = AlignedWireRef < epicsUInt32 > ( LWA[0] ); + nElem = AlignedWireRef < epicsUInt32 > ( LWA[1] ); + } + + msgTmp.m_cmmd = AlignedWireRef < epicsUInt16 > ( smallHdr.m_cmmd ); + msgTmp.m_postsize = payloadSize; + msgTmp.m_dataType = AlignedWireRef < epicsUInt16 > ( smallHdr.m_dataType ); + msgTmp.m_count = nElem; + msgTmp.m_cid = AlignedWireRef < epicsUInt32 > ( smallHdr.m_cid ); + msgTmp.m_available = AlignedWireRef < epicsUInt32 > ( smallHdr.m_available ); + + if ( payloadSize & 0x7 ) { + status = this->sendErr ( + & msgTmp, invalidResID, ECA_INTERNAL, + "CAS: Datagram request wasn't 8 byte aligned" ); + this->in.removeMsg ( bytesLeft ); + break; + } + + msgSize = hdrSize + payloadSize; + if ( bytesLeft < msgSize ) { + if ( msgSize > this->in.bufferSize() ) { + status = this->sendErr ( & msgTmp, invalidResID, ECA_TOLARGE, + "client's request didnt fit within the CA server's message buffer" ); + this->in.removeMsg ( bytesLeft ); + } + break; + } + + this->ctx.setMsg ( msgTmp, rawMP + hdrSize ); + + if ( this->getCAS().getDebugLevel() > 5u ) { + char pHostName[64u]; + this->lastRecvAddr.stringConvert ( pHostName, sizeof ( pHostName ) ); + caServerI::dumpMsg ( pHostName, "?", + & msgTmp, rawMP + hdrSize, 0 ); + } + + } + + // + // Reset the context to the default + // (guarantees that previous message does not get mixed + // up with the current message) + // + this->ctx.setChannel ( NULL ); + this->ctx.setPV ( NULL ); + + // + // Call protocol stub + // + casDGClient::pCASMsgHandler pHandler; + if ( msgTmp.m_cmmd < NELEMENTS ( casDGClient::msgHandlers ) ) { + pHandler = this->casDGClient::msgHandlers[msgTmp.m_cmmd]; + } + else { + pHandler = & casDGClient::uknownMessageAction; + } + status = ( this->*pHandler ) (); + if ( status ) { + this->in.removeMsg ( this->in.bytesPresent() ); + break; + } + + this->in.removeMsg ( msgSize ); + } + } + catch ( std::exception & except ) { + this->in.removeMsg ( this->in.bytesPresent() ); + status = this->sendErr ( + this->ctx.getMsg(), invalidResID, ECA_INTERNAL, + "C++ exception \"%s\" in CA circuit server", + except.what () ); + status = S_cas_internal; + } + catch (...) { + this->in.removeMsg ( this->in.bytesPresent() ); + status = this->sendErr ( + this->ctx.getMsg(), invalidResID, ECA_INTERNAL, + "unexpected C++ exception in CA datagram server" ); + status = S_cas_internal; + } + + return status; +} + +// +// casDGClient::sendErr() +// +caStatus casDGClient::sendErr ( const caHdrLargeArray *curp, + ca_uint32_t cid, const int reportedStatus, const char *pformat, ... ) +{ + unsigned stringSize; + char msgBuf[1024]; /* allocate plenty of space for the message string */ + if ( pformat ) { + va_list args; + va_start ( args, pformat ); + int status = vsprintf ( msgBuf, pformat, args ); + if ( status < 0 ) { + errPrintf (S_cas_internal, __FILE__, __LINE__, + "bad sendErr(%s)", pformat); + stringSize = 0u; + } + else { + stringSize = 1u + (unsigned) status; + } + } + else { + stringSize = 0u; + } + + unsigned hdrSize = sizeof ( caHdr ); + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + CA_V49( this->minor_version_number ) ) { + hdrSize += 2 * sizeof ( ca_uint32_t ); + } + + caHdr * pReqOut; + epicsGuard < epicsMutex > guard ( this->mutex ); + caStatus status = this->out.copyInHeader ( CA_PROTO_ERROR, + hdrSize + stringSize, 0, 0, cid, reportedStatus, + reinterpret_cast ( & pReqOut ) ); + if ( ! status ) { + char * pMsgString; + + /* + * copy back the request protocol + * (in network byte order) + */ + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + CA_V49( this->minor_version_number ) ) { + ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); + pReqOut->m_cmmd = htons ( curp->m_cmmd ); + pReqOut->m_postsize = htons ( 0xffff ); + pReqOut->m_dataType = htons ( curp->m_dataType ); + pReqOut->m_count = htons ( 0u ); + pReqOut->m_cid = htonl ( curp->m_cid ); + pReqOut->m_available = htonl ( curp->m_available ); + pLW[0] = htonl ( curp->m_postsize ); + pLW[1] = htonl ( curp->m_count ); + pMsgString = ( char * ) ( pLW + 2 ); + } + else { + pReqOut->m_cmmd = htons (curp->m_cmmd); + pReqOut->m_postsize = htons ( ( (ca_uint16_t) curp->m_postsize ) ); + pReqOut->m_dataType = htons (curp->m_dataType); + pReqOut->m_count = htons ( ( (ca_uint16_t) curp->m_count ) ); + pReqOut->m_cid = htonl (curp->m_cid); + pReqOut->m_available = htonl (curp->m_available); + pMsgString = ( char * ) ( pReqOut + 1 ); + } + + /* + * add their context string into the protocol + */ + memcpy ( pMsgString, msgBuf, stringSize ); + + this->out.commitMsg (); + } + + return S_cas_success; +} + + +// +// echoAction() +// +caStatus casDGClient::echoAction () +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + const void * dp = this->ctx.getData(); + void * pPayloadOut; + + epicsGuard < epicsMutex > guard ( this->mutex ); + caStatus status = this->out.copyInHeader ( mp->m_cmmd, mp->m_postsize, + mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, + & pPayloadOut ); + if ( ! status ) { + memcpy ( pPayloadOut, dp, mp->m_postsize ); + this->out.commitMsg (); + } + return S_cas_success; +} diff --git a/src/cas/generic/casDGClient.h b/src/cas/generic/casDGClient.h new file mode 100644 index 000000000..334af2043 --- /dev/null +++ b/src/cas/generic/casDGClient.h @@ -0,0 +1,109 @@ + + +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef casDGClienth +#define casDGClienth + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casDGClienth +# undef epicsExportSharedSymbols +#endif + +#include "epicsTime.h" + +#ifdef epicsExportSharedSymbols_casDGClienth +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casCoreClient.h" +#include "inBuf.h" +#include "outBuf.h" + +class casDGClient : public casCoreClient, public outBufClient, + public inBufClient { +public: + casDGClient ( class caServerI & serverIn, + clientBufMemoryManager & ); + virtual ~casDGClient (); + caStatus processMsg (); + void show ( unsigned level ) const; + void sendBeacon ( ca_uint32_t beaconNumber ); + virtual void sendBeaconIO ( char & msg, bufSizeT length, + aitUint16 & portField, aitUint32 & addrField ) = 0; + void destroy (); + unsigned getDebugLevel () const; + void hostName ( char * pBuf, unsigned bufSize ) const; + caNetAddr fetchLastRecvAddr () const; + virtual caNetAddr serverAddress () const = 0; + caStatus sendErr ( const caHdrLargeArray * curp, + ca_uint32_t cid, const int reportedStatus, + const char *pformat, ... ); + caStatus processDG (); +protected: + bool inBufFull () const; + void inBufFill ( inBufClient::fillParameter ); + bufSizeT inBufBytesPending () const; + bufSizeT outBufBytesPending () const; + outBufClient::flushCondition flush (); +private: + inBuf in; + outBuf out; + caNetAddr lastRecvAddr; + epicsTime lastSendTS; + epicsTime lastRecvTS; + ca_uint32_t seqNoOfReq; + ca_uint16_t minor_version_number; + + typedef caStatus ( casDGClient :: * pCASMsgHandler ) (); + static pCASMsgHandler const msgHandlers[CA_PROTO_LAST_CMMD+1u]; + + // one function for each CA request type + caStatus searchAction (); + caStatus uknownMessageAction (); + caStatus echoAction (); + + caStatus searchFailResponse ( const caHdrLargeArray *pMsg ); + caStatus searchResponse ( const caHdrLargeArray &, + const pvExistReturn & retVal ); + caStatus asyncSearchResponse ( + epicsGuard < casClientMutex > &, const caNetAddr & outAddr, + const caHdrLargeArray &, const pvExistReturn &, + ca_uint16_t protocolRevision, ca_uint32_t sequenceNumber ); + void sendVersion (); + outBufClient::flushCondition xSend ( char *pBufIn, bufSizeT nBytesToSend, + bufSizeT &nBytesSent ); + inBufClient::fillCondition xRecv ( char * pBufIn, bufSizeT nBytesToRecv, + fillParameter parm, bufSizeT & nByesRecv ); + virtual outBufClient::flushCondition osdSend ( + const char * pBuf, bufSizeT nBytesReq, const caNetAddr & addr ) = 0; + virtual inBufClient::fillCondition osdRecv ( char *pBuf, bufSizeT nBytesReq, + fillParameter parm, bufSizeT &nBytesActual, caNetAddr & addr ) = 0; + caStatus versionAction (); + ca_uint32_t datagramSequenceNumber () const; + ca_uint16_t protocolRevision () const; + struct cadg { + caNetAddr cadg_addr; // invalid address indicates pad + bufSizeT cadg_nBytes; + }; + casDGClient ( const casDGClient & ); + casDGClient & operator = ( const casDGClient & ); +}; + +inline ca_uint16_t casDGClient::protocolRevision () const +{ + return this->minor_version_number; +} + + +#endif // casDGClienth + diff --git a/src/cas/generic/casEvent.h b/src/cas/generic/casEvent.h new file mode 100644 index 000000000..d273ce961 --- /dev/null +++ b/src/cas/generic/casEvent.h @@ -0,0 +1,54 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casEventh +#define casEventh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casEventh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "tsDLList.h" + +#ifdef epicsExportSharedSymbols_casEventh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casdef.h" + +class casCoreClient; + +class evSysMutex; +class casClientMutex; +template < class MUTEX > class epicsGuard; + +class casEvent : public tsDLNode < casEvent > { +public: + virtual caStatus cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ) = 0; +protected: + epicsShareFunc virtual ~casEvent(); +}; + +#endif // casEventh + diff --git a/src/cas/generic/casEventMask.cc b/src/cas/generic/casEventMask.cc new file mode 100644 index 000000000..3f58d073d --- /dev/null +++ b/src/cas/generic/casEventMask.cc @@ -0,0 +1,155 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casdef.h" +#include "epicsAssert.h" +#include "casEventRegistry.h" + +#ifdef TEST +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +main () +{ + casEventRegistry reg; + casEventMask bill1 (reg, "bill"); + casEventMask bill2 (reg, "bill"); + casEventMask bill3 (reg, "bill"); + casEventMask art1 (reg, "art"); + casEventMask art2 (reg, "art"); + casEventMask jane (reg, "jane"); + casEventMask artBill; + casEventMask tmp; + + bill1.show(10u); + reg.show(10u); + bill2.show(10u); + reg.show(10u); + bill3.show(10u); + reg.show(10u); + jane.show(10u); + reg.show(10u); + art1.show(10u); + reg.show(10u); + art2.show(10u); + reg.show(10u); + + verify (bill1 == bill2); + verify (bill1 == bill3); + verify (jane != bill1); + verify (jane != art1); + verify (bill1 != art1); + verify (art1 == art2); + + artBill = art1 | bill1; + tmp = artBill & art1; + verify (tmp.eventsSelected()); + tmp = artBill & bill1; + verify (tmp.eventsSelected()); + tmp = artBill&jane; + verify (tmp.noEventsSelected()); +} +#endif + +casEventMask casEventRegistry::maskAllocator () +{ + casEventMask evMask; + + if ( this->maskBitAllocator < CHAR_BIT * sizeof ( evMask.mask ) ) { + evMask.mask = 1u << ( this->maskBitAllocator++ ); + } + return evMask; +} + +casEventMask casEventRegistry::registerEvent ( const char *pName ) +{ + // + // NOTE: pName outlives id here + // (so the refString option is ok) + // + stringId id ( pName, stringId::refString ); + casEventMaskEntry * pEntry; + casEventMask mask; + + pEntry = this->lookup ( id ); + if (pEntry) { + mask = *pEntry; + } + else { + mask = this->maskAllocator (); + if ( mask.mask == 0u ) { + errMessage ( S_cas_tooManyEvents, "casEventRegistry::registerEvent" ); + } + else { + pEntry = new casEventMaskEntry ( *this, mask, pName ); + mask = *pEntry; + } + } + return mask; +} + +void casEventMask::show ( unsigned level ) const +{ + if ( level > 0u ) { + printf ( "casEventMask = %x\n", this->mask ); + } +} + +casEventMask::casEventMask ( casEventRegistry & reg, const char * pName ) +{ + *this = reg.registerEvent ( pName ); +} + +void casEventRegistry::show ( unsigned level ) const +{ + if ( level > 1u ) { + printf ("casEventRegistry: bit allocator = %d\n", + this->maskBitAllocator); + } + this->resTable < casEventMaskEntry, stringId >::show ( level ); +} + +casEventMaskEntry::casEventMaskEntry ( + casEventRegistry & regIn, casEventMask maskIn, const char * pName ) : + casEventMask ( maskIn ), stringId ( pName ), reg ( regIn ) +{ + assert ( this->resourceName() != NULL ); + int stat = this->reg.add ( *this ); + assert ( stat == 0 ); +} + +casEventMaskEntry::~casEventMaskEntry() +{ + this->reg.remove ( *this ); +} + +void casEventMaskEntry::destroy () +{ + delete this; +} + +void casEventMaskEntry::show ( unsigned level ) const +{ + this->casEventMask::show ( level ); + this->stringId::show ( level ); +} + diff --git a/src/cas/generic/casEventMask.h b/src/cas/generic/casEventMask.h new file mode 100644 index 000000000..0274fc27c --- /dev/null +++ b/src/cas/generic/casEventMask.h @@ -0,0 +1,109 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + + +#ifndef casEventMaskH +#define casEventMaskH + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casEventMaskH +# undef epicsExportSharedSymbols +#endif +#include "resourceLib.h" +#ifdef epicsExportSharedSymbols_casEventMaskH +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class casEventRegistry; + +class epicsShareClass casEventMask { + friend inline casEventMask operator| (const casEventMask &lhs, + const casEventMask &rhs); + friend inline casEventMask operator& (const casEventMask &lhs, + const casEventMask &rhs); + friend inline int operator== (const casEventMask &lhs, + const casEventMask &rhs); + friend inline int operator!= (const casEventMask &lhs, + const casEventMask &rhs); + friend class casEventRegistry; +public: + void clear () + { + this->mask = 0u; + } + + casEventMask ( casEventRegistry ®, const char *pName ); + + casEventMask () + { + this->clear(); + } + + void show ( unsigned level ) const; + + bool eventsSelected () const + { + return this->mask != 0u; + } + bool noEventsSelected () const + { + return this->mask == 0u; + } + + inline void operator |= ( const casEventMask & rhs ); + inline void operator &= ( const casEventMask & rhs ); + +private: + unsigned mask; +}; + +inline casEventMask operator| (const casEventMask &lhs, const casEventMask &rhs) +{ + casEventMask result; + + result.mask = lhs.mask | rhs.mask; + return result; +} +inline casEventMask operator& (const casEventMask &lhs, const casEventMask &rhs) +{ + casEventMask result; + + result.mask = lhs.mask & rhs.mask; + return result; +} + +inline int operator== (const casEventMask &lhs, const casEventMask &rhs) +{ + return lhs.mask == rhs.mask; +} + +inline int operator!= (const casEventMask &lhs, const casEventMask &rhs) +{ + return lhs.mask != rhs.mask; +} + +inline void casEventMask::operator|= (const casEventMask &rhs) { + *this = *this | rhs; +} + +inline void casEventMask::operator&= (const casEventMask &rhs) { + *this = *this & rhs; +} + +#endif // casEventMaskH + diff --git a/src/cas/generic/casEventRegistry.h b/src/cas/generic/casEventRegistry.h new file mode 100644 index 000000000..9ece7c8d4 --- /dev/null +++ b/src/cas/generic/casEventRegistry.h @@ -0,0 +1,69 @@ + + + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casEventRegistryh +#define casEventRegistryh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casEventRegistryh +# undef epicsExportSharedSymbols +#endif + +#include "tsSLList.h" + +#ifdef epicsExportSharedSymbols_casEventRegistryh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casEventMask.h" + +class casEventMaskEntry : public tsSLNode < casEventMaskEntry >, + public casEventMask, public stringId { +public: + casEventMaskEntry (casEventRegistry ®In, + casEventMask maskIn, const char *pName); + virtual ~casEventMaskEntry(); + void show (unsigned level) const; + + virtual void destroy(); +private: + casEventRegistry ® + casEventMaskEntry ( const casEventMaskEntry & ); + casEventMaskEntry & operator = ( const casEventMaskEntry & ); +}; + +class casEventRegistry : + private resTable < casEventMaskEntry, stringId > { + friend class casEventMaskEntry; +public: + casEventRegistry () : maskBitAllocator ( 0 ) {} + virtual ~casEventRegistry(); + casEventMask registerEvent ( const char * pName ); + void show ( unsigned level ) const; +private: + unsigned maskBitAllocator; + + casEventMask maskAllocator(); + casEventRegistry ( const casEventRegistry & ); + casEventRegistry & operator = ( const casEventRegistry & ); +}; + +#endif // casEventRegistryh diff --git a/src/cas/generic/casEventSys.cc b/src/cas/generic/casEventSys.cc new file mode 100644 index 000000000..9c964941a --- /dev/null +++ b/src/cas/generic/casEventSys.cc @@ -0,0 +1,398 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "caHdrLargeArray.h" +#include "casCoreClient.h" +#include "casAsyncIOI.h" +#include "casChannelI.h" +#include "channelDestroyEvent.h" + +void casEventSys::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + printf ( "casEventSys at %p\n", + static_cast ( this ) ); + if (level>=1u) { + printf ( "\numSubscriptions = %u, maxLogEntries = %u\n", + this->numSubscriptions, this->maxLogEntries ); + printf ( "\tthere are %d items in the event queue\n", + this->eventLogQue.count() ); + printf ( "\tthere are %d items in the io queue\n", + this->ioQue.count() ); + printf ( "Replace events flag = %d, dontProcessSubscr flag = %d\n", + static_cast < int > ( this->replaceEvents ), + static_cast < int > ( this->dontProcessSubscr ) ); + } +} + +casEventSys::~casEventSys() +{ + if ( this->pPurgeEvent != NULL ) { + this->eventLogQue.remove ( *this->pPurgeEvent ); + delete this->pPurgeEvent; + } + + // at this point: + // o all channels have been deleted + // o all IO has been deleted + // o any subscription events remaining on the queue + // are pending destroy + + // verify above assertion is true + casVerify ( this->eventLogQue.count() == 0 ); + casVerify ( this->ioQue.count() == 0 ); + + // all active subscriptions should also have been + // uninstalled + casVerify ( this->numSubscriptions == 0 ); + if ( this->numSubscriptions != 0 ) { + printf ( "numSubscriptions=%u\n", this->numSubscriptions ); + } +} + +void casEventSys::installMonitor () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + assert ( this->numSubscriptions < UINT_MAX ); + this->numSubscriptions++; + this->maxLogEntries += averageEventEntries; +} + +void casEventSys::removeMonitor () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + assert ( this->numSubscriptions >= 1u ); + this->numSubscriptions--; + this->maxLogEntries -= averageEventEntries; +} + +casProcCond casEventSys :: process ( + epicsGuard < casClientMutex > & casClientGuard ) +{ + casProcCond cond = casProcOk; + + epicsGuard < evSysMutex > evGuard ( this->mutex ); + + // we need two queues, one for io and one for subscriptions, + // so that we dont hang up the server when in an IO postponed + // state simultaneouly with a flow control active state + while ( true ) { + casEvent * pEvent = this->ioQue.get (); + if ( pEvent == NULL ) { + break; + } + + caStatus status = pEvent->cbFunc ( + this->client, casClientGuard, evGuard ); + if ( status == S_cas_success ) { + cond = casProcOk; + } + else if ( status == S_cas_sendBlocked ) { + // not accepted so return to the head of the list + // (we will try again later) + this->ioQue.push ( *pEvent ); + cond = casProcOk; + break; + } + else if ( status == S_cas_disconnect ) { + cond = casProcDisconnect; + break; + } + else { + errMessage ( status, + "- unexpected error, processing io queue" ); + cond = casProcDisconnect; + break; + } + } + + if ( cond == casProcOk ) { + while ( ! this->dontProcessSubscr ) { + casEvent * pEvent = this->eventLogQue.get (); + if ( pEvent == NULL ) { + break; + } + + caStatus status = pEvent->cbFunc ( + this->client, casClientGuard, evGuard ); + if ( status == S_cas_success ) { + cond = casProcOk; + } + else if ( status == S_cas_sendBlocked ) { + // not accepted so return to the head of the list + // (we will try again later) + this->eventLogQue.push ( *pEvent ); + cond = casProcOk; + break; + } + else if ( status == S_cas_disconnect ) { + cond = casProcDisconnect; + break; + } + else { + errMessage ( status, + "- unexpected error, processing event queue" ); + cond = casProcDisconnect; + break; + } + } + } + + // + // allows the derived class to be informed that it + // needs to delete itself via the event system + // + // this gets the server out of nasty situations + // where the client needs to be deleted but + // the caller may be using the client's "this" + // pointer. + // + if ( this->destroyPending ) { + cond = casProcDisconnect; + } + + return cond; +} + +void casEventSys::eventsOn () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + // + // allow multiple events for each monitor + // + this->replaceEvents = false; + + // + // allow the event queue to be processed + // + this->dontProcessSubscr = false; + + // + // remove purge event if it is still pending + // + if ( this->pPurgeEvent != NULL ) { + this->eventLogQue.remove ( *this->pPurgeEvent ); + delete this->pPurgeEvent; + this->pPurgeEvent = NULL; + } +} + +bool casEventSys::eventsOff () +{ + bool signalNeeded = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + + // + // new events will replace the last event on + // the queue for a particular monitor + // + this->replaceEvents = true; + + // + // suppress the processing and sending of events + // only after we have purged the event queue + // for this particular client + // + if ( this->pPurgeEvent == NULL ) { + this->pPurgeEvent = new casEventPurgeEv ( *this ); + if ( this->pPurgeEvent == NULL ) { + // + // if there is no room for the event then immediately + // stop processing and sending events to the client + // until we exit flow control + // + this->dontProcessSubscr = true; + } + else { + if ( this->eventLogQue.count() == 0 ) { + signalNeeded = true; + } + this->eventLogQue.add ( *this->pPurgeEvent ); + } + } + } + return signalNeeded; +} + +casEventPurgeEv::casEventPurgeEv ( casEventSys & evSysIn ) : + evSys ( evSysIn ) +{ +} + +caStatus casEventPurgeEv::cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ) +{ + this->evSys.dontProcessSubscr = true; + this->evSys.pPurgeEvent = NULL; + delete this; + return S_cas_success; +} + +caStatus casEventSys::addToEventQueue ( casAsyncIOI & event, + bool & onTheQueue, bool & posted, bool & wakeupNeeded ) +{ + { + epicsGuard < epicsMutex > guard ( this->mutex ); + // dont allow them to post completion more than once + if ( posted || onTheQueue ) { + wakeupNeeded = false; + return S_cas_redundantPost; + } + posted = true; + onTheQueue = true; + wakeupNeeded = + ( this->dontProcessSubscr || this->eventLogQue.count() == 0 ) && + this->ioQue.count() == 0; + this->ioQue.add ( event ); + } + return S_cas_success; +} + +void casEventSys::removeFromEventQueue ( casAsyncIOI & io, bool & onTheIOQueue ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( onTheIOQueue ) { + onTheIOQueue = false; + this->ioQue.remove ( io ); + } +} + +bool casEventSys::addToEventQueue ( casChannelI & event, + bool & onTheIOQueue ) +{ + bool wakeupNeeded = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( ! onTheIOQueue ) { + onTheIOQueue = true; + wakeupNeeded = + ( this->dontProcessSubscr || this->eventLogQue.count() == 0 ) && + this->ioQue.count() == 0; + this->ioQue.add ( event ); + } + } + return wakeupNeeded; +} + +void casEventSys::removeFromEventQueue ( class casChannelI & io, + bool & onTheIOQueue ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( onTheIOQueue ) { + onTheIOQueue = false; + this->ioQue.remove ( io ); + } +} + +bool casEventSys::addToEventQueue ( channelDestroyEvent & event ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + bool wakeupRequired = + ( this->dontProcessSubscr || this->eventLogQue.count() == 0 ) && + this->ioQue.count() == 0; + this->ioQue.add ( event ); + return wakeupRequired; +} + +void casEventSys::setDestroyPending () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->destroyPending = true; +} + +inline bool casEventSys::full () const +{ + return this->replaceEvents || + this->eventLogQue.count() >= this->maxLogEntries; +} + +bool casEventSys::postEvent ( tsDLList < casMonitor > & monitorList, + const casEventMask & select, const gdd & event ) +{ + bool signalNeeded = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + tsDLIter < casMonitor > iter = monitorList.firstIter (); + while ( iter.valid () ) { + if ( iter->selected ( select ) ) { + // get a new block if we havent exceeded quotas + bool full = ( iter->numEventsQueued() >= individualEventEntries ) + || this->full (); + casMonEvent * pLog; + if ( ! full ) { + // should I get rid of this try block by implementing a no + // throw version of the free list alloc? However, crude + // tests on windows with ms visual C++ dont appear to argue + // against the try block. + try { + pLog = new ( this->casMonEventFreeList ) + casMonEvent ( *iter, event ); + } + catch ( ... ) { + pLog = 0; + } + } + else { + pLog = 0; + } + + signalNeeded |= + !this->dontProcessSubscr && + this->eventLogQue.count() == 0 && + this->ioQue.count() == 0; + + iter->installNewEventLog ( + this->eventLogQue, pLog, event ); + } + ++iter; + } + } + return signalNeeded; +} + +void casEventSys::casMonEventDestroy ( + casMonEvent & ev, epicsGuard < evSysMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + ev.~casMonEvent (); + this->casMonEventFreeList.release ( & ev ); +} + +void casEventSys::prepareMonitorForDestroy ( casMonitor & mon ) +{ + bool safeToDestroy = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + mon.markDestroyPending (); + // if events reference it on the queue then it gets + // deleted when it reaches the top of the queue + if ( mon.numEventsQueued () == 0 ) { + safeToDestroy = true; + } + } + if ( safeToDestroy ) { + this->client.destroyMonitor ( mon ); + } +} diff --git a/src/cas/generic/casEventSys.h b/src/cas/generic/casEventSys.h new file mode 100644 index 000000000..42130bee9 --- /dev/null +++ b/src/cas/generic/casEventSys.h @@ -0,0 +1,145 @@ + + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casEventSysh +#define casEventSysh + +#if defined ( epicsExportSharedSymbols ) +# undef epicsExportSharedSymbols +# define casEventSysh_restore_epicsExportSharedSymbols +#endif + +#include "tsDLList.h" +#include "tsFreeList.h" +#include "epicsMutex.h" + +#if defined ( casEventSysh_restore_epicsExportSharedSymbols ) +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casdef.h" +#include "casEvent.h" + +/* + * maximum peak log entries for each event block (registartion) + * (events cached into the last queue entry if over flow occurs) + * (if this exceeds 256 then the casMonitor::nPend must + * be assigned a new data type) + */ +#define individualEventEntries 16u + +/* + * maximum average log entries for each event block (registartion) + * (events cached into the last queue entry if over flow occurs) + * (if this exceeds 256 then the casMonitor::nPend must + * be assigned a new data type) + */ +#define averageEventEntries 4u + +enum casProcCond { casProcOk, casProcDisconnect }; + +class casMonitor; +class casMonEvent; +class casCoreClient; + +template < class MUTEX > class epicsGuard; + +class evSysMutex : public epicsMutex {}; + +class casEventSys { +public: + casEventSys ( casCoreClient & ); + ~casEventSys (); + void show ( unsigned level ) const; + casProcCond process ( epicsGuard < casClientMutex > & guard ); + void installMonitor (); + void removeMonitor (); + void prepareMonitorForDestroy ( casMonitor & mon ); + bool postEvent ( tsDLList < casMonitor > & monitorList, + const casEventMask & select, const gdd & event ); + caStatus addToEventQueue ( class casAsyncIOI &, + bool & onTheQueue, bool & posted, bool & signalNeeded ); + void removeFromEventQueue ( class casAsyncIOI &, + bool & onTheEventQueue ); + bool addToEventQueue ( + casChannelI &, bool & inTheEventQueue ); + void removeFromEventQueue ( class casChannelI &, + bool & inTheEventQueue ); + bool addToEventQueue ( class channelDestroyEvent & ); + bool getNDuplicateEvents () const; + void setDestroyPending (); + void eventsOn (); + bool eventsOff (); + void casMonEventDestroy ( + casMonEvent &, epicsGuard < evSysMutex > & ); +private: + mutable evSysMutex mutex; + tsDLList < casEvent > eventLogQue; + tsDLList < casEvent > ioQue; + tsFreeList < casMonEvent, 1024, epicsMutexNOOP > casMonEventFreeList; + casCoreClient & client; + class casEventPurgeEv * pPurgeEvent; // flow control purge complete event + unsigned numSubscriptions; // N subscriptions installed + unsigned maxLogEntries; // max log entries + bool destroyPending; + bool replaceEvents; // replace last existing event on queue + bool dontProcessSubscr; // flow ctl is on - dont process subscr event queue + + bool full () const; + casEventSys ( const casEventSys & ); + casEventSys & operator = ( const casEventSys & ); + friend class casEventPurgeEv; +}; + +/* + * when this event reaches the top of the queue we + * know that all duplicate events have been purged + * and that now no events should not be sent to the + * client until it exits flow control mode + */ +class casEventPurgeEv : public casEvent { +public: + casEventPurgeEv ( class casEventSys & ); +private: + casEventSys & evSys; + caStatus cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ); + casEventPurgeEv & operator = ( const casEventPurgeEv & ); + casEventPurgeEv ( const casEventPurgeEv & ); +}; + +// +// casEventSys::casEventSys () +// +inline casEventSys::casEventSys ( casCoreClient & clientIn ) : + client ( clientIn ), + pPurgeEvent ( NULL ), + numSubscriptions ( 0u ), + maxLogEntries ( individualEventEntries ), + destroyPending ( false ), + replaceEvents ( false ), + dontProcessSubscr ( false ) +{ +} + +#endif // casEventSysh + diff --git a/src/cas/generic/casMonEvent.cc b/src/cas/generic/casMonEvent.cc new file mode 100644 index 000000000..094fcaba3 --- /dev/null +++ b/src/cas/generic/casMonEvent.cc @@ -0,0 +1,79 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casMonEvent.h" +#include "casMonitor.h" +#include "casCoreClient.h" + +caStatus casMonEvent::cbFunc ( + casCoreClient & client, + epicsGuard < casClientMutex > & clientGuard, + epicsGuard < evSysMutex > & evGuard ) +{ + return this->monitor.executeEvent ( + client, * this, *this->pValue, + clientGuard, evGuard ); +} + +void casMonEvent::assign ( const gdd & valueIn ) +{ + this->pValue = & valueIn; +} + +void casMonEvent::swapValues ( casMonEvent & in ) +{ + assert ( & in.monitor == & this->monitor ); + this->pValue.swap ( in.pValue ); +} + +casMonEvent::~casMonEvent () +{ +} + +#ifdef CXX_PLACEMENT_DELETE +void casMonEvent::operator delete ( void * pCadaver, + tsFreeList < class casMonEvent, 1024, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver, sizeof ( casMonEvent ) ); +} +#endif + +void * casMonEvent::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void casMonEvent::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + + + diff --git a/src/cas/generic/casMonEvent.h b/src/cas/generic/casMonEvent.h new file mode 100644 index 000000000..44a4e91bc --- /dev/null +++ b/src/cas/generic/casMonEvent.h @@ -0,0 +1,81 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casMonEventh +#define casMonEventh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casMonEventh +# undef epicsExportSharedSymbols +#endif + +#include "tsFreeList.h" +#include "smartGDDPointer.h" + +#ifdef epicsExportSharedSymbols_casMonEventh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casEvent.h" + +class casMonEvent : public casEvent { +public: + casMonEvent ( class casMonitor & monitor ); + casMonEvent ( class casMonitor & monitor, const gdd & value ); + ~casMonEvent (); + void clear (); + void assign ( const gdd & value ); + void swapValues ( casMonEvent & ); + void * operator new ( size_t size, + tsFreeList < casMonEvent, 1024, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < casMonEvent, 1024, epicsMutexNOOP > & )) +private: + class casMonitor & monitor; + smartConstGDDPointer pValue; + void * operator new ( size_t ); + void operator delete ( void * ); + caStatus cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ); + casMonEvent ( const casMonEvent & ); + casMonEvent & operator = ( const casMonEvent & ); +}; + +inline casMonEvent::casMonEvent ( class casMonitor & monitorIn ) : + monitor ( monitorIn ) {} + +inline casMonEvent::casMonEvent ( + class casMonitor & monitorIn, const gdd & value ) : + monitor ( monitorIn ), pValue ( value ) {} + +inline void casMonEvent::clear () +{ + this->pValue.set ( 0 ); +} + +inline void * casMonEvent::operator new ( size_t size, + tsFreeList < class casMonEvent, 1024, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#endif // casMonEventh + diff --git a/src/cas/generic/casMonitor.cc b/src/cas/generic/casMonitor.cc new file mode 100644 index 000000000..e2f2cb8f7 --- /dev/null +++ b/src/cas/generic/casMonitor.cc @@ -0,0 +1,174 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casMonitor.h" +#include "casChannelI.h" + +casEvent::~casEvent () {} + +casMonitor::casMonitor ( + caResId clientIdIn, + casChannelI & chan, + ca_uint32_t nElemIn, + unsigned dbrTypeIn, + const casEventMask & maskIn, + casMonitorCallbackInterface & cb ) : + overFlowEvent ( *this ), + nElem ( nElemIn ), + pChannel ( & chan ), + callBackIntf ( cb ), + mask ( maskIn ), + clientId ( clientIdIn ), + dbrType ( static_cast ( dbrTypeIn ) ), + nPend ( 0u ), + ovf ( false ) +{ + assert ( dbrTypeIn <= 0xff ); +} + +casMonitor::~casMonitor() +{ +} + +caStatus casMonitor::response ( + epicsGuard < casClientMutex > & guard, + casCoreClient & client, const gdd & value ) +{ + if ( this->pChannel ) { + // reconstruct request header + caHdrLargeArray msg; + msg.m_cmmd = CA_PROTO_EVENT_ADD; + msg.m_postsize = 0u; + msg.m_dataType = this->dbrType; + msg.m_count = this->nElem; + msg.m_cid = this->pChannel->getSID(); + msg.m_available = this->clientId; + return client.monitorResponse ( + guard, *this->pChannel, msg, value, S_cas_success ); + } + else { + return S_cas_success; + } +} + +void casMonitor::installNewEventLog ( + tsDLList < casEvent > & eventLogQue, + casMonEvent * pLog, const gdd & event ) +{ + if ( this->ovf ) { + if ( pLog ) { + pLog->assign ( event ); + this->overFlowEvent.swapValues ( *pLog ); + eventLogQue.insertAfter ( *pLog, this->overFlowEvent ); + assert ( this->nPend != UCHAR_MAX ); + this->nPend++; // X aCC 818 + } + else { + // replace the old OVF value with the current one + this->overFlowEvent.assign ( event ); + } + // remove OVF entry (with its new value) from the queue so + // that it ends up properly ordered at the back of the + // queue + eventLogQue.remove ( this->overFlowEvent ); + pLog = & this->overFlowEvent; + } + else { + if ( pLog == 0 ) { + // use the over flow block in the event structure + this->ovf = true; + pLog = & this->overFlowEvent; + } + pLog->assign ( event ); + assert ( this->nPend != UCHAR_MAX ); + this->nPend++; // X aCC 818 + } + eventLogQue.add ( *pLog ); +} + +caStatus casMonitor::executeEvent ( casCoreClient & client, + casMonEvent & ev, const gdd & value, + epicsGuard < casClientMutex > & clientGuard, + epicsGuard < evSysMutex > & evGuard ) +{ + if ( this->pChannel ) { + caStatus status = this->callBackIntf.casMonitorCallBack ( + clientGuard, *this, value ); + if ( status != S_cas_success ) { + return status; + } + } + + client.getCAS().incrEventsProcessedCounter (); + assert ( this->nPend != 0u ); + this->nPend--; // X aCC 818 + + // delete event object if it isnt a cache entry + // saved in the call back object + if ( & ev == & this->overFlowEvent ) { + assert ( this->ovf ); + this->ovf = false; + this->overFlowEvent.clear (); + } + else { + client.casMonEventDestroy ( ev, evGuard ); + } + + if ( ! this->pChannel && this->nPend == 0 ) { + // we carefully avoid inverting the lock hierarchy here + epicsGuardRelease < evSysMutex > unGuardEv ( evGuard ); + { + epicsGuardRelease < casClientMutex > unGuardClient ( clientGuard ); + client.destroyMonitor ( *this ); + } + } + + return S_cas_success; +} + +void casMonitor::show ( unsigned level ) const +{ + if ( level > 1u ) { + printf ( +"\tmonitor type=%u count=%u client id=%u OVF=%u nPend=%u\n", + dbrType, nElem, clientId, ovf, nPend ); + this->mask.show ( level ); + } +} + +void * casMonitor::operator new ( + size_t size, + tsFreeList < casMonitor, 1024 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +void casMonitor::operator delete ( void * pCadaver, + tsFreeList < casMonitor, 1024 > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +void casMonitor::operator delete ( void * ) +{ + errlogPrintf ( "casMonitor: compiler is confused " + "about placement delete?\n" ); +} diff --git a/src/cas/generic/casMonitor.h b/src/cas/generic/casMonitor.h new file mode 100644 index 000000000..6e23af17f --- /dev/null +++ b/src/cas/generic/casMonitor.h @@ -0,0 +1,116 @@ + + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casMonitorh +#define casMonitorh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casMonitorh +# undef epicsExportSharedSymbols +#endif + +#include "tsDLList.h" + +#ifdef epicsExportSharedSymbols_casMonitorh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + + +#include "caHdrLargeArray.h" +#include "casMonEvent.h" + +class casMonitor; +class casClientMutex; + +class casMonitorCallbackInterface { // X aCC 655 +public: + virtual caStatus casMonitorCallBack ( + epicsGuard < casClientMutex > &, casMonitor &, + const gdd & ) = 0; +protected: + virtual ~casMonitorCallbackInterface() {} +}; + +class casEvent; + +class casMonitor : public tsDLNode < casMonitor > { +public: + casMonitor ( caResId clientIdIn, casChannelI & chan, + ca_uint32_t nElem, unsigned dbrType, + const casEventMask & maskIn, + casMonitorCallbackInterface & ); + virtual ~casMonitor(); + void markDestroyPending (); + void installNewEventLog ( + tsDLList < casEvent > & eventLogQue, + casMonEvent * pLog, const gdd & event ); + void show ( unsigned level ) const; + bool selected ( const casEventMask & select ) const; + bool matchingClientId ( caResId clientIdIn ) const; + unsigned numEventsQueued () const; + caStatus response ( + epicsGuard < casClientMutex > &, casCoreClient & client, + const gdd & value ); + caStatus executeEvent ( casCoreClient &, + casMonEvent &, const gdd &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ); + void * operator new ( size_t size, + tsFreeList < casMonitor, 1024 > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < casMonitor, 1024 > & )) +private: + casMonEvent overFlowEvent; + ca_uint32_t const nElem; + casChannelI * pChannel; + casMonitorCallbackInterface & callBackIntf; + const casEventMask mask; + caResId const clientId; + unsigned char const dbrType; + unsigned char nPend; + bool destroyPending; + bool ovf; + void * operator new ( size_t ); + void operator delete ( void * ); + casMonitor ( const casMonitor & ); + casMonitor & operator = ( const casMonitor & ); +}; + +inline unsigned casMonitor::numEventsQueued () const +{ + return this->nPend; +} + +inline void casMonitor::markDestroyPending () +{ this->pChannel = 0; +} + +inline bool casMonitor::matchingClientId ( caResId clientIdIn ) const +{ + return clientIdIn == this->clientId; +} + +inline bool casMonitor::selected ( const casEventMask & select ) const +{ + casEventMask result ( select & this->mask ); + return result.eventsSelected () && this->pChannel; +} + +#endif // casMonitorh diff --git a/src/cas/generic/casOpaqueAddr.cc b/src/cas/generic/casOpaqueAddr.cc new file mode 100644 index 000000000..02f2c870f --- /dev/null +++ b/src/cas/generic/casOpaqueAddr.cc @@ -0,0 +1,38 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "casdef.h" + +// +// this needs to be here (and not in casOpaqueAddrIL.h) if we +// are to avoid undefined symbols under gcc 2.7.x without -O +// +// +// casOpaqueAddr::casOpaqueAddr() +// +casOpaqueAddr::casOpaqueAddr() +{ + this->clear(); +} + +// +// this needs to be here (and not in casOpaqueAddrIL.h) if we +// are to avoid undefined symbols under gcc 2.7.x without -O +// +// +// casOpaqueAddr::clear() +// +void casOpaqueAddr::clear() +{ + this->init = false; +} + + diff --git a/src/cas/generic/casOpaqueAddrIL.h b/src/cas/generic/casOpaqueAddrIL.h new file mode 100644 index 000000000..91e0235d6 --- /dev/null +++ b/src/cas/generic/casOpaqueAddrIL.h @@ -0,0 +1,55 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef casOpaqueAddrILh +#define casOpaqueAddrILh + +// +// casOpaqueAddr::set() +// +inline void casOpaqueAddr::set (const caAddr &addr) +{ + caAddr *p = (caAddr *) this->opaqueAddr; + + // + // see class casOpaqueAddr::checkSize + // for assert fail when + // sizeof(casOpaqueAddr::opaqueAddr) < sizeof(caAddr) + // + *p = addr; + this->init = true; +} + +// +// casOpaqueAddr::set() +// +inline casOpaqueAddr::casOpaqueAddr (const caAddr &addr) +{ + this->set(addr); +} + +// +// casOpaqueAddr::get() +// +inline caAddr casOpaqueAddr::get () const +{ + caAddr *p = (caAddr *) this->opaqueAddr; + + assert ( this->init ); + // + // see class casOpaqueAddr::checkSize + // for assert fail when + // sizeof(casOpaqueAddr::opaqueAddr) < sizeof(caAddr) + // + return *p; +} + +#endif // casOpaqueAddrILh + diff --git a/src/cas/generic/casPV.cc b/src/cas/generic/casPV.cc new file mode 100644 index 000000000..5d59eb653 --- /dev/null +++ b/src/cas/generic/casPV.cc @@ -0,0 +1,186 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#define epicsExportSharedSymbols +#include "casPVI.h" + +casPV::casPV () : + pPVI ( 0 ) +{ +} + +// +// This constructor is preserved for backwards compatibility only. +// Please do _not_ use this constructor. +// +casPV::casPV ( caServer & ) : + pPVI ( 0 ) +{ +} + +casPV::~casPV () +{ + if ( this->pPVI ) { + this->pPVI->casPVDestroyNotify (); + } +} + +void casPV::destroyRequest () +{ + this->pPVI = 0; + this->destroy (); +} + +// +// casPV::destroy() +// +// (this default action will _never_ be called while in the +// casPVI destructor) +// +void casPV::destroy () +{ + delete this; +} + +// +// casPV::createChannel() +// +casChannel *casPV::createChannel ( + const casCtx &ctx, const char * const, const char * const ) +{ + return new casChannel ( ctx ); +} + +// +// casPV::interestRegister() +// +caStatus casPV::interestRegister () +{ + return S_casApp_success; +} + +// +// casPV::interestDelete() +// +void casPV::interestDelete () +{ +} + +// +// casPV::beginTransaction() +// +caStatus casPV::beginTransaction () +{ + return S_casApp_success; +} + +// +// casPV::endTransaction() +// +void casPV::endTransaction () +{ +} + +// +// casPV::read() +// +caStatus casPV::read (const casCtx &, gdd &) +{ + return S_casApp_noSupport; +} + +// +// casPV::write() +// +caStatus casPV::write (const casCtx &, const gdd &) +{ + return S_casApp_noSupport; +} + +// +// casPV::writeNotify() +// +caStatus casPV :: writeNotify ( + const casCtx & ctx, const gdd & val ) +{ + // plumbed this way to preserve backwards + // compatibility with the old interface which + // did not include a writeNotify interface + return this->write ( ctx, val ); +} + +// +// casPV::bestExternalType() +// +aitEnum casPV::bestExternalType () const +{ + return aitEnumString; +} + +// +// casPV::maxDimension() +// (base returns zero - scalar) +// +unsigned casPV::maxDimension () const +{ + return 0u; +} + +// +// casPV::maxBound() +// (base returns scalar bound independent of the dimension arg) +// +aitIndex casPV::maxBound (unsigned /* dimension */) const +{ + return 1u; +} + +// +// casPV::show (unsigned level) +// +void casPV::show ( unsigned /* level */ ) const +{ +} + +// +// Server tool calls this function to post a PV event. +// +void casPV::postEvent ( const casEventMask &select, const gdd &event ) +{ + if ( this->pPVI ) { + this->pPVI->postEvent ( select, event ); + } +} + +// +// Find the server associated with this PV +// ****WARNING**** +// this returns NULL if the PV isnt currently installed +// into a server. +// *************** +// +caServer * casPV::getCAS () const +{ + if ( this->pPVI ) { + return this->pPVI->getExtServer (); + } + else { + return 0; + } +} + + diff --git a/src/cas/generic/casPVI.cc b/src/cas/generic/casPVI.cc new file mode 100644 index 000000000..cd08b4f9a --- /dev/null +++ b/src/cas/generic/casPVI.cc @@ -0,0 +1,532 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "epicsGuard.h" +#include "gddAppTable.h" // EPICS application type table +#include "gddApps.h" +#include "dbMapper.h" // EPICS application type table +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "caServerDefs.h" +#include "caServerI.h" +#include "casPVI.h" +#include "chanIntfForPV.h" +#include "casAsyncIOI.h" +#include "casMonitor.h" + +casPVI::casPVI ( casPV & intf ) : + pCAS ( NULL ), pPV ( & intf ), nMonAttached ( 0u ), + nIOAttached ( 0u ), deletePending ( false ) {} + +casPVI::~casPVI () +{ + // + // all channels should have been destroyed + // (otherwise the server tool is yanking the + // PV out from under the server) + // + casVerify ( this->chanList.count() == 0u ); + + // + // all outstanding IO should have been deleted + // when we destroyed the channels + // + casVerify ( this->nIOAttached == 0u ); + if ( this->nIOAttached ) { + errlogPrintf ( "The number of IO objected attached is %u\n", this->nIOAttached ); + } + + // + // all monitors should have been deleted + // when we destroyed the channels + // + casVerify ( this->nMonAttached == 0u ); + + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->deletePending = true; + if ( this->pPV ) { + this->pPV->destroyRequest (); + } + } +} + +void casPVI::casPVDestroyNotify () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->pPV = 0; + if ( ! this->deletePending ) { + // last channel to be destroyed destroys the casPVI + tsDLIter < chanIntfForPV > iter = this->chanList.firstIter (); + while ( iter.valid() ) { + iter->postDestroyEvent (); + iter++; + } + } +} + +// +// check for none attached and delete self if so +// +// this call must be protected by the server's lock +// ( which also protects channel creation) +// +void casPVI::deleteSignal () +{ + bool destroyNeeded = false; + { + epicsGuard < epicsMutex > guard ( this->mutex ); + + // + // if we are not attached to a server then the + // following steps are not relevant + // + if ( this->pCAS ) { + if ( this->chanList.count() == 0u ) { + this->pCAS = NULL; + // refresh the table whenever the server reattaches to the PV + this->enumStrTbl.clear (); + destroyNeeded = true; + } + } + } + + if ( destroyNeeded ) { + delete this; + } + + // !! dont access self after potential delete above !! +} + +caStatus casPVI::attachToServer ( caServerI & cas ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pCAS ) { + // + // currently we enforce that the PV can be attached to only + // one server at a time + // + if ( this->pCAS != & cas ) { + return S_cas_pvAlreadyAttached; + } + } + else { + this->pCAS = & cas; + } + return S_cas_success; +} + +// +// casPVI::updateEnumStringTable () +// +// fetch string conversion table so that we can perform proper conversion +// of enumerated PVs to strings during reads +// +// what a API complexity nightmare this GDD is +// +caStatus casPVI::updateEnumStringTable ( casCtx & ctxIn ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + // + // create a gdd with the "enum string table" application type + // + // gddArray(int app, aitEnum prim, int dimen, ...); + gdd * pTmp = new gddScalar ( gddAppType_enums ); + if ( pTmp == NULL ) { + errMessage ( S_cas_noMemory, + "unable to create gdd for read of application type \"enums\" string" + " conversion table for enumerated PV" ); + return S_cas_noMemory; + } + + caStatus status = convertContainerMemberToAtomic ( *pTmp, + gddAppType_enums, MAX_ENUM_STATES ); + if ( status != S_cas_success ) { + pTmp->unreference (); + errMessage ( status, + "unable to to config gdd for read of application type \"enums\" string" + " conversion table for enumerated PV"); + return status; + } + + // + // read the enum string table + // + status = this->read ( ctxIn, *pTmp ); + if ( status == S_cas_success ) { + updateEnumStringTableAsyncCompletion ( *pTmp ); + } + else if ( status != S_casApp_asyncCompletion && + status != S_casApp_postponeAsyncIO ) { + errMessage ( status, + "- unable to read application type \"enums\" string" + " conversion table for enumerated PV"); + } + + pTmp->unreference (); + + return status; +} + +void casPVI::updateEnumStringTableAsyncCompletion ( const gdd & resp ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + if ( resp.isContainer() ) { + errMessage ( S_cas_badType, + "application type \"enums\" string conversion table for" + " enumerated PV was a container (expected vector of strings)" ); + return; + } + + if ( resp.dimension() == 0 ) { + if ( resp.primitiveType() == aitEnumString ) { + aitString *pStr = (aitString *) resp.dataVoid (); + if ( ! this->enumStrTbl.setString ( 0, pStr->string() ) ) { + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); + } + } + else if ( resp.primitiveType() == aitEnumFixedString ) { + aitFixedString *pStr = (aitFixedString *) resp.dataVoid (); + if ( ! this->enumStrTbl.setString ( 0, pStr->fixed_string ) ) { + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); + } + } + else { + errMessage ( S_cas_badType, + "application type \"enums\" string conversion" + " table for enumerated PV isnt a string type?" ); + } + } + else if ( resp.dimension() == 1 ) { + gddStatus gdd_status; + aitIndex index, first, count; + + gdd_status = resp.getBound ( 0, first, count ); + assert ( gdd_status == 0 ); + + // + // preallocate the correct amount + // + this->enumStrTbl.reserve ( count ); + + if ( resp.primitiveType() == aitEnumString ) { + aitString *pStr = (aitString *) resp.dataVoid (); + for ( index = 0; indexenumStrTbl.setString ( index, pStr[index].string() ) ) { + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); + } + } + } + else if ( resp.primitiveType() == aitEnumFixedString ) { + aitFixedString *pStr = (aitFixedString *) resp.dataVoid (); + for ( index = 0; index < count; index++ ) { + if ( ! this->enumStrTbl.setString ( index, pStr[index].fixed_string ) ) { + errMessage ( S_cas_noMemory, + "no memory to set enumerated PV string cache" ); + } + } + } + else { + errMessage ( S_cas_badType, + "application type \"enums\" string conversion" + " table for enumerated PV isnt a string type?" ); + } + } + else { + errMessage ( S_cas_badType, + "application type \"enums\" string conversion table" + " for enumerated PV was multi-dimensional" + " (expected vector of strings)" ); + } +} + +void casPVI::postEvent ( const casEventMask & select, const gdd & event ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->nMonAttached ) { + // we are paying some significant locking overhead for + // these diagnostic counters + this->pCAS->updateEventsPostedCounter ( this->nMonAttached ); + tsDLIter < chanIntfForPV > iter = this->chanList.firstIter (); + while ( iter.valid () ) { + iter->postEvent ( select, event ); + ++iter; + } + } +} + +caStatus casPVI::installMonitor ( + casMonitor & mon, tsDLList < casMonitor > & monitorList ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + assert ( this->nMonAttached < UINT_MAX ); + this->nMonAttached++; + // use pv lock to protect channel's monitor list + monitorList.add ( mon ); + if ( this->nMonAttached == 1u && this->pPV ) { + return this->pPV->interestRegister (); + } + else { + return S_cas_success; + } +} + +casMonitor * casPVI::removeMonitor ( + tsDLList < casMonitor > & list, ca_uint32_t clientIdIn ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + casMonitor * pMon = 0; + // + // (it is reasonable to do a linear search here because + // sane clients will require only one or two monitors + // per channel) + // + tsDLIter < casMonitor > iter = list.firstIter (); + while ( iter.valid () ) { + if ( iter->matchingClientId ( clientIdIn ) ) { + list.remove ( *iter.pointer () ); + assert ( this->nMonAttached > 0 ); + this->nMonAttached--; + pMon = iter.pointer (); + break; + } + iter++; + } + if ( this->nMonAttached == 0u && this->pPV ) { + this->pPV->interestDelete (); + } + return pMon; +} + +caServer *casPVI::getExtServer () const // X aCC 361 +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pCAS ) { + return this->pCAS->getAdapter (); + } + else { + return NULL; + } +} + +void casPVI::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + printf ( "CA Server PV: nChanAttached=%u nMonAttached=%u nIOAttached=%u\n", + this->chanList.count(), this->nMonAttached, this->nIOAttached ); + if ( level >= 1u ) { + printf ( "\tBest external type = %d\n", this->bestExternalType() ); + } + if ( level >= 2u ) { + this->pPV->show ( level - 2u ); + } +} + +void casPVI::installChannel ( chanIntfForPV & chan ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->chanList.add ( chan ); +} + +void casPVI::removeChannel ( + chanIntfForPV & chan, tsDLList < casMonitor > & src, + tsDLList < casMonitor > & dest ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + src.removeAll ( dest ); + if ( dest.count() ) { + assert ( this->nMonAttached >= dest.count() ); + this->nMonAttached -= dest.count (); + } + this->chanList.remove ( chan ); + if ( this->nMonAttached == 0u && this->pPV ) { + this->pPV->interestDelete (); + } +} + +void casPVI::clearOutstandingReads ( tsDLList < casAsyncIOI > & ioList ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + // cancel any pending asynchronous IO + tsDLIter < casAsyncIOI > iterIO = + ioList.firstIter (); + while ( iterIO.valid () ) { + tsDLIter < casAsyncIOI > tmp = iterIO; + ++tmp; + if ( iterIO->oneShotReadOP () ) { + ioList.remove ( *iterIO ); + delete iterIO.pointer (); + assert ( this->nIOAttached != 0 ); + this->nIOAttached--; + } + iterIO = tmp; + } +} + +void casPVI::destroyAllIO ( tsDLList < casAsyncIOI > & ioList ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + while ( casAsyncIOI * pIO = ioList.get() ) { + pIO->removeFromEventQueue (); + delete pIO; + assert ( this->nIOAttached != 0 ); + this->nIOAttached--; + } +} + +void casPVI::installIO ( + tsDLList < casAsyncIOI > & ioList, casAsyncIOI & io ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + ioList.add ( io ); + assert ( this->nIOAttached != UINT_MAX ); + this->nIOAttached++; +} + +void casPVI::uninstallIO ( + tsDLList < casAsyncIOI > & ioList, casAsyncIOI & io ) +{ + { + epicsGuard < epicsMutex > guard ( this->mutex ); + ioList.remove ( io ); + assert ( this->nIOAttached != 0 ); + this->nIOAttached--; + } + this->ioBlockedList::signal(); +} + +caStatus casPVI::bestDBRType ( unsigned & dbrType ) // X aCC 361 +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + aitEnum bestAIT = this->bestExternalType (); + if ( bestAIT == aitEnumInvalid || bestAIT < 0 ) { + return S_cas_badType; + } + unsigned aitIndex = static_cast < unsigned > ( bestAIT ); + if ( aitIndex >= sizeof ( gddAitToDbr ) / sizeof ( gddAitToDbr[0] ) ) { + return S_cas_badType; + } + dbrType = gddAitToDbr[bestAIT]; + return S_cas_success; +} + +caStatus casPVI::read ( const casCtx & ctx, gdd & prototype ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pPV ) { + caStatus status = this->pPV->beginTransaction (); + if ( status != S_casApp_success ) { + return status; + } + status = this->pPV->read ( ctx, prototype ); + this->pPV->endTransaction (); + return status; + } + else { + return S_cas_disconnect; + } +} + +caStatus casPVI::write ( const casCtx & ctx, const gdd & value ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pPV ) { + caStatus status = this->pPV->beginTransaction (); + if ( status != S_casApp_success ) { + return status; + } + status = this->pPV->write ( ctx, value ); + this->pPV->endTransaction (); + return status; + } + else { + return S_cas_disconnect; + } +} + +caStatus casPVI::writeNotify ( const casCtx & ctx, const gdd & value ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pPV ) { + caStatus status = this->pPV->beginTransaction (); + if ( status != S_casApp_success ) { + return status; + } + status = this->pPV->writeNotify ( ctx, value ); + this->pPV->endTransaction (); + return status; + } + else { + return S_cas_disconnect; + } +} + +casChannel * casPVI::createChannel ( const casCtx & ctx, + const char * const pUserName, const char * const pHostName ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pPV ) { + return this->pPV->createChannel ( ctx, pUserName, pHostName ); + } + else { + return 0; + } +} + +aitEnum casPVI::bestExternalType () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pPV ) { + return this->pPV->bestExternalType (); + } + else { + return aitEnumInvalid; + } +} + +// CA only does 1D arrays for now +aitIndex casPVI::nativeCount () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pPV ) { + if ( this->pPV->maxDimension() == 0u ) { + return 1u; // scalar + } + return this->pPV->maxBound ( 0u ); + } + else { + return S_cas_disconnect; + } +} + +const char * casPVI::getName () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + if ( this->pPV ) { + return this->pPV->getName (); + } + else { + return ""; + } +} + diff --git a/src/cas/generic/casPVI.h b/src/cas/generic/casPVI.h new file mode 100644 index 000000000..cadacfa89 --- /dev/null +++ b/src/cas/generic/casPVI.h @@ -0,0 +1,123 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef casPVIh +#define casPVIh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casPVIh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "tsSLList.h" +#include "epicsMutex.h" +#include "caProto.h" + +#ifdef epicsExportSharedSymbols_casPVIh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casdef.h" +#include "ioBlocked.h" + +class chanIntfForPV; +class caServerI; +class casMonitor; + +class casPVI : + public tsSLNode < casPVI >, // server resource table installation + public ioBlockedList // list of clients io blocked on this pv +{ +public: + casPVI ( casPV & ); + epicsShareFunc virtual ~casPVI (); + caServerI * getPCAS () const; + caStatus attachToServer ( caServerI & cas ); + aitIndex nativeCount (); + bool ioIsPending () const; + void clearOutstandingReads ( tsDLList < class casAsyncIOI > &); + void destroyAllIO ( + tsDLList < casAsyncIOI > & ); + void installIO ( + tsDLList < casAsyncIOI > &, casAsyncIOI & ); + void uninstallIO ( + tsDLList < casAsyncIOI > &, casAsyncIOI & ); + void installChannel ( chanIntfForPV & chan ); + void removeChannel ( + chanIntfForPV & chan, tsDLList < casMonitor > & src, + tsDLList < casMonitor > & dest ); + caStatus installMonitor ( + casMonitor & mon, tsDLList < casMonitor > & monitorList ); + casMonitor * removeMonitor ( + tsDLList < casMonitor > & list, ca_uint32_t clientIdIn ); + void deleteSignal (); + void postEvent ( const casEventMask & select, const gdd & event ); + caServer * getExtServer () const; + caStatus bestDBRType ( unsigned & dbrType ); + const gddEnumStringTable & enumStringTable () const; + caStatus updateEnumStringTable ( casCtx & ); + void updateEnumStringTableAsyncCompletion ( const gdd & resp ); + casPV * apiPointer (); // retuns NULL if casPVI isnt a base of casPV + void show ( unsigned level ) const; + caStatus read ( const casCtx & ctx, gdd & prototype ); + caStatus write ( const casCtx & ctx, const gdd & value ); + caStatus writeNotify ( const casCtx & ctx, const gdd & value ); + casChannel * createChannel ( const casCtx & ctx, + const char * const pUserName, const char * const pHostName ); + aitEnum bestExternalType () const; + const char * getName () const; + void casPVDestroyNotify (); + +private: + mutable epicsMutex mutex; + tsDLList < chanIntfForPV > chanList; + gddEnumStringTable enumStrTbl; + caServerI * pCAS; + casPV * pPV; + unsigned nMonAttached; + unsigned nIOAttached; + bool deletePending; + + casPVI ( const casPVI & ); + casPVI & operator = ( const casPVI & ); +}; + +inline caServerI * casPVI::getPCAS() const +{ + return this->pCAS; +} + +inline const gddEnumStringTable & casPVI::enumStringTable () const +{ + return this->enumStrTbl; +} + +inline casPV * casPVI::apiPointer () +{ + return this->pPV; +} + +inline bool casPVI :: ioIsPending () const +{ + return this->nIOAttached > 0u; +} + +#endif // casPVIh + diff --git a/src/cas/generic/casStrmClient.cc b/src/cas/generic/casStrmClient.cc new file mode 100644 index 000000000..4ecb290b0 --- /dev/null +++ b/src/cas/generic/casStrmClient.cc @@ -0,0 +1,2889 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author Jeffrey O. Hill + */ + +// *must* be defined before including net_convert.h +typedef unsigned long arrayElementCount; + +#include "osiWireFormat.h" +#include "net_convert.h" // byte order conversion from libca +#include "dbMapper.h" // ait to dbr types +#include "gddAppTable.h" // EPICS application type table +#include "gddApps.h" // gdd predefined application type codes +#include "errlog.h" +#include "osiPoolStatus.h" // is there sufficent space in pool + +#define epicsExportSharedSymbols +#include "casStrmClient.h" +#include "casChannelI.h" +#include "casAsyncIOI.h" +#include "channelDestroyEvent.h" + +#if defined(__BORLANDC__) && defined(__linux__) +namespace std { +const nothrow_t nothrow ; +} +#endif + +static const caHdr nill_msg = { 0u, 0u, 0u, 0u, 0u, 0u }; + +casStrmClient::pCASMsgHandler const casStrmClient::msgHandlers[] = +{ + & casStrmClient::versionAction, + & casStrmClient::eventAddAction, + & casStrmClient::eventCancelAction, + & casStrmClient::readAction, + & casStrmClient::writeAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::searchAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::eventsOffAction, + & casStrmClient::eventsOnAction, + & casStrmClient::readSyncAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::clearChannelAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::readNotifyAction, + & casStrmClient::ignoreMsgAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::claimChannelAction, + & casStrmClient::writeNotifyAction, + & casStrmClient::clientNameAction, + & casStrmClient::hostNameAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::echoAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::uknownMessageAction, + & casStrmClient::uknownMessageAction +}; + +// +// casStrmClient::casStrmClient() +// +casStrmClient::casStrmClient ( + caServerI & cas, clientBufMemoryManager & mgrIn, + const caNetAddr & clientAddr ) : + casCoreClient ( cas ), + in ( *this, mgrIn, 1 ), + out ( *this, mgrIn ), + _clientAddr ( clientAddr ), + pUserName ( 0 ), + pHostName ( 0 ), + incommingBytesToDrain ( 0 ), + pendingResponseStatus ( S_cas_success ), + minor_version_number ( 0 ), + reqPayloadNeedsByteSwap ( true ), + responseIsPending ( false ) +{ + this->pHostName = new char [1u]; + *this->pHostName = '\0'; + + this->pUserName = new ( std::nothrow ) char [1u]; + if ( ! this->pUserName ) { + delete [] this->pHostName; + throw std::bad_alloc(); + } + *this->pUserName= '\0'; +} + +// +// casStrmClient::~casStrmClient () +// +casStrmClient::~casStrmClient () +{ + while ( casChannelI * pChan = this->chanList.get() ) { + pChan->uninstallFromPV ( this->eventSys ); + this->chanTable.remove ( *pChan ); + delete pChan; + } + delete [] this->pUserName; + delete [] this->pHostName; +} + +// +// casStrmClient :: processMsg () +// +caStatus casStrmClient :: processMsg () +{ + epicsGuard < casClientMutex > guard ( this->mutex ); + int status = S_cas_success; + + // protect against service returning s_casApp_success when it + // returned S_casApp_postponeAsyncIO before, but no + // asyn IO completed since the last attempt + if ( this->isBlocked () ) { + return S_casApp_postponeAsyncIO; + } + + try { + + // drain message that does not fit + if ( this->incommingBytesToDrain ) { + unsigned bytesLeft = this->in.bytesPresent(); + if ( bytesLeft < this->incommingBytesToDrain ) { + this->in.removeMsg ( bytesLeft ); + this->incommingBytesToDrain -= bytesLeft; + return S_cas_success; + } + else { + this->in.removeMsg ( this->incommingBytesToDrain ); + this->incommingBytesToDrain = 0u; + } + } + + // + // process any messages in the in buffer + // + unsigned bytesLeft; + while ( ( bytesLeft = this->in.bytesPresent() ) ) { + caHdrLargeArray msgTmp; + unsigned msgSize; + ca_uint32_t hdrSize; + char * rawMP; + { + // + // copy as raw bytes in order to avoid + // alignment problems + // + caHdr smallHdr; + if ( bytesLeft < sizeof ( smallHdr ) ) { + status = S_cas_success; + break; + } + + rawMP = this->in.msgPtr (); + memcpy ( & smallHdr, rawMP, sizeof ( smallHdr ) ); + + ca_uint32_t payloadSize = AlignedWireRef < epicsUInt16 > ( smallHdr.m_postsize ); + ca_uint32_t nElem = AlignedWireRef < epicsUInt16 > ( smallHdr.m_count ); + if ( payloadSize != 0xffff && nElem != 0xffff ) { + hdrSize = sizeof ( smallHdr ); + } + else { + ca_uint32_t LWA[2]; + hdrSize = sizeof ( smallHdr ) + sizeof ( LWA ); + if ( bytesLeft < hdrSize ) { + status = S_cas_success; + break; + } + // + // copy as raw bytes in order to avoid + // alignment problems + // + memcpy ( LWA, rawMP + sizeof ( caHdr ), sizeof( LWA ) ); + payloadSize = AlignedWireRef < epicsUInt32 > ( LWA[0] ); + nElem = AlignedWireRef < epicsUInt32 > ( LWA[1] ); + } + + msgTmp.m_cmmd = AlignedWireRef < epicsUInt16 > ( smallHdr.m_cmmd ); + msgTmp.m_postsize = payloadSize; + msgTmp.m_dataType = AlignedWireRef < epicsUInt16 > ( smallHdr.m_dataType ); + msgTmp.m_count = nElem; + msgTmp.m_cid = AlignedWireRef < epicsUInt32 > ( smallHdr.m_cid ); + msgTmp.m_available = AlignedWireRef < epicsUInt32 > ( smallHdr.m_available ); + + // disconnect clients that dont send 8 byte aligned payloads + if ( payloadSize & 0x7 ) { + caServerI::dumpMsg ( this->pHostName, this->pUserName, & msgTmp, 0, + "CAS: Stream request wasn't 8 byte aligned\n" ); + this->sendErr ( guard, & msgTmp, invalidResID, ECA_INTERNAL, + "Stream request wasn't 8 byte aligned" ); + status = S_cas_internal; + break; + } + + msgSize = hdrSize + payloadSize; + if ( bytesLeft < msgSize ) { + status = S_cas_success; + if ( msgSize > this->in.bufferSize() ) { + this->in.expandBuffer (); + // msg to large - set up message drain + if ( msgSize > this->in.bufferSize() ) { + caServerI::dumpMsg ( this->pHostName, this->pUserName, & msgTmp, 0, + "The client requested transfer is greater than available " + "memory in server or EPICS_CA_MAX_ARRAY_BYTES\n" ); + status = this->sendErr ( guard, & msgTmp, invalidResID, ECA_TOLARGE, + "client's request didnt fit within the CA server's message buffer" ); + if ( status == S_cas_success ) { + this->in.removeMsg ( bytesLeft ); + this->incommingBytesToDrain = msgSize - bytesLeft; + } + } + } + break; + } + + this->ctx.setMsg ( msgTmp, rawMP + hdrSize ); + + if ( this->getCAS().getDebugLevel() > 2u ) { + caServerI::dumpMsg ( this->pHostName, this->pUserName, + & msgTmp, rawMP + hdrSize, 0 ); + } + } + + // + // Reset the context to the default + // (guarantees that previous message does not get mixed + // up with the current message) + // + this->ctx.setChannel ( NULL ); + this->ctx.setPV ( NULL ); + + // + // Call protocol stub + // + casStrmClient::pCASMsgHandler pHandler; + if ( msgTmp.m_cmmd < NELEMENTS ( casStrmClient::msgHandlers ) ) { + pHandler = this->casStrmClient::msgHandlers[msgTmp.m_cmmd]; + } + else { + pHandler = & casStrmClient::uknownMessageAction; + } + status = ( this->*pHandler ) ( guard ); + if ( status ) { + break; + } + this->in.removeMsg ( msgSize ); + this->pendingResponseStatus = S_cas_success; + this->reqPayloadNeedsByteSwap = true; + this->responseIsPending = false; + this->pValueRead.set ( 0 ); + } + } + catch ( std::bad_alloc & ) { + this->sendErr ( guard, + this->ctx.getMsg(), invalidResID, ECA_ALLOCMEM, + "inablility to allocate memory in " + "the CA server - disconnected client" ); + status = S_cas_noMemory; + } + catch ( std::exception & except ) { + this->sendErr ( guard, + this->ctx.getMsg(), invalidResID, ECA_INTERNAL, + "C++ exception \"%s\" in server - " + "disconnected client", + except.what () ); + status = S_cas_internal; + } + catch ( ... ) { + this->sendErr ( guard, + this->ctx.getMsg(), invalidResID, ECA_INTERNAL, + "unexpected C++ exception in server " + "disconnected client" ); + status = S_cas_internal; + } + + return status; +} + +// +// casStrmClient::uknownMessageAction() +// +caStatus casStrmClient::uknownMessageAction ( epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray *mp = this->ctx.getMsg(); + caStatus status; + + caServerI::dumpMsg ( this->pHostName, + this->pUserName, mp, this->ctx.getData(), + "bad request code from virtual circuit=%u\n", mp->m_cmmd ); + + /* + * most clients dont recover from this + */ + status = this->sendErr ( guard, mp, invalidResID, + ECA_INTERNAL, "Invalid Request Code" ); + if (status) { + return status; + } + + /* + * returning S_cas_badProtocol here disconnects + * the client with the bad message + */ + return S_cas_badProtocol; +} + +/* + * casStrmClient::ignoreMsgAction() + */ +caStatus casStrmClient::ignoreMsgAction ( epicsGuard < casClientMutex > & ) +{ + return S_cas_success; +} + +// +// versionAction() +// +caStatus casStrmClient::versionAction ( epicsGuard < casClientMutex > & ) +{ +#if 1 + return S_cas_success; +#else + // + // eventually need to set the priority here + // + const caHdrLargeArray * mp = this->ctx.getMsg(); + + if ( mp->m_dataType > CA_PROTO_PRIORITY_MAX ) { + return S_cas_badProtocol; + } + + double tmp = mp->m_dataType - CA_PROTO_PRIORITY_MIN; + tmp *= epicsThreadPriorityCAServerHigh - epicsThreadPriorityCAServerLow; + tmp /= CA_PROTO_PRIORITY_MAX - CA_PROTO_PRIORITY_MIN; + tmp += epicsThreadPriorityCAServerLow; + unsigned epicsPriorityNew = (unsigned) tmp; + unsigned epicsPrioritySelf = epicsThreadGetPrioritySelf(); + if ( epicsPriorityNew != epicsPrioritySelf ) { + epicsThreadBooleanStatus tbs; + unsigned priorityOfEvents; + tbs = epicsThreadHighestPriorityLevelBelow ( epicsPriorityNew, &priorityOfEvents ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + priorityOfEvents = epicsPriorityNew; + } + + if ( epicsPriorityNew > epicsPrioritySelf ) { + epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsPriorityNew ); + db_event_change_priority ( client->evuser, priorityOfEvents ); + } + else { + db_event_change_priority ( client->evuser, priorityOfEvents ); + epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsPriorityNew ); + } + client->priority = mp->m_dataType; + } + return S_cas_success; +#endif +} + +// +// echoAction() +// +caStatus casStrmClient::echoAction ( epicsGuard < casClientMutex > & ) +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + const void * dp = this->ctx.getData(); + void * pPayloadOut; + + caStatus status = this->out.copyInHeader ( mp->m_cmmd, mp->m_postsize, + mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, + & pPayloadOut ); + if ( ! status ) { + memcpy ( pPayloadOut, dp, mp->m_postsize ); + this->out.commitMsg (); + } + return S_cas_success; +} + +// +// casStrmClient::verifyRequest() +// +caStatus casStrmClient::verifyRequest ( casChannelI * & pChan ) +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + + // + // channel exists for this resource id ? + // + chronIntId tmpId ( mp->m_cid ); + pChan = this->chanTable.lookup ( tmpId ); + if ( ! pChan ) { + return ECA_BADCHID; + } + + // + // data type out of range ? + // + if ( mp->m_dataType > ((unsigned)LAST_BUFFER_TYPE) ) { + return ECA_BADTYPE; + } + + // + // element count out of range ? + // + if ( mp->m_count > pChan->getPVI().nativeCount() || mp->m_count == 0u ) { + return ECA_BADCOUNT; + } + + this->ctx.setChannel ( pChan ); + this->ctx.setPV ( &pChan->getPVI() ); + + return ECA_NORMAL; +} + +void casStrmClient::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > locker ( this->mutex ); + printf ( "casStrmClient at %p\n", + static_cast ( this ) ); + if ( level > 1u ) { + printf ("\tuser %s at %s\n", this->pUserName, this->pHostName); + this->casCoreClient::show ( level - 1 ); + this->in.show ( level - 1 ); + this->out.show ( level - 1 ); + this->chanTable.show ( level - 1 ); + } +} + +/* + * casStrmClient::readAction() + */ +caStatus casStrmClient::readAction ( epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + casChannelI * pChan; + + { + caStatus status = this->verifyRequest ( pChan ); + if ( status != ECA_NORMAL ) { + if ( pChan ) { + return this->sendErr ( guard, mp, pChan->getCID(), + status, "get request" ); + } + else { + return this->sendErr ( guard, mp, invalidResID, + status, "get request" ); + } + } + } + + // dont allow a request that completed with the service in the + // past, but was incomplete because no response was sent to be + // executed twice with the service + if ( this->responseIsPending ) { + // dont read twice if we didnt finish in the past + // because we were send blocked + if ( this->pendingResponseStatus == S_cas_success ) { + assert ( pValueRead.valid () ); + return this->readResponse ( guard, pChan, *mp, + *pValueRead, S_cas_success ); + } + else { + return this->sendErrWithEpicsStatus ( + guard, mp, pChan->getCID(), + this->pendingResponseStatus, + ECA_GETFAIL ); + } + } + + /* + * verify read access + */ + if ( ! pChan->readAccess() ) { + int v41 = CA_V41 ( this->minor_version_number ); + int cacStatus; + if ( v41 ) { + cacStatus = ECA_NORDACCESS; + } + else{ + cacStatus = ECA_GETFAIL; + } + return this->sendErr ( guard, mp, pChan->getCID(), + cacStatus, "read access denied" ); + } + + { + caStatus servStat = this->read (); + if ( servStat == S_casApp_success ) { + assert ( pValueRead.valid () ); + caStatus status = this->readResponse ( guard, pChan, *mp, + *pValueRead, S_cas_success ); + this->responseIsPending = ( status != S_cas_success ); + return status; + } + else if ( servStat == S_casApp_asyncCompletion ) { + return S_cas_success; + } + else if ( servStat == S_casApp_postponeAsyncIO ) { + return S_casApp_postponeAsyncIO; + } + else { + caStatus status = this->sendErrWithEpicsStatus ( guard, mp, + pChan->getCID(), servStat, ECA_GETFAIL ); + if ( status != S_cas_success ) { + this->pendingResponseStatus = servStat; + this->responseIsPending = true; + } + return status; + } + } +} + +// +// casStrmClient::readResponse() +// +caStatus casStrmClient::readResponse ( epicsGuard < casClientMutex > & guard, + casChannelI * pChan, const caHdrLargeArray & msg, + const gdd & desc, const caStatus status ) +{ + if ( status != S_casApp_success ) { + return this->sendErrWithEpicsStatus ( guard, & msg, + pChan->getCID(), status, ECA_GETFAIL ); + } + + void * pPayload; + { + unsigned payloadSize = dbr_size_n ( msg.m_dataType, msg.m_count ); + caStatus localStatus = this->out.copyInHeader ( msg.m_cmmd, payloadSize, + msg.m_dataType, msg.m_count, pChan->getCID (), + msg.m_available, & pPayload ); + if ( localStatus ) { + if ( localStatus==S_cas_hugeRequest ) { + localStatus = sendErr ( guard, & msg, pChan->getCID(), ECA_TOLARGE, + "unable to fit read response into server's buffer" ); + } + return localStatus; + } + } + + // + // convert gdd to db_access type + // (places the data in network format) + // + int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr( + pPayload, msg.m_count, desc, pChan->enumStringTable() ); + if ( mapDBRStatus < 0 ) { + desc.dump (); + errPrintf ( S_cas_badBounds, __FILE__, __LINE__, "- get with PV=%s type=%u count=%u", + pChan->getPVI().getName(), msg.m_dataType, msg.m_count ); + return this->sendErrWithEpicsStatus ( + guard, & msg, pChan->getCID(), S_cas_badBounds, ECA_GETFAIL ); + } + int cacStatus = caNetConvert ( + msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + if ( cacStatus != ECA_NORMAL ) { + return this->sendErrWithEpicsStatus ( + guard, & msg, pChan->getCID(), S_cas_internal, cacStatus ); + } + if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; + this->out.commitMsg ( reducedPayloadSize ); + } + else { + this->out.commitMsg (); + } + + return S_cas_success; +} + +// +// casStrmClient::readNotifyAction() +// +caStatus casStrmClient::readNotifyAction ( epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + casChannelI * pChan; + + { + caStatus status = this->verifyRequest ( pChan ); + if ( status != ECA_NORMAL ) { + return this->readNotifyFailureResponse ( guard, * mp, status ); + } + } + + // dont allow a request that completed with the service in the + // past, but was incomplete because no response was sent to be + // executed twice with the service + if ( this->responseIsPending ) { + // dont read twice if we didnt finish in the past + // because we were send blocked + if ( this->pendingResponseStatus == S_cas_success ) { + assert ( pValueRead.valid () ); + return this->readNotifyResponse ( guard, pChan, *mp, + *pValueRead, S_cas_success ); + } + else { + return this->readNotifyFailureResponse ( + guard, *mp, ECA_GETFAIL ); + } + } + + // + // verify read access + // + if ( ! pChan->readAccess() ) { + return this->readNotifyFailureResponse ( guard, *mp, ECA_NORDACCESS ); + } + + { + caStatus servStat = this->read (); + if ( servStat == S_casApp_success ) { + assert ( pValueRead.valid () ); + caStatus status = this->readNotifyResponse ( + guard, pChan, *mp, + *pValueRead, servStat ); + this->responseIsPending = ( status != S_cas_success ); + return status; + } + else if ( servStat == S_casApp_asyncCompletion ) { + return S_cas_success; + } + else if ( servStat == S_casApp_postponeAsyncIO ) { + return S_casApp_postponeAsyncIO; + } + else { + caStatus status = this->readNotifyFailureResponse ( + guard, *mp, ECA_GETFAIL ); + if ( status != S_cas_success ) { + this->pendingResponseStatus = servStat; + this->responseIsPending = true; + } + return status; + } + } +} + +// +// casStrmClient::readNotifyResponse() +// +caStatus casStrmClient::readNotifyResponse ( epicsGuard < casClientMutex > & guard, + casChannelI * pChan, const caHdrLargeArray & msg, const gdd & desc, + const caStatus completionStatus ) +{ + if ( completionStatus != S_cas_success ) { + caStatus ecaStatus = this->readNotifyFailureResponse ( + guard, msg, ECA_GETFAIL ); + return ecaStatus; + } + + void *pPayload; + { + unsigned size = dbr_size_n ( msg.m_dataType, msg.m_count ); + caStatus status = this->out.copyInHeader ( msg.m_cmmd, size, + msg.m_dataType, msg.m_count, ECA_NORMAL, + msg.m_available, & pPayload ); + if ( status ) { + if ( status == S_cas_hugeRequest ) { + status = sendErr ( guard, & msg, pChan->getCID(), ECA_TOLARGE, + "unable to fit read notify response into server's buffer" ); + } + return status; + } + } + + // + // convert gdd to db_access type + // + int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr ( pPayload, + msg.m_count, desc, pChan->enumStringTable() ); + if ( mapDBRStatus < 0 ) { + desc.dump(); + errPrintf ( S_cas_badBounds, __FILE__, __LINE__, + "- get notify with PV=%s type=%u count=%u", + pChan->getPVI().getName(), msg.m_dataType, msg.m_count ); + return this->readNotifyFailureResponse ( guard, msg, ECA_NOCONVERT ); + } + + int cacStatus = caNetConvert ( + msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + if ( cacStatus != ECA_NORMAL ) { + return this->sendErrWithEpicsStatus ( + guard, & msg, pChan->getCID(), S_cas_internal, cacStatus ); + } + + if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; + this->out.commitMsg ( reducedPayloadSize ); + } + else { + this->out.commitMsg (); + } + + return S_cas_success; +} + +// +// casStrmClient::readNotifyFailureResponse () +// +caStatus casStrmClient::readNotifyFailureResponse ( + epicsGuard < casClientMutex > &, const caHdrLargeArray & msg, const caStatus ECA_XXXX ) +{ + assert ( ECA_XXXX != ECA_NORMAL ); + void *pPayload; + unsigned size = dbr_size_n ( msg.m_dataType, msg.m_count ); + caStatus status = this->out.copyInHeader ( msg.m_cmmd, size, + msg.m_dataType, msg.m_count, ECA_XXXX, + msg.m_available, & pPayload ); + if ( ! status ) { + memset ( pPayload, '\0', size ); + this->out.commitMsg (); + } + return status; +} + +// +// set bounds on an application type within a container, but dont +// preallocate space (not preallocating buffer space allows gdd::put +// to be more efficent if it discovers that the source has less data +// than the destination) +// +caStatus convertContainerMemberToAtomic ( gdd & dd, + aitUint32 appType, aitUint32 elemCount ) +{ + gdd * pVal; + if ( dd.isContainer() ) { + // All DBR types have a value member + aitUint32 index; + int gdds = gddApplicationTypeTable::app_table.mapAppToIndex + ( dd.applicationType(), appType, index ); + if ( gdds ) { + return S_cas_badType; + } + + pVal = dd.getDD ( index ); + if ( ! pVal ) { + return S_cas_badType; + } + } + else { + pVal = & dd; + } + + // we cant changed a managed type that is + // already atomic (array) + if ( ! pVal->isScalar () ) { + return S_cas_badType; + } + + if ( elemCount <= 1 ) { + return S_cas_success; + } + + // convert to atomic + gddBounds bds; + bds.setSize ( elemCount ); + bds.setFirst ( 0u ); + pVal->setDimension ( 1u, & bds ); + return S_cas_success; +} + +// +// createDBRDD () +// +static caStatus createDBRDD ( unsigned dbrType, + unsigned elemCount, gdd * & pDD ) +{ + /* + * DBR type has already been checked, but it is possible + * that "gddDbrToAit" will not track with changes in + * the DBR_XXXX type system + */ + if ( dbrType >= NELEMENTS ( gddDbrToAit ) ) { + return S_cas_badType; + } + + if ( gddDbrToAit[dbrType].type == aitEnumInvalid ) { + return S_cas_badType; + } + + aitUint16 appType = gddDbrToAit[dbrType].app; + + // + // create the descriptor + // + gdd * pDescRet = + gddApplicationTypeTable::app_table.getDD ( appType ); + if ( ! pDescRet ) { + return S_cas_noMemory; + } + + // fix the value element count + caStatus status = convertContainerMemberToAtomic ( + *pDescRet, gddAppType_value, elemCount ); + if ( status != S_cas_success ) { + pDescRet->unreference (); + return status; + } + + // fix the enum string table element count + // (this is done here because the application type table in gdd + // does not appear to handle this correctly) + if ( dbrType == DBR_CTRL_ENUM || dbrType == DBR_GR_ENUM ) { + status = convertContainerMemberToAtomic ( + *pDescRet, gddAppType_enums, MAX_ENUM_STATES ); + if ( status != S_cas_success ) { + pDescRet->unreference (); + return status; + } + } + + pDD = pDescRet; + return S_cas_success; +} + +// +// casStrmClient::monitorFailureResponse () +// +caStatus casStrmClient::monitorFailureResponse ( + epicsGuard < casClientMutex > &, const caHdrLargeArray & msg, + const caStatus ECA_XXXX ) +{ + assert ( ECA_XXXX != ECA_NORMAL ); + void *pPayload; + unsigned size = dbr_size_n ( msg.m_dataType, msg.m_count ); + caStatus status = this->out.copyInHeader ( msg.m_cmmd, size, + msg.m_dataType, msg.m_count, ECA_XXXX, + msg.m_available, & pPayload ); + if ( ! status ) { + memset ( pPayload, '\0', size ); + this->out.commitMsg (); + } + return status; +} + +// +// casStrmClient::monitorResponse () +// +caStatus casStrmClient::monitorResponse ( + epicsGuard < casClientMutex > & guard, + casChannelI & chan, const caHdrLargeArray & msg, + const gdd & desc, const caStatus completionStatus ) +{ + void * pPayload = 0; + { + ca_uint32_t size = dbr_size_n ( msg.m_dataType, msg.m_count ); + caStatus status = out.copyInHeader ( msg.m_cmmd, size, + msg.m_dataType, msg.m_count, ECA_NORMAL, + msg.m_available, & pPayload ); + if ( status ) { + if ( status == S_cas_hugeRequest ) { + status = sendErr ( guard, & msg, chan.getCID(), ECA_TOLARGE, + "unable to fit read subscription update response " + "into server's buffer" ); + } + return status; + } + } + + if ( ! chan.readAccess () ) { + return monitorFailureResponse ( guard, msg, ECA_NORDACCESS ); + } + + gdd * pDBRDD = 0; + if ( completionStatus == S_cas_success ) { + caStatus status = createDBRDD ( msg.m_dataType, msg.m_count, pDBRDD ); + if ( status != S_cas_success ) { + caStatus ecaStatus; + if ( status == S_cas_badType ) { + ecaStatus = ECA_BADTYPE; + } + else if ( status == S_cas_noMemory ) { + ecaStatus = ECA_ALLOCMEM; + } + else { + ecaStatus = ECA_GETFAIL; + } + return monitorFailureResponse ( guard, msg, ecaStatus ); + } + else { + gddStatus gdds = gddApplicationTypeTable:: + app_table.smartCopy ( pDBRDD, & desc ); + if ( gdds < 0 ) { + pDBRDD->unreference (); + errPrintf ( S_cas_noConvert, __FILE__, __LINE__, + "no conversion between event app type=%d and DBR type=%d Element count=%d", + desc.applicationType (), msg.m_dataType, msg.m_count); + return monitorFailureResponse ( guard, msg, ECA_NOCONVERT ); + } + } + } + else { + if ( completionStatus == S_cas_noRead ) { + return monitorFailureResponse ( guard, msg, ECA_NORDACCESS ); + } + else if ( completionStatus == S_cas_noMemory || + completionStatus == S_casApp_noMemory ) { + return monitorFailureResponse ( guard, msg, ECA_ALLOCMEM ); + } + else if ( completionStatus == S_cas_badType ) { + return monitorFailureResponse ( guard, msg, ECA_BADTYPE ); + } + else { + errMessage ( completionStatus, "- in monitor response" ); + return monitorFailureResponse ( guard, msg, ECA_GETFAIL ); + } + } + + int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr ( + pPayload, msg.m_count, *pDBRDD, chan.enumStringTable() ); + if ( mapDBRStatus < 0 ) { + pDBRDD->unreference (); + return monitorFailureResponse ( guard, msg, ECA_NOCONVERT ); + } + + int cacStatus = caNetConvert ( + msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + if ( cacStatus != ECA_NORMAL ) { + return this->sendErrWithEpicsStatus ( + guard, & msg, chan.getCID(), S_cas_internal, cacStatus ); + } + + // + // force string message size to be the true size + // + if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + ca_uint32_t reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; + this->out.commitMsg ( reducedPayloadSize ); + } + else { + this->out.commitMsg (); + } + + pDBRDD->unreference (); + + return S_cas_success; +} + +/* + * casStrmClient::writeActionSendFailureStatus() + */ +caStatus casStrmClient :: + writeActionSendFailureStatus ( epicsGuard < casClientMutex > & guard, + const caHdrLargeArray & msg, ca_uint32_t cid, caStatus status ) +{ + caStatus ecaStatus; + if ( status == S_cas_noMemory ) { + ecaStatus = ECA_ALLOCMEM; + } + else if ( status == S_cas_noConvert ) { + ecaStatus = ECA_NOCONVERT; + } + else if ( status == S_cas_badType ) { + ecaStatus = ECA_BADTYPE; + } + else { + ecaStatus = ECA_PUTFAIL; + } + status = this->sendErrWithEpicsStatus ( guard, & msg, cid, + status, ecaStatus ); + return status; +} + + +/* + * casStrmClient::writeAction() + */ +caStatus casStrmClient::writeAction ( epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray *mp = this->ctx.getMsg(); + casChannelI *pChan; + + { + caStatus status = this->verifyRequest ( pChan ); + if (status != ECA_NORMAL) { + if ( pChan ) { + return this->sendErr ( guard, mp, pChan->getCID(), + status, "get request" ); + } + else { + return this->sendErr ( guard, mp, invalidResID, + status, "get request" ); + } + } + } + + // dont allow a request that completed with the service in the + // past, but was incomplete because no response was sent be + // executed twice with the service + if ( this->responseIsPending ) { + caStatus status = this->writeActionSendFailureStatus ( + guard, *mp, pChan->getCID(), + this->pendingResponseStatus ); + return status; + } + + // + // verify write access + // + if ( ! pChan->writeAccess() ) { + caStatus status; + int v41 = CA_V41 ( this->minor_version_number ); + if (v41) { + status = ECA_NOWTACCESS; + } + else{ + status = ECA_PUTFAIL; + } + return this->sendErr ( guard, mp, pChan->getCID(), + status, "write access denied"); + } + + // + // initiate the write operation + // + { + caStatus servStat = this->write ( & casChannelI :: write ); + if ( servStat == S_casApp_success || + servStat == S_casApp_asyncCompletion ) { + return S_cas_success; + } + else if ( servStat == S_casApp_postponeAsyncIO ) { + return S_casApp_postponeAsyncIO; + } + else { + caStatus status = + this->writeActionSendFailureStatus ( guard, *mp, + pChan->getCID(), servStat ); + if ( status != S_cas_success ) { + this->pendingResponseStatus = servStat; + this->responseIsPending = true; + } + return status; + } + } + + // + // The gdd created above is deleted by the server tool + // +} + +// +// casStrmClient::writeResponse() +// +caStatus casStrmClient::writeResponse ( + epicsGuard < casClientMutex > & guard, casChannelI & chan, + const caHdrLargeArray & msg, const caStatus completionStatus ) +{ + caStatus status; + + if ( completionStatus ) { + errMessage ( completionStatus, "write failed" ); + status = this->sendErrWithEpicsStatus ( guard, & msg, + chan.getCID(), completionStatus, ECA_PUTFAIL ); + } + else { + status = S_cas_success; + } + + return status; +} + +/* + * casStrmClient::writeNotifyAction() + */ +caStatus casStrmClient::writeNotifyAction ( + epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray *mp = this->ctx.getMsg (); + + casChannelI *pChan; + { + caStatus status = this->verifyRequest ( pChan ); + if ( status != ECA_NORMAL ) { + return casStrmClient::writeNotifyResponseECA_XXX ( guard, *mp, status ); + } + } + + // dont allow a request that completed with the service in the + // past, but was incomplete because no response was sent be + // executed twice with the service + if ( this->responseIsPending ) { + caStatus status = this->writeNotifyResponse ( guard, *pChan, + *mp, this->pendingResponseStatus ); + return status; + } + + // + // verify write access + // + if ( ! pChan->writeAccess() ) { + if ( CA_V41(this->minor_version_number) ) { + return this->casStrmClient::writeNotifyResponseECA_XXX ( + guard, *mp, ECA_NOWTACCESS); + } + else { + return this->casStrmClient::writeNotifyResponse ( + guard, *pChan, *mp, S_cas_noWrite ); + } + } + + // + // initiate the write operation + // + { + caStatus servStat = this->write ( & casChannelI :: writeNotify ); + if ( servStat == S_casApp_asyncCompletion ) { + return S_cas_success; + } + else if ( servStat == S_casApp_postponeAsyncIO ) { + return S_casApp_postponeAsyncIO; + } + else { + caStatus status = this->writeNotifyResponse ( guard, *pChan, + *mp, servStat ); + if ( status != S_cas_success ) { + this->pendingResponseStatus = servStat; + this->responseIsPending = true; + } + return status; + } + } +} + +/* + * casStrmClient::writeNotifyResponse() + */ +caStatus casStrmClient::writeNotifyResponse ( epicsGuard < casClientMutex > & guard, + casChannelI & chan, const caHdrLargeArray & msg, const caStatus completionStatus ) +{ + caStatus ecaStatus; + + if ( completionStatus == S_cas_success ) { + ecaStatus = ECA_NORMAL; + } + else { + ecaStatus = ECA_PUTFAIL; + } + + ecaStatus = this->casStrmClient::writeNotifyResponseECA_XXX ( + guard, msg, ecaStatus ); + if (ecaStatus) { + return ecaStatus; + } + + // + // send independent warning exception to the client so that they + // will see the error string associated with this error code + // since the error string cant be sent with the put call back + // response (hopefully this is useful information) + // + // order is very important here because it determines that the put + // call back response is always sent, and that this warning exception + // message will be sent at most one time. In rare instances it will + // not be sent, but at least it will not be sent multiple times. + // The message is logged to the console in the rare situations when + // we are unable to send. + // + if ( completionStatus != S_cas_success ) { + ecaStatus = this->sendErrWithEpicsStatus ( guard, & msg, chan.getCID(), + completionStatus, ECA_NOCONVERT ); + if ( ecaStatus ) { + errMessage ( completionStatus, + "<= put callback failure detail not passed to client" ); + } + } + return S_cas_success; +} + +/* + * casStrmClient::writeNotifyResponseECA_XXX() + */ +caStatus casStrmClient::writeNotifyResponseECA_XXX ( + epicsGuard < casClientMutex > &, + const caHdrLargeArray & msg, const caStatus ecaStatus ) +{ + caStatus status = out.copyInHeader ( msg.m_cmmd, 0, + msg.m_dataType, msg.m_count, ecaStatus, + msg.m_available, 0 ); + if ( ! status ) { + this->out.commitMsg (); + } + + return status; +} + +// +// casStrmClient :: asyncSearchResp() +// +caStatus casStrmClient :: asyncSearchResponse ( + epicsGuard < casClientMutex > & guard, const caNetAddr & /* outAddr */, + const caHdrLargeArray & msg, const pvExistReturn & retVal, + ca_uint16_t /* protocolRevision */, ca_uint32_t /* sequenceNumber */ ) +{ + return this->searchResponse ( guard, msg, retVal ); +} + +// casStrmClient :: hostName() +void casStrmClient :: hostName ( char * pInBuf, unsigned bufSizeIn ) const +{ + _clientAddr.stringConvert ( pInBuf, bufSizeIn ); +} + +// +// caStatus casStrmClient :: searchResponse() +// +caStatus casStrmClient :: searchResponse ( + epicsGuard < casClientMutex > & guard, + const caHdrLargeArray & msg, + const pvExistReturn & retVal ) +{ + if ( retVal.getStatus() != pverExistsHere ) { + return S_cas_success; + } + + // + // starting with V4.1 the count field is used (abused) + // by the client to store the minor version number of + // the client. + // + // Old versions expect alloc of channel in response + // to a search request. This is no longer supported. + // + if ( !CA_V44( msg.m_count ) ) { + errlogPrintf ( + "client \"%s\" using EPICS R3.11 CA " + "connect protocol was ignored\n", + pHostName ); + // + // old connect protocol was dropped when the + // new API was added to the server (they must + // now use clients at EPICS 3.12 or higher) + // + caStatus status = this->sendErr ( + guard, & msg, invalidResID, ECA_DEFUNCT, + "R3.11 connect sequence from old client was ignored" ); + return status; + } + + // + // cid field is abused to carry the IP + // address in CA_V48 or higher + // (this allows a CA servers to serve + // as a directory service) + // + // data type field is abused to carry the IP + // port number here CA_V44 or higher + // (this allows multiple CA servers on one + // host) + // + ca_uint32_t serverAddr; + ca_uint16_t serverPort; + if ( CA_V48( msg.m_count ) ) { + struct sockaddr_in ina; + if ( retVal.addrIsValid() ) { + caNetAddr addr = retVal.getAddr(); + ina = addr.getSockIP(); + // + // If they dont specify a port number then the default + // CA server port is assumed when it it is a server + // address redirect (it is never correct to use this + // server's port when it is a redirect). + // + if ( ina.sin_port == 0u ) { + ina.sin_port = htons ( CA_SERVER_PORT ); + } + } + else { + // + // We dont fill in the servers address here because + // the client has a tcp circuit to us and he knows + // our address + // + ina.sin_addr.s_addr = htonl ( ~0U ); + ina.sin_port = htons ( 0 ); + } + serverAddr = ntohl ( ina.sin_addr.s_addr ); + serverPort = ntohs ( ina.sin_port ); + } + else { + serverAddr = ntohl ( ~0U ); + serverPort = ntohs ( 0 ); + } + + caStatus status = this->out.copyInHeader ( CA_PROTO_SEARCH, + 0, serverPort, 0, serverAddr, msg.m_available, 0 ); + // + // Starting with CA V4.1 the minor version number + // is appended to the end of each search reply. + // This value is ignored by earlier clients. + // + if ( status == S_cas_success ) { + this->out.commitMsg (); + } + + return status; +} + +// +// casStrmClient :: searchAction() +// +caStatus casStrmClient :: searchAction ( epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray *mp = this->ctx.getMsg(); + const char *pChanName = static_cast ( this->ctx.getData() ); + caStatus status; + + // + // check the sanity of the message + // + if ( mp->m_postsize <= 1 ) { + caServerI::dumpMsg ( this->pHostName, "?", mp, this->ctx.getData(), + "empty PV name extension in TCP search request?\n" ); + return S_cas_success; + } + + if ( pChanName[0] == '\0' ) { + caServerI::dumpMsg ( this->pHostName, "?", mp, this->ctx.getData(), + "zero length PV name in UDP search request?\n" ); + return S_cas_success; + } + + // check for an unterminated string before calling server tool + // by searching backwards through the string (some early versions + // of the client library might not be setting the pad bytes to nill) + for ( unsigned i = mp->m_postsize-1; pChanName[i] != '\0'; i-- ) { + if ( i <= 1 ) { + caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), + "unterminated PV name in UDP search request?\n" ); + return S_cas_success; + } + } + + if ( this->getCAS().getDebugLevel() > 6u ) { + this->hostName ( this->pHostName, sizeof ( pHostName ) ); + printf ( "\"%s\" is searching for \"%s\"\n", + pHostName, pChanName ); + } + + // + // verify that we have sufficent memory for a PV and a + // monitor prior to calling PV exist test so that when + // the server runs out of memory we dont reply to + // search requests, and therefore dont thrash through + // caServer::pvExistTest() and casCreatePV::pvAttach() + // + if ( ! osiSufficentSpaceInPool ( 0 ) ) { + return S_cas_success; + } + + // + // ask the server tool if this PV exists + // + this->userStartedAsyncIO = false; + pvExistReturn pver = + this->getCAS()->pvExistTest ( + this->ctx, _clientAddr, pChanName ); + + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + if ( this->userStartedAsyncIO ) { + if ( pver.getStatus() != pverAsyncCompletion ) { + errMessage ( S_cas_badParameter, + "- assuming asynch IO status from caServer::pvExistTest()"); + } + status = S_cas_success; + } + else { + // + // otherwise we assume sync IO operation was initiated + // + switch ( pver.getStatus() ) { + case pverExistsHere: + status = this->searchResponse ( guard, *mp, pver ); + break; + + case pverDoesNotExistHere: + status = S_cas_success; + break; + + case pverAsyncCompletion: + errMessage ( S_cas_badParameter, + "- unexpected asynch IO status from " + "caServer::pvExistTest() ignored"); + status = S_cas_success; + break; + + default: + errMessage ( S_cas_badParameter, + "- invalid return from " + "caServer::pvExistTest() ignored"); + status = S_cas_success; + break; + } + } + return status; +} + +/* + * casStrmClient::hostNameAction() + */ +caStatus casStrmClient::hostNameAction ( epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray *mp = this->ctx.getMsg(); + char *pName = (char *) this->ctx.getData(); + unsigned size; + char *pMalloc; + caStatus status; + + // currently this has to occur prior to + // creating channels or its not allowed + if ( this->chanList.count () ) { + return this->sendErr ( guard, mp, invalidResID, + ECA_UNAVAILINSERV, pName ); + } + + size = strlen(pName)+1u; + /* + * user name will not change if there isnt enough memory + */ + pMalloc = new char [size]; + if ( ! pMalloc ){ + status = this->sendErr ( guard, mp, invalidResID, + ECA_ALLOCMEM, pName ); + if (status) { + return status; + } + return S_cas_internal; + } + strncpy ( pMalloc, pName, size - 1 ); + pMalloc[ size - 1 ]='\0'; + + if ( this->pHostName ) { + delete [] this->pHostName; + } + this->pHostName = pMalloc; + + return S_cas_success; +} + +/* + * casStrmClient::clientNameAction() + */ +caStatus casStrmClient::clientNameAction ( + epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray *mp = this->ctx.getMsg(); + char *pName = (char *) this->ctx.getData(); + unsigned size; + char *pMalloc; + caStatus status; + + // currently this has to occur prior to + // creating channels or its not allowed + if ( this->chanList.count () ) { + return this->sendErr ( guard, mp, invalidResID, + ECA_UNAVAILINSERV, pName ); + } + + size = strlen(pName)+1; + + /* + * user name will not change if there isnt enough memory + */ + pMalloc = new char [size]; + if(!pMalloc){ + status = this->sendErr ( guard, mp, invalidResID, + ECA_ALLOCMEM, pName ); + if (status) { + return status; + } + return S_cas_internal; + } + strncpy ( pMalloc, pName, size - 1 ); + pMalloc[size-1]='\0'; + + if ( this->pUserName ) { + delete [] this->pUserName; + } + this->pUserName = pMalloc; + + return S_cas_success; +} + +/* + * casStrmClientMon::claimChannelAction() + */ +caStatus casStrmClient::claimChannelAction ( + epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray * mp = this->ctx.getMsg(); + char *pName = (char *) this->ctx.getData(); + caServerI & cas = *this->ctx.getServer(); + + /* + * The available field is used (abused) + * here to communicate the miner version number + * starting with CA 4.1. The field was set to zero + * prior to 4.1 + */ + if ( mp->m_available < 0xffff ) { + this->minor_version_number = + static_cast < ca_uint16_t > ( mp->m_available ); + } + else { + this->minor_version_number = 0; + } + + // + // We shouldnt be receiving a connect message from + // an R3.11 client because we will not respond to their + // search requests (if so we disconnect) + // + if ( ! CA_V44 ( this->minor_version_number ) ) { + // + // old connect protocol was dropped when the + // new API was added to the server (they must + // now use clients at EPICS 3.12 or higher) + // + caStatus status = this->sendErr ( guard, mp, mp->m_cid, ECA_DEFUNCT, + "R3.11 connect sequence from old client was ignored"); + if ( status ) { + return status; + } + return S_cas_badProtocol; // disconnect client + } + + if ( mp->m_postsize <= 1u ) { + return S_cas_badProtocol; // disconnect client + } + + pName[mp->m_postsize-1u] = '\0'; + + if ( ( mp->m_postsize - 1u ) > unreasonablePVNameSize ) { + return S_cas_badProtocol; // disconnect client + } + + this->userStartedAsyncIO = false; + + // + // attach to the PV + // + pvAttachReturn pvar = cas->pvAttach ( this->ctx, pName ); + + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + if ( this->userStartedAsyncIO ) { + if ( pvar.getStatus() != S_casApp_asyncCompletion ) { + fprintf ( stderr, + "Application returned %d from cas::pvAttach()" + " - expected S_casApp_asyncCompletion\n", + pvar.getStatus() ); + } + return S_cas_success; + } + else if ( pvar.getStatus() == S_casApp_asyncCompletion ) { + errMessage ( S_cas_badParameter, + "- expected asynch IO creation " + "from caServer::pvAttach()" ); + return this->createChanResponse ( guard, + this->ctx, S_cas_badParameter ); + } + else if ( pvar.getStatus() == S_casApp_postponeAsyncIO ) { + caServerI & casi ( * this->ctx.getServer() ); + if ( casi.ioIsPending () ) { + casi.addItemToIOBLockedList ( *this ); + return S_casApp_postponeAsyncIO; + } + else { + // Its not ok to postpone IO when there isnt at + // least one request pending. In that situation + // there is no event from the service telling us + // when its ok to start issuing requests again! + // So in that situation we tell the client that + // the service refused the request, and this + // caused the request to fail. + this->issuePosponeWhenNonePendingWarning ( "PV attach channel" ); + return this->createChanResponse ( guard, this->ctx, + S_cas_posponeWhenNonePending ); + } + } + else { + return this->createChanResponse ( guard, this->ctx, pvar ); + } +} + +// +// casStrmClient::issuePosponeWhenNonePendingWarning() +// +void casStrmClient :: + issuePosponeWhenNonePendingWarning ( const char * pReqTypeStr ) +{ + errlogPrintf ( "service attempted to postpone %s IO when " + "no IO was pending against the target\n", pReqTypeStr ); + errlogPrintf ( "server library will not receive a restart event, " + "and so failure response was sent to client\n" ); +} + +// +// casStrmClient::createChanResponse() +// +caStatus casStrmClient::createChanResponse ( + epicsGuard < casClientMutex > & guard, + casCtx & ctxIn, const pvAttachReturn & pvar ) +{ + const caHdrLargeArray & hdr = *ctxIn.getMsg(); + + if ( pvar.getStatus() != S_cas_success ) { + return this->channelCreateFailedResp ( guard, + hdr, pvar.getStatus() ); + } + + if ( ! pvar.getPV()->pPVI ) { + // @#$!* Tornado 2 Cygnus GNU compiler bugs +# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) + pvar.getPV()->pPVI = new ( std::nothrow ) // X aCC 930 + casPVI ( *pvar.getPV() ); +# else + try { + pvar.getPV()->pPVI = new + casPVI ( *pvar.getPV() ); + } + catch ( ... ) { + pvar.getPV()->pPVI = 0; + } +# endif + + if ( ! pvar.getPV()->pPVI ) { + pvar.getPV()->destroyRequest (); + return this->channelCreateFailedResp ( guard, hdr, S_casApp_pvNotFound ); + } + } + + unsigned nativeTypeDBR; + caStatus status = pvar.getPV()->pPVI->bestDBRType ( nativeTypeDBR ); + if ( status ) { + pvar.getPV()->pPVI->deleteSignal(); + errMessage ( status, "best external dbr type fetch failed" ); + return this->channelCreateFailedResp ( guard, hdr, status ); + } + + // + // attach the PV to this server + // + status = pvar.getPV()->pPVI->attachToServer ( this->getCAS() ); + if ( status ) { + pvar.getPV()->pPVI->deleteSignal(); + return this->channelCreateFailedResp ( guard, hdr, status ); + } + + // + // create server tool XXX derived from casChannel + // + casChannel * pChan = pvar.getPV()->pPVI->createChannel ( + ctxIn, this->pUserName, this->pHostName ); + if ( ! pChan ) { + pvar.getPV()->pPVI->deleteSignal(); + return this->channelCreateFailedResp ( + guard, hdr, S_cas_noMemory ); + } + + if ( ! pChan->pChanI ) { + // @#$!* Tornado 2 Cygnus GNU compiler bugs +# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) + pChan->pChanI = new ( std::nothrow ) // X aCC 930 + casChannelI ( * this, *pChan, + * pvar.getPV()->pPVI, hdr.m_cid ); +# else + try { + pChan->pChanI = new + casChannelI ( * this, *pChan, + * pvar.getPV()->pPVI, hdr.m_cid ); + } + catch ( ... ) { + pChan->pChanI = 0; + } +# endif + + if ( ! pChan->pChanI ) { + pChan->destroyRequest (); + pChan->getPV()->pPVI->deleteSignal (); + return this->channelCreateFailedResp ( + guard, hdr, S_cas_noMemory ); + } + } + + // + // Install the channel now so that the server will + // clean up properly if the client disconnects + // while an asynchronous IO fetching the enum + // string table is outstanding + // + this->chanTable.idAssignAdd ( *pChan->pChanI ); + this->chanList.add ( *pChan->pChanI ); + pChan->pChanI->installIntoPV (); + + assert ( hdr.m_cid == pChan->pChanI->getCID() ); + + // + // check to see if the enum table is empty and therefore + // an update is needed every time that a PV attaches + // to the server in case the client disconnected before + // an asynchronous IO to get the table completed + // + if ( nativeTypeDBR == DBR_ENUM ) { + ctxIn.setChannel ( pChan->pChanI ); + ctxIn.setPV ( pvar.getPV()->pPVI ); + this->userStartedAsyncIO = false; + status = pvar.getPV()->pPVI->updateEnumStringTable ( ctxIn ); + if ( this->userStartedAsyncIO ) { + if ( status != S_casApp_asyncCompletion ) { + fprintf ( stderr, + "Application returned %d from casChannel::read()" + " - expected S_casApp_asyncCompletion\n", status); + } + status = S_cas_success; + } + else if ( status == S_cas_success ) { + status = privateCreateChanResponse ( + guard, * pChan->pChanI, hdr, nativeTypeDBR ); + } + else { + if ( status == S_casApp_asyncCompletion ) { + errMessage ( status, + "- enum string tbl cache read returned asynch IO creation, but async IO not started?"); + } + else if ( status == S_casApp_postponeAsyncIO ) { + errMessage ( status, "- enum string tbl cache read ASYNC IO postponed ?"); + errlogPrintf ( "The server library does not currently support postponment of\n" ); + errlogPrintf ( "string table cache update of casChannel::read().\n" ); + errlogPrintf ( "To postpone this request please postpone the PC attach IO request.\n" ); + errlogPrintf ( "String table cache update did not occur.\n" ); + } + else { + errMessage ( status, "- enum string tbl cache read failed ?"); + } + status = privateCreateChanResponse ( + guard, * pChan->pChanI, hdr, nativeTypeDBR ); + } + } + else { + status = privateCreateChanResponse ( + guard, * pChan->pChanI, hdr, nativeTypeDBR ); + } + + if ( status != S_cas_success ) { + this->chanTable.remove ( *pChan->pChanI ); + this->chanList.remove ( *pChan->pChanI ); + pChan->pChanI->uninstallFromPV ( this->eventSys ); + delete pChan->pChanI; + } + + return status; +} + +// +// casStrmClient::enumPostponedCreateChanResponse() +// +// LOCK must be applied +// +caStatus casStrmClient::enumPostponedCreateChanResponse ( + epicsGuard < casClientMutex > & guard, casChannelI & chan, + const caHdrLargeArray & hdr ) +{ + caStatus status = this->privateCreateChanResponse ( + guard, chan, hdr, DBR_ENUM ); + if ( status != S_cas_success ) { + if ( status != S_cas_sendBlocked ) { + this->chanTable.remove ( chan ); + this->chanList.remove ( chan ); + chan.uninstallFromPV ( this->eventSys ); + delete & chan; + } + } + return status; +} + +// +// privateCreateChanResponse +// +caStatus casStrmClient::privateCreateChanResponse ( + epicsGuard < casClientMutex > & guard, + casChannelI & chan, const caHdrLargeArray & hdr, + unsigned nativeTypeDBR ) +{ + // + // We are allocating enough space for both the claim + // response and the access rights response so that we know for + // certain that they will both be sent together. + // + // Considering the possibility of large arrays we must allocate + // an additional 2 * sizeof(ca_uint32_t) + // + void *pRaw; + const outBufCtx outctx = this->out.pushCtx + ( 0, 2 * sizeof ( caHdr ) + 2 * sizeof(ca_uint32_t), pRaw ); + if ( outctx.pushResult() != outBufCtx::pushCtxSuccess ) { + return S_cas_sendBlocked; + } + + // + // We are certain that the request will complete + // here because we allocated enough space for this + // and the claim response above. + // + caStatus status = this->accessRightsResponse ( guard, & chan ); + if ( status ) { + this->out.popCtx ( outctx ); + errMessage ( status, "incomplete channel create?" ); + status = this->channelCreateFailedResp ( guard, hdr, status ); + if ( status != S_cas_sendBlocked ) { + this->chanTable.remove ( chan ); + this->chanList.remove ( chan ); + chan.uninstallFromPV ( this->eventSys ); + delete & chan; + } + return status; + } + + // + // We are allocated enough space for both the claim + // response and the access response so that we know for + // certain that they will both be sent together. + // Nevertheles, some (old) clients do not receive + // an access rights response so we allocate again + // here to be certain that we are at the correct place in + // the protocol buffer. + // + assert ( nativeTypeDBR <= 0xffff ); + aitIndex nativeCount = chan.getPVI().nativeCount(); + assert ( nativeCount <= 0xffffffff ); + assert ( hdr.m_cid == chan.getCID() ); + status = this->out.copyInHeader ( CA_PROTO_CREATE_CHAN, 0, + static_cast ( nativeTypeDBR ), + static_cast ( nativeCount ), // X aCC 392 + chan.getCID(), chan.getSID(), 0 ); + if ( status != S_cas_success ) { + this->out.popCtx ( outctx ); + errMessage ( status, "incomplete channel create?" ); + status = this->channelCreateFailedResp ( guard, hdr, status ); + if ( status != S_cas_sendBlocked ) { + this->chanTable.remove ( chan ); + this->chanList.remove ( chan ); + chan.uninstallFromPV ( this->eventSys ); + delete & chan; + } + return status; + } + + this->out.commitMsg (); + + // + // commit the message + // + bufSizeT nBytes = this->out.popCtx ( outctx ); + assert ( + nBytes == 2 * sizeof ( caHdr ) || + nBytes == 2 * sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ) ); + this->out.commitRawMsg ( nBytes ); + + return status; +} + +/* + * casStrmClient::channelCreateFailed() + */ +caStatus casStrmClient::channelCreateFailedResp ( + epicsGuard < casClientMutex > & guard, const caHdrLargeArray & hdr, + const caStatus createStatus ) +{ + if ( createStatus == S_casApp_asyncCompletion ) { + errMessage( S_cas_badParameter, + "- no asynchronous IO create in pvAttach() ?"); + errMessage( S_cas_badParameter, + "- or S_casApp_asyncCompletion was " + "async IO competion code ?"); + } + else if ( createStatus != S_casApp_pvNotFound ) { + errMessage ( createStatus, + "- Server unable to create a new PV" ); + } + caStatus status; + if ( CA_V46 ( this->minor_version_number ) ) { + status = this->out.copyInHeader ( + CA_PROTO_CREATE_CH_FAIL, 0, + 0, 0, hdr.m_cid, 0, 0 ); + if ( status == S_cas_success ) { + this->out.commitMsg (); + } + } + else { + status = this->sendErrWithEpicsStatus ( + guard, & hdr, hdr.m_cid, createStatus, ECA_ALLOCMEM ); + } + return status; +} + +// +// casStrmClient::eventsOnAction() +// +caStatus casStrmClient::eventsOnAction ( epicsGuard < casClientMutex > & ) +{ + this->enableEvents (); + return S_cas_success; +} + +// +// casStrmClient::eventsOffAction() +// +caStatus casStrmClient::eventsOffAction ( epicsGuard < casClientMutex > & ) +{ + this->disableEvents (); + return S_cas_success; +} + +// +// eventAddAction() +// +caStatus casStrmClient::eventAddAction ( + epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray *mp = this->ctx.getMsg(); + struct mon_info *pMonInfo = (struct mon_info *) + this->ctx.getData(); + + casChannelI *pciu; + { + caStatus status = casStrmClient::verifyRequest ( pciu ); + if ( status != ECA_NORMAL ) { + if ( pciu ) { + return this->sendErr ( guard, mp, + pciu->getCID(), status, NULL); + } + else { + return this->sendErr ( guard, mp, + invalidResID, status, NULL ); + } + } + } + + // dont allow a request that completed with the service in the + // past, but was incomplete because no response was sent to be + // executed twice with the service + if ( this->responseIsPending ) { + // dont read twice if we didnt finish in the past + // because we were send blocked + if ( this->pendingResponseStatus == S_cas_success ) { + assert ( pValueRead.valid () ); + return this->monitorResponse ( guard, *pciu, + *mp, *pValueRead, S_cas_success ); + } + else { + return this->monitorFailureResponse ( + guard, *mp, ECA_GETFAIL ); + } + } + + // + // place monitor mask in correct byte order + // + casEventMask mask; + ca_uint16_t caProtoMask = AlignedWireRef < epicsUInt16 > ( pMonInfo->m_mask ); + if (caProtoMask&DBE_VALUE) { + mask |= this->getCAS().valueEventMask(); + } + + if (caProtoMask&DBE_LOG) { + mask |= this->getCAS().logEventMask(); + } + + if (caProtoMask&DBE_ALARM) { + mask |= this->getCAS().alarmEventMask(); + } + + if (mask.noEventsSelected()) { + char errStr[40]; + sprintf ( errStr, "event add req with mask=0X%X\n", caProtoMask ); + return this->sendErr ( guard, mp, pciu->getCID(), + ECA_BADMASK, errStr ); + } + + casMonitor & mon = this->monitorFactory ( + *pciu, mp->m_available, mp->m_count, + mp->m_dataType, mask ); + pciu->installMonitor ( mon ); + + + // + // Attempt to read the first monitored value prior to creating + // the monitor object so that if the server tool chooses + // to postpone asynchronous IO we can safely restart this + // request later. + // + { + caStatus servStat = this->read (); + // + // always send immediate monitor response at event add + // + if ( servStat == S_cas_success ) { + assert ( pValueRead.valid () ); + caStatus status = this->monitorResponse ( guard, *pciu, + *mp, *pValueRead, servStat ); + this->responseIsPending = ( status != S_cas_success ); + return status; + } + else if ( servStat == S_casApp_asyncCompletion ) { + return S_cas_success; + } + else if ( servStat == S_casApp_postponeAsyncIO ) { + return S_casApp_postponeAsyncIO; + } + else { + caStatus status = this->monitorFailureResponse ( + guard, *mp, ECA_GETFAIL ); + if ( status != S_cas_success ) { + pendingResponseStatus = servStat; + this->responseIsPending = true; + } + return status; + } + } +} + + +// +// casStrmClient::clearChannelAction() +// +caStatus casStrmClient::clearChannelAction ( + epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray * mp = this->ctx.getMsg (); + const void * dp = this->ctx.getData (); + int status; + + // send delete confirmed message + status = this->out.copyInHeader ( mp->m_cmmd, 0, + mp->m_dataType, mp->m_count, + mp->m_cid, mp->m_available, 0 ); + if ( status ) { + return status; + } + this->out.commitMsg (); + + // Verify the channel + chronIntId tmpId ( mp->m_cid ); + casChannelI * pciu = this->chanTable.remove ( tmpId ); + if ( pciu ) { + this->chanList.remove ( *pciu ); + pciu->uninstallFromPV ( this->eventSys ); + delete pciu; + } + else { + /* + * it is possible that the channel delete arrives just + * after the server tool has deleted the PV so we will + * not disconnect the client in this case. Nevertheless, + * we send a warning message in case either the client + * or server has become corrupted + */ + logBadId ( guard, mp, dp, ECA_BADCHID, mp->m_cid ); + } + + return status; +} + +// +// If the channel pointer is nill this indicates that +// the existence of the channel isnt certain because +// it is still installed and the client or the server +// tool might have destroyed it. Therefore, we must +// locate it using the supplied server id. +// +// If the channel pointer isnt nill this indicates +// that the channel has already been uninstalled. +// +// In both situations we need to send a channel +// disconnect message to the client and destroy the +// channel. +// +caStatus casStrmClient::channelDestroyEventNotify ( + epicsGuard < casClientMutex > &, + casChannelI * const pChan, ca_uint32_t sid ) +{ + casChannelI * pChanFound; + if ( pChan ) { + pChanFound = pChan; + } + else { + chronIntId tmpId ( sid ); + pChanFound = + this->chanTable.lookup ( tmpId ); + if ( ! pChanFound ) { + return S_cas_success; + } + } + + if ( CA_V47 ( this->minor_version_number ) ) { + caStatus status = this->out.copyInHeader ( + CA_PROTO_SERVER_DISCONN, 0, + 0, 0, pChanFound->getCID(), 0, 0 ); + if ( status == S_cas_sendBlocked ) { + return status; + } + this->out.commitMsg (); + } + else { + this->forceDisconnect (); + } + + if ( ! pChan ) { + this->chanTable.remove ( * pChanFound ); + this->chanList.remove ( * pChanFound ); + pChanFound->uninstallFromPV ( this->eventSys ); + } + + delete pChanFound; + + return S_cas_success; +} + +// casStrmClient::casChannelDestroyFromInterfaceNotify() +// immediateUninstallNeeded is false when we must avoid +// taking the lock in situations where we would compromise +// the lock hierarchy +void casStrmClient::casChannelDestroyFromInterfaceNotify ( + casChannelI & chan, bool immediateUninstallNeeded ) +{ + if ( immediateUninstallNeeded ) { + epicsGuard < casClientMutex > guard ( this->mutex ); + + this->chanTable.remove ( chan ); + this->chanList.remove ( chan ); + chan.uninstallFromPV ( this->eventSys ); + } + + class channelDestroyEvent * pEvent = + new ( std::nothrow ) class channelDestroyEvent ( // X aCC 930 + immediateUninstallNeeded ? & chan : 0, + chan.getSID() ); + if ( pEvent ) { + this->addToEventQueue ( *pEvent ); + } + else { + this->forceDisconnect (); + if ( immediateUninstallNeeded ) { + delete & chan; + } + } +} + +// casStrmClient::eventCancelAction() +caStatus casStrmClient::eventCancelAction ( + epicsGuard < casClientMutex > & guard ) +{ + const caHdrLargeArray * mp = this->ctx.getMsg (); + const void * dp = this->ctx.getData (); + + { + chronIntId tmpId ( mp->m_cid ); + casChannelI * pChan = this->chanTable.lookup ( tmpId ); + if ( ! pChan ) { + // It is possible that the event delete arrives just + // after the server tool has deleted the PV. Its probably + // best to just diconnect for now since some old clients + // may still exist. + logBadId ( guard, mp, dp, ECA_BADCHID, mp->m_cid ); + return S_cas_badResourceId; + } + + caStatus status = this->out.copyInHeader ( + CA_PROTO_EVENT_ADD, 0, + mp->m_dataType, mp->m_count, + mp->m_cid, mp->m_available, 0 ); + if ( status != S_cas_success ) { + return status; + } + this->out.commitMsg (); + + casMonitor * pMon = pChan->removeMonitor ( mp->m_available ); + if ( pMon ) { + this->eventSys.prepareMonitorForDestroy ( *pMon ); + } + else { + // this indicates client or server library + // corruption so a disconnect is probably + // the best option + logBadId ( guard, mp, dp, ECA_BADMONID, mp->m_available ); + return S_cas_badResourceId; + } + } + + return S_cas_success; +} + +#if 0 +/* + * casStrmClient::noReadAccessEvent() + * + * substantial complication introduced here by the need for backwards + * compatibility + */ +caStatus casStrmClient::noReadAccessEvent ( + epicsGuard < casClientMutex > & guard, casClientMon * pMon ) +{ + caHdr falseReply; + unsigned size; + caHdr * reply; + int status; + + size = dbr_size_n ( pMon->getType(), pMon->getCount() ); + + falseReply.m_cmmd = CA_PROTO_EVENT_ADD; + falseReply.m_postsize = size; + falseReply.m_dataType = pMon->getType(); + falseReply.m_count = pMon->getCount(); + falseReply.m_cid = pMon->getChannel().getCID(); + falseReply.m_available = pMon->getClientId(); + + status = this->allocMsg ( size, &reply ); + if ( status ) { + if( status == S_cas_hugeRequest ) { + return this->sendErr ( &falseReply, ECA_TOLARGE, NULL ); + } + return status; + } + else{ + /* + * New clients recv the status of the + * operation directly to the + * event/put/get callback. + * + * Fetched value is zerod in case they + * use it even when the status indicates + * failure. + * + * The m_cid field in the protocol + * header is abused to carry the status + */ + *reply = falseReply; + reply->m_postsize = size; + reply->m_cid = ECA_NORDACCESS; + memset((char *)(reply+1), 0, size); + this->commitMsg (); + } + + return S_cas_success; +} +#endif + +// +// casStrmClient::readSyncAction() +// +// This message indicates that the R3.13 or before client +// timed out on a read so we must clear out any pending +// asynchronous IO associated with a read. +// +caStatus casStrmClient::readSyncAction ( epicsGuard < casClientMutex > & ) +{ + tsDLIter < casChannelI > iter = + this->chanList.firstIter (); + while ( iter.valid() ) { + iter->clearOutstandingReads (); + iter++; + } + + const caHdrLargeArray * mp = this->ctx.getMsg (); + + int status = this->out.copyInHeader ( mp->m_cmmd, 0, + mp->m_dataType, mp->m_count, + mp->m_cid, mp->m_available, 0 ); + if ( ! status ) { + this->out.commitMsg (); + } + + return status; +} + + // + // casStrmClient::accessRightsResponse() + // + // NOTE: + // Do not change the size of this response without making + // parallel changes in createChanResp + // +caStatus casStrmClient::accessRightsResponse ( casChannelI * pciu ) +{ + epicsGuard < casClientMutex > guard ( this->mutex ); + return this->accessRightsResponse ( guard, pciu ); +} + +caStatus casStrmClient::accessRightsResponse ( + epicsGuard < casClientMutex > &, casChannelI * pciu ) +{ + unsigned ar; + int v41; + int status; + + // noop if this is an old client + v41 = CA_V41 ( this->minor_version_number ); + if ( ! v41 ) { + return S_cas_success; + } + + ar = 0; // none + if ( pciu->readAccess() ) { + ar |= CA_PROTO_ACCESS_RIGHT_READ; + } + if ( pciu->writeAccess() ) { + ar |= CA_PROTO_ACCESS_RIGHT_WRITE; + } + + status = this->out.copyInHeader ( CA_PROTO_ACCESS_RIGHTS, 0, + 0, 0, pciu->getCID(), ar, 0 ); + if ( ! status ) { + this->out.commitMsg (); + } + + return status; +} + +// +// casStrmClient::write() +// +caStatus casStrmClient :: write ( PWriteMethod pWriteMethod ) +{ + const caHdrLargeArray *pHdr = this->ctx.getMsg(); + + // no puts via compound types (for now) + if (dbr_value_offset[pHdr->m_dataType]) { + return S_cas_badType; + } + + // dont byte swap twice + if ( this->reqPayloadNeedsByteSwap ) { + int cacStatus = caNetConvert ( + pHdr->m_dataType, this->ctx.getData(), this->ctx.getData(), + false, pHdr->m_count ); + if ( cacStatus != ECA_NORMAL ) { + return S_cas_badType; + } + this->reqPayloadNeedsByteSwap = false; + } + + // + // clear async IO flag + // + this->userStartedAsyncIO = false; + + // + // DBR_STRING is stored outside the DD so it + // lumped in with arrays + // + { + caStatus servStatus; + if ( pHdr->m_count > 1u ) { + servStatus = this->writeArrayData ( pWriteMethod ); + } + else { + servStatus = this->writeScalarData ( pWriteMethod ); + } + + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + if ( this->userStartedAsyncIO ) { + if ( servStatus != S_casApp_asyncCompletion ) { + errlogPrintf ( + "Application returned %d from casChannel::write() - " + "expected S_casApp_asyncCompletion\n", + servStatus ); + servStatus = S_casApp_asyncCompletion; + } + } + else if ( servStatus == S_casApp_postponeAsyncIO ) { + casPVI & pvi ( this->ctx.getChannel()->getPVI() ); + if ( pvi.ioIsPending () ) { + pvi.addItemToIOBLockedList ( *this ); + } + else { + // Its not ok to postpone IO when there isnt at + // least one request pending. In that situation + // there is no event from the service telling us + // when its ok to start issuing requests again! + // So in that situation we tell the client that + // the service refused the request, and this + // caused the request to fail. + this->issuePosponeWhenNonePendingWarning ( "write" ); + servStatus = S_cas_posponeWhenNonePending; + } + } + else { + if ( servStatus == S_casApp_asyncCompletion ) { + servStatus = S_cas_badParameter; + errMessage ( servStatus, + "- expected asynch IO creation from casChannel::write()" ); + } + } + + return servStatus; + } +} + +// +// casStrmClient::writeScalarData() +// +caStatus casStrmClient :: writeScalarData ( PWriteMethod pWriteMethod ) +{ + const caHdrLargeArray * pHdr = this->ctx.getMsg (); + + /* + * DBR type has already been checked, but it is possible + * that "gddDbrToAit" will not track with changes in + * the DBR_XXXX type system + */ + if ( pHdr->m_dataType >= NELEMENTS(gddDbrToAit) ) { + return S_cas_badType; + } + + // a primitive type matching the atomic DBR_XXX type + aitEnum type = gddDbrToAit[pHdr->m_dataType].type; + if ( type == aitEnumInvalid ) { + return S_cas_badType; + } + + // the application type best maching this DBR_XXX type + aitUint16 app = gddDbrToAit[pHdr->m_dataType].app; + + // When possible, preconvert to best external type in order + // to reduce problems in the services + aitEnum bestWritePrimType = + app == gddAppType_value ? + this->ctx.getPV()->bestExternalType () : + type; + + gdd * pDD = new gddScalar ( app, bestWritePrimType ); + if ( ! pDD ) { + return S_cas_noMemory; + } + + // + // copy in, and convert to native type, the incoming data + // + gddStatus gddStat = aitConvert ( + pDD->primitiveType(), pDD->dataVoid(), type, + this->ctx.getData(), 1, &this->ctx.getPV()->enumStringTable() ); + caStatus status = S_cas_noConvert; + if ( gddStat >= 0 ) { + // + // set the status and severity to normal + // + pDD->setStat ( epicsAlarmNone ); + pDD->setSevr ( epicsSevNone ); + + // + // set the time stamp to the last time that + // we added bytes to the in buf + // + aitTimeStamp gddts = this->lastRecvTS; + pDD->setTimeStamp ( & gddts ); + + // + // call the server tool's virtual function + // + status = ( this->ctx.getChannel()->*pWriteMethod ) ( this->ctx, *pDD ); + } + + // + // reference count is managed by smart pointer class + // from here down + // + gddStat = pDD->unreference(); + assert ( ! gddStat ); + + return status; +} + +// +// casStrmClient::writeArrayData() +// +caStatus casStrmClient :: writeArrayData ( PWriteMethod pWriteMethod ) +{ + const caHdrLargeArray *pHdr = this->ctx.getMsg (); + + /* + * DBR type has already been checked, but it is possible + * that "gddDbrToAit" will not track with changes in + * the DBR_XXXX type system + */ + if ( pHdr->m_dataType >= NELEMENTS(gddDbrToAit) ) { + return S_cas_badType; + } + aitEnum type = gddDbrToAit[pHdr->m_dataType].type; + if ( type == aitEnumInvalid ) { + return S_cas_badType; + } + + aitEnum bestExternalType = this->ctx.getPV()->bestExternalType (); + + // the application type best maching this DBR_XXX type + aitUint16 app = gddDbrToAit[pHdr->m_dataType].app; + + // When possible, preconvert to best external type in order + // to reduce problems in the services + aitEnum bestWritePrimType = + app == gddAppType_value ? + this->ctx.getPV()->bestExternalType () : + type; + + gdd * pDD = new gddAtomic( app, bestWritePrimType, 1, pHdr->m_count); + if ( ! pDD ) { + return S_cas_noMemory; + } + + size_t size = aitSize[bestExternalType] * pHdr->m_count; + char * pData = new char [size]; + if ( ! pData ) { + pDD->unreference(); + return S_cas_noMemory; + } + + // + // ok to use the default gddDestructor here because + // an array of characters was allocated above + // + gddDestructor * pDestructor = new gddDestructor; + if ( ! pDestructor ) { + pDD->unreference(); + delete [] pData; + return S_cas_noMemory; + } + + // + // install allocated area into the DD + // + pDD->putRef ( pData, bestWritePrimType, pDestructor ); + + // + // convert the data from the protocol buffer + // to the allocated area so that they + // will be allowed to ref the DD + // + caStatus status = S_cas_noConvert; + gddStatus gddStat = aitConvert ( bestWritePrimType, + pData, type, this->ctx.getData(), + pHdr->m_count, &this->ctx.getPV()->enumStringTable() ); + if ( gddStat >= 0 ) { + // + // set the status and severity to normal + // + pDD->setStat ( epicsAlarmNone ); + pDD->setSevr ( epicsSevNone ); + + // + // set the time stamp to the last time that + // we added bytes to the in buf + // + aitTimeStamp gddts = this->lastRecvTS; + pDD->setTimeStamp ( & gddts ); + + // + // call the server tool's virtual function + // + status = ( this->ctx.getChannel()->*pWriteMethod ) ( this->ctx, *pDD ); + } + else { + status = S_cas_noConvert; + } + + gddStat = pDD->unreference (); + assert ( ! gddStat ); + + return status; +} + +// +// casStrmClient::read() +// +caStatus casStrmClient::read () +{ + const caHdrLargeArray * pHdr = this->ctx.getMsg(); + + { + gdd * pDD = 0; + caStatus status = createDBRDD ( pHdr->m_dataType, + pHdr->m_count, pDD ); + if ( status != S_cas_success ) { + return status; + } + pValueRead.set ( pDD ); + pDD->unreference (); + } + + // + // clear the async IO flag + // + this->userStartedAsyncIO = false; + + { + // + // call the server tool's virtual function + // + caStatus servStat = this->ctx.getChannel()-> + read ( this->ctx, *pValueRead ); + + // + // prevent problems when they initiate + // async IO but dont return status + // indicating so (and vise versa) + // + if ( this->userStartedAsyncIO ) { + if ( servStat != S_casApp_asyncCompletion ) { + errlogPrintf ( + "Application returned %d from casChannel::read() - " + "expected S_casApp_asyncCompletion\n", + servStat ); + servStat = S_casApp_asyncCompletion; + } + pValueRead.set ( 0 ); + } + else if ( servStat == S_casApp_asyncCompletion ) { + servStat = S_cas_badParameter; + errMessage ( servStat, + "- expected asynch IO creation from casChannel::read()"); + } + else if ( servStat != S_casApp_success ) { + pValueRead.set ( 0 ); + if ( servStat == S_casApp_postponeAsyncIO ) { + casPVI & pvi ( this->ctx.getChannel()->getPVI() ); + if ( pvi.ioIsPending () ) { + pvi.addItemToIOBLockedList ( *this ); + } + else { + // Its not ok to postpone IO when there isnt at + // least one request pending. In that situation + // there is no event from the service telling us + // when its ok to start issuing requests again! + // So in that situation we tell the client that + // the service refused the request, and this + // caused the request to fail. + this->issuePosponeWhenNonePendingWarning ( "read" ); + servStat = S_cas_posponeWhenNonePending; + } + } + } + return servStat; + } +} + +// +// casStrmClient::userName() +// +void casStrmClient::userName ( char * pBuf, unsigned bufSize ) const +{ + if ( bufSize ) { + const char *pName = this->pUserName ? this->pUserName : "?"; + strncpy ( pBuf, pName, bufSize ); + pBuf [bufSize-1] = '\0'; + } +} + +// +// caServerI::roomForNewChannel() +// +inline bool caServerI::roomForNewChannel() const +{ + return true; +} + +// +// casStrmClient::xSend() +// +outBufClient::flushCondition casStrmClient :: + xSend ( char * pBufIn, bufSizeT nBytesToSend, bufSizeT & nBytesSent ) +{ + return this->osdSend ( pBufIn, nBytesToSend, nBytesSent ); +} + +// +// casStrmClient::xRecv() +// +inBufClient::fillCondition casStrmClient::xRecv ( char * pBufIn, bufSizeT nBytesToRecv, + inBufClient::fillParameter, bufSizeT & nActualBytes ) +{ + inBufClient::fillCondition stat = + this->osdRecv ( pBufIn, nBytesToRecv, nActualBytes ); + // + // this is used to set the time stamp for write GDD's + // + this->lastRecvTS = epicsTime::getCurrent (); + return stat; +} + +// +// casStrmClient::getDebugLevel() +// +unsigned casStrmClient::getDebugLevel () const +{ + return this->getCAS().getDebugLevel (); +} + +// +// casStrmClient::casMonitorCallBack() +// +caStatus casStrmClient::casMonitorCallBack ( + epicsGuard < casClientMutex > & guard, + casMonitor & mon, const gdd & value ) +{ + return mon.response ( guard, *this, value ); +} + +// +// casStrmClient::sendErr() +// +caStatus casStrmClient::sendErr ( epicsGuard &, + const caHdrLargeArray * curp, ca_uint32_t cid, + const int reportedStatus, const char *pformat, ... ) +{ + unsigned stringSize; + char msgBuf[1024]; /* allocate plenty of space for the message string */ + if ( pformat ) { + va_list args; + va_start ( args, pformat ); + int status = vsprintf (msgBuf, pformat, args); + if ( status < 0 ) { + errPrintf (S_cas_internal, __FILE__, __LINE__, + "bad sendErr(%s)", pformat); + stringSize = 0u; + } + else { + stringSize = 1u + (unsigned) status; + } + } + else { + stringSize = 0u; + } + + unsigned hdrSize = sizeof ( caHdr ); + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + CA_V49( this->minor_version_number ) ) { + hdrSize += 2 * sizeof ( ca_uint32_t ); + } + + caHdr * pReqOut; + caStatus status = this->out.copyInHeader ( CA_PROTO_ERROR, + hdrSize + stringSize, 0, 0, cid, reportedStatus, + reinterpret_cast ( & pReqOut ) ); + if ( ! status ) { + char * pMsgString; + + /* + * copy back the request protocol + * (in network byte order) + */ + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + CA_V49( this->minor_version_number ) ) { + ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); + pReqOut->m_cmmd = htons ( curp->m_cmmd ); + pReqOut->m_postsize = htons ( 0xffff ); + pReqOut->m_dataType = htons ( curp->m_dataType ); + pReqOut->m_count = htons ( 0u ); + pReqOut->m_cid = htonl ( curp->m_cid ); + pReqOut->m_available = htonl ( curp->m_available ); + pLW[0] = htonl ( curp->m_postsize ); + pLW[1] = htonl ( curp->m_count ); + pMsgString = ( char * ) ( pLW + 2 ); + } + else { + pReqOut->m_cmmd = htons (curp->m_cmmd); + pReqOut->m_postsize = htons ( ( (ca_uint16_t) curp->m_postsize ) ); + pReqOut->m_dataType = htons (curp->m_dataType); + pReqOut->m_count = htons ( ( (ca_uint16_t) curp->m_count ) ); + pReqOut->m_cid = htonl (curp->m_cid); + pReqOut->m_available = htonl (curp->m_available); + pMsgString = ( char * ) ( pReqOut + 1 ); + } + + /* + * add their context string into the protocol + */ + memcpy ( pMsgString, msgBuf, stringSize ); + + this->out.commitMsg (); + } + + return S_cas_success; +} + +// send minor protocol revision to the client +void casStrmClient::sendVersion () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + caStatus status = this->out.copyInHeader ( CA_PROTO_VERSION, 0, + 0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 ); + if ( ! status ) { + this->out.commitMsg (); + } +} + +bool casStrmClient::inBufFull () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->in.full (); +} + +inBufClient::fillCondition casStrmClient::inBufFill () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->in.fill (); +} + +bufSizeT casStrmClient :: inBufBytesPending () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->in.bytesPresent (); +} + +bufSizeT casStrmClient :: + outBufBytesPending () const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->out.bytesPresent (); +} + +outBufClient::flushCondition casStrmClient::flush () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + return this->out.flush (); +} + +// +// casStrmClient::logBadIdWithFileAndLineno() +// +caStatus casStrmClient::logBadIdWithFileAndLineno ( + epicsGuard < casClientMutex > & guard, + const caHdrLargeArray * mp, const void * dp, + const int cacStatus, const char * pFileName, + const unsigned lineno, const unsigned idIn ) +{ + if ( pFileName ) { + caServerI::dumpMsg ( this->pHostName, this->pUserName, mp, dp, + "bad resource id in \"%s\" at line %d\n", + pFileName, lineno ); + } + else { + caServerI::dumpMsg ( this->pHostName, this->pUserName, mp, dp, + "bad resource id\n" ); + } + + int status = this->sendErr ( guard, + mp, invalidResID, cacStatus, + "Bad Resource ID=%u detected at %s.%d", + idIn, pFileName, lineno ); + + return status; +} + +/* + * casStrmClient::sendErrWithEpicsStatus() + * + * same as sendErr() except that we convert epicsStatus + * to a string and send that additional detail + */ +caStatus casStrmClient::sendErrWithEpicsStatus ( + epicsGuard < casClientMutex > & guard, const caHdrLargeArray * pMsg, + ca_uint32_t cid, caStatus epicsStatus, caStatus clientStatus ) +{ + char buf[0x1ff]; + errSymLookup ( epicsStatus, buf, sizeof(buf) ); + return this->sendErr ( guard, pMsg, cid, clientStatus, buf ); +} + diff --git a/src/cas/generic/casStrmClient.h b/src/cas/generic/casStrmClient.h new file mode 100644 index 000000000..3f6c0a51b --- /dev/null +++ b/src/cas/generic/casStrmClient.h @@ -0,0 +1,196 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef casStrmClienth +#define casStrmClienth + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casStrmClienth +# undef epicsExportSharedSymbols +#endif + +#include "epicsTime.h" + +#ifdef epicsExportSharedSymbols_casStrmClienth +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casCoreClient.h" +#include "inBuf.h" +#include "outBuf.h" + +enum xBlockingStatus { xIsBlocking, xIsntBlocking }; + +// +// casStrmClient +// +class casStrmClient : + public casCoreClient, public outBufClient, + public inBufClient, public tsDLNode < casStrmClient > { +public: + casStrmClient ( caServerI &, clientBufMemoryManager &, const caNetAddr & clientAddr ); + virtual ~casStrmClient(); + void show ( unsigned level ) const; + outBufClient::flushCondition flush (); + unsigned getDebugLevel () const; + void hostName ( char * pBuf, unsigned bufSize ) const; + void userName ( char * pBuf, unsigned bufSize ) const; + ca_uint16_t protocolRevision () const; + void sendVersion (); +protected: + caStatus processMsg (); + bool inBufFull () const; + inBufClient::fillCondition inBufFill (); + bufSizeT inBufBytesPending () const; + bufSizeT outBufBytesPending () const; +private: + //char hostNameStr [32]; + inBuf in; + outBuf out; + chronIntIdResTable < casChannelI > chanTable; + tsDLList < casChannelI > chanList; + epicsTime lastSendTS; + epicsTime lastRecvTS; + caNetAddr _clientAddr; + char * pUserName; + char * pHostName; + smartGDDPointer pValueRead; + unsigned incommingBytesToDrain; + caStatus pendingResponseStatus; + ca_uint16_t minor_version_number; + bool reqPayloadNeedsByteSwap; + bool responseIsPending; + + caStatus createChannel ( const char * pName ); + caStatus verifyRequest ( casChannelI * & pChan ); + typedef caStatus ( casStrmClient :: * pCASMsgHandler ) + ( epicsGuard < casClientMutex > & ); + static pCASMsgHandler const msgHandlers[CA_PROTO_LAST_CMMD+1u]; + + // + // one function for each CA request type + // + caStatus uknownMessageAction ( epicsGuard < casClientMutex > & ); + caStatus ignoreMsgAction ( epicsGuard < casClientMutex > & ); + caStatus versionAction ( epicsGuard < casClientMutex > & ); + caStatus echoAction ( epicsGuard < casClientMutex > & ); + caStatus eventAddAction ( epicsGuard < casClientMutex > & ); + caStatus eventCancelAction ( epicsGuard < casClientMutex > & ); + caStatus readAction ( epicsGuard < casClientMutex > & ); + caStatus readNotifyAction ( epicsGuard < casClientMutex > & ); + caStatus writeAction ( epicsGuard < casClientMutex > & ); + caStatus eventsOffAction ( epicsGuard < casClientMutex > & ); + caStatus eventsOnAction ( epicsGuard < casClientMutex > & ); + caStatus readSyncAction ( epicsGuard < casClientMutex > & ); + caStatus clearChannelAction ( epicsGuard < casClientMutex > & ); + caStatus claimChannelAction ( epicsGuard < casClientMutex > & ); + caStatus writeNotifyAction ( epicsGuard < casClientMutex > & ); + caStatus clientNameAction ( epicsGuard < casClientMutex > & ); + caStatus hostNameAction ( epicsGuard < casClientMutex > & ); + caStatus searchAction ( epicsGuard < casClientMutex > & ); + caStatus sendErr ( epicsGuard < casClientMutex > &, + const caHdrLargeArray *curp, ca_uint32_t cid, + const int reportedStatus, const char * pformat, ... ); + caStatus readNotifyFailureResponse ( epicsGuard < casClientMutex > &, + const caHdrLargeArray & msg, const caStatus ECA_XXXX ); + caStatus monitorFailureResponse ( epicsGuard < casClientMutex > &, + const caHdrLargeArray & msg, const caStatus ECA_XXXX ); + caStatus writeNotifyResponseECA_XXX ( epicsGuard < casClientMutex > &, + const caHdrLargeArray & msg, const caStatus status ); + caStatus sendErrWithEpicsStatus ( epicsGuard < casClientMutex > &, + const caHdrLargeArray * pMsg, ca_uint32_t cid, caStatus epicsStatus, + caStatus clientStatus ); + caStatus writeActionSendFailureStatus ( epicsGuard < casClientMutex > &, + const caHdrLargeArray &, ca_uint32_t cid, caStatus ); + + // + // one function for each CA request type that has + // asynchronous completion + // + caStatus createChanResponse ( epicsGuard < casClientMutex > &, + casCtx &, const pvAttachReturn & ); + caStatus readResponse ( epicsGuard < casClientMutex > &, + casChannelI * pChan, const caHdrLargeArray & msg, + const gdd & desc, const caStatus status ); + caStatus readNotifyResponse ( epicsGuard < casClientMutex > &, + casChannelI *pChan, const caHdrLargeArray & msg, + const gdd & desc, const caStatus status ); + caStatus writeResponse ( epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray & msg, const caStatus status ); + caStatus writeNotifyResponse ( epicsGuard < casClientMutex > &, casChannelI &, + const caHdrLargeArray &, const caStatus status ); + caStatus monitorResponse ( epicsGuard < casClientMutex > &, + casChannelI & chan, const caHdrLargeArray & msg, + const gdd & desc, const caStatus status ); + caStatus enumPostponedCreateChanResponse ( epicsGuard < casClientMutex > &, + casChannelI & chan, const caHdrLargeArray & hdr ); + caStatus privateCreateChanResponse ( epicsGuard < casClientMutex > &, + casChannelI & chan, const caHdrLargeArray & hdr, unsigned dbrType ); + caStatus channelCreateFailedResp ( epicsGuard < casClientMutex > &, + const caHdrLargeArray &, const caStatus createStatus ); + caStatus channelDestroyEventNotify ( + epicsGuard < casClientMutex > & guard, + casChannelI * const pChan, ca_uint32_t sid ); + caStatus accessRightsResponse ( + casChannelI * pciu ); + caStatus accessRightsResponse ( + epicsGuard < casClientMutex > &, casChannelI * pciu ); + caStatus searchResponse ( + epicsGuard < casClientMutex > &, const caHdrLargeArray &, const pvExistReturn & ); + caStatus asyncSearchResponse ( + epicsGuard < casClientMutex > &, const caNetAddr & outAddr, + const caHdrLargeArray & msg, const pvExistReturn & retVal, + ca_uint16_t protocolRevision, ca_uint32_t sequenceNumber ); + + + typedef caStatus ( casChannelI :: * PWriteMethod ) ( + const casCtx &, const gdd & ); + caStatus read (); + caStatus write ( PWriteMethod ); + caStatus writeArrayData( PWriteMethod ); + caStatus writeScalarData( PWriteMethod ); + + outBufClient::flushCondition xSend ( char * pBuf, bufSizeT nBytesToSend, + bufSizeT & nBytesSent ); + inBufClient::fillCondition xRecv ( char * pBuf, bufSizeT nBytesToRecv, + inBufClient::fillParameter parm, bufSizeT & nByesRecv ); + + virtual xBlockingStatus blockingState () const = 0; + + virtual outBufClient::flushCondition osdSend ( const char *pBuf, bufSizeT nBytesReq, + bufSizeT & nBytesActual ) = 0; + virtual inBufClient::fillCondition osdRecv ( char *pBuf, bufSizeT nBytesReq, + bufSizeT &nBytesActual ) = 0; + virtual void forceDisconnect () = 0; + caStatus casMonitorCallBack ( + epicsGuard < casClientMutex > &, casMonitor &, const gdd & ); + caStatus logBadIdWithFileAndLineno ( + epicsGuard < casClientMutex > & guard, const caHdrLargeArray * mp, + const void * dp, const int cacStatus, const char * pFileName, + const unsigned lineno, const unsigned idIn ); + void casChannelDestroyFromInterfaceNotify ( casChannelI & chan, + bool immediatedSestroyNeeded ); + static void issuePosponeWhenNonePendingWarning ( const char * pReqTypeStr ); + + casStrmClient ( const casStrmClient & ); + casStrmClient & operator = ( const casStrmClient & ); +}; + +#define logBadId(GUARD, MP, DP, CACSTAT, RESID) \ + this->logBadIdWithFileAndLineno ( GUARD, MP, DP, \ + CACSTAT, __FILE__, __LINE__, RESID ) + + +inline ca_uint16_t casStrmClient::protocolRevision () const +{ + return this->minor_version_number; +} + +#endif // casStrmClienth diff --git a/src/cas/generic/casdef.h b/src/cas/generic/casdef.h new file mode 100644 index 000000000..72f0cb365 --- /dev/null +++ b/src/cas/generic/casdef.h @@ -0,0 +1,980 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// +// Author: Jeffrey O. Hill +// johill@lanl.gov +// (505) 665 1831 +// Date: 1-95 +// + +#ifndef includecasdefh +#define includecasdefh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casdefh +# undef epicsExportSharedSymbols +#endif + +// +// EPICS +// +#include "errMdef.h" // EPICS error codes +#include "gdd.h" // EPICS data descriptors +#include "alarm.h" // EPICS alarm severity/condition + +#ifdef epicsExportSharedSymbols_casdefh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "caNetAddr.h" +#include "casEventMask.h" // EPICS event select class + +typedef aitUint32 caStatus; + +/* + * =========================================================== + * for internal use by the server library + * (and potentially returned to the server tool) + * =========================================================== + */ +#define S_cas_success 0 +#define S_cas_internal (M_cas| 1) /*Internal failure*/ +#define S_cas_noMemory (M_cas| 2) /*Memory allocation failed*/ +#define S_cas_bindFail (M_cas| 3) /*Attempt to set server's IP address/port failed*/ +#define S_cas_hugeRequest (M_cas | 4) /*Requested op does not fit*/ +#define S_cas_sendBlocked (M_cas | 5) /*Blocked for send q space*/ +#define S_cas_badElementCount (M_cas | 6) /*Bad element count*/ +#define S_cas_noConvert (M_cas | 7) /*No conversion between src & dest types*/ +#define S_cas_badWriteType (M_cas | 8) /*Src type inappropriate for write*/ +#define S_cas_noContext (M_cas | 11) /*Context parameter is required*/ +#define S_cas_disconnect (M_cas | 12) /*Lost connection to server*/ +#define S_cas_recvBlocked (M_cas | 13) /*Recv blocked*/ +#define S_cas_badType (M_cas | 14) /*Bad data type*/ +#define S_cas_timerDoesNotExist (M_cas | 15) /*Timer does not exist*/ +#define S_cas_badEventType (M_cas | 16) /*Bad event type*/ +#define S_cas_badResourceId (M_cas | 17) /*Bad resource identifier*/ +#define S_cas_chanCreateFailed (M_cas | 18) /*Unable to create channel*/ +#define S_cas_noRead (M_cas | 19) /*read access denied*/ +#define S_cas_noWrite (M_cas | 20) /*write access denied*/ +#define S_cas_noEventsSelected (M_cas | 21) /*no events selected*/ +#define S_cas_noFD (M_cas | 22) /*no file descriptors available*/ +#define S_cas_badProtocol (M_cas | 23) /*protocol from client was invalid*/ +#define S_cas_redundantPost (M_cas | 24) /*redundundant io completion post*/ +#define S_cas_badPVName (M_cas | 25) /*bad PV name from server tool*/ +#define S_cas_badParameter (M_cas | 26) /*bad parameter from server tool*/ +#define S_cas_validRequest (M_cas | 27) /*valid request*/ +#define S_cas_tooManyEvents (M_cas | 28) /*maximum simult event types exceeded*/ +#define S_cas_noInterface (M_cas | 29) /*server isnt attached to a network*/ +#define S_cas_badBounds (M_cas | 30) /*server tool changed bounds on request*/ +#define S_cas_pvAlreadyAttached (M_cas | 31) /*PV attached to another server*/ +#define S_cas_badRequest (M_cas | 32) /*client's request was invalid*/ +#define S_cas_invalidAsynchIO (M_cas | 33) /*inappropriate asynchronous IO type*/ +#define S_cas_posponeWhenNonePending (M_cas | 34) /*request postponement, none pending*/ + +/* + * =========================================================== + * returned by the application (to the server library) + * =========================================================== + */ +#define S_casApp_success 0 +#define S_casApp_noMemory (M_casApp | 1) /*Memory allocation failed*/ +#define S_casApp_pvNotFound (M_casApp | 2) /*PV not found*/ +#define S_casApp_badPVId (M_casApp | 3) /*Unknown PV identifier*/ +#define S_casApp_noSupport (M_casApp | 4) /*No application support for op*/ +#define S_casApp_asyncCompletion (M_casApp | 5) /*will complete asynchronously*/ +#define S_casApp_badDimension (M_casApp | 6) /*bad matrix size in request*/ +#define S_casApp_canceledAsyncIO (M_casApp | 7) /*asynchronous io canceled*/ +#define S_casApp_outOfBounds (M_casApp | 8) /*operation was out of bounds*/ +#define S_casApp_undefined (M_casApp | 9) /*undefined value*/ +#define S_casApp_postponeAsyncIO (M_casApp | 10) /*postpone asynchronous IO*/ + +// +// pv exist test return +// +// If the server tool does not wish to start another simultaneous +// asynchronous IO operation or if there is not enough memory +// to do so return pverDoesNotExistHere (and the client will +// retry the request later). +// +enum pvExistReturnEnum { pverExistsHere, pverDoesNotExistHere, + pverAsyncCompletion }; + +class casPV; +class casPVI; +class casCtx; +class casChannel; + +class epicsShareClass pvExistReturn { // X aCC 361 +public: + // most server tools will use this + pvExistReturn ( pvExistReturnEnum s = pverDoesNotExistHere ); + // directory service server tools will use this (see caNetAddr.h) + pvExistReturn ( const caNetAddr & ); + ~pvExistReturn (); + const pvExistReturn & operator = ( pvExistReturnEnum rhs ); + const pvExistReturn & operator = ( const caNetAddr & rhs ); + pvExistReturnEnum getStatus () const; + caNetAddr getAddr () const; + bool addrIsValid () const; +private: + caNetAddr address; + pvExistReturnEnum status; +}; + +// +// pvAttachReturn +// +class epicsShareClass pvAttachReturn { +public: + pvAttachReturn (); + pvAttachReturn ( caStatus statIn ); + pvAttachReturn ( casPV & pv ); + const pvAttachReturn & operator = ( caStatus rhs ); + const pvAttachReturn & operator = ( casPV * pPVIn ); + caStatus getStatus() const ; + casPV * getPV() const; +private: + casPV * pPV; + caStatus stat; +}; + +// +// caServer - Channel Access Server API Class +// +class caServer { + friend class casPVI; +public: + epicsShareFunc caServer (); + epicsShareFunc virtual ~caServer() = 0; + + // + // pvExistTest() + // + // This function is called by the server library when it needs to + // determine if a named PV exists (or could be created) in the + // server tool. + // + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). + // + // The server tool is encouraged to accept multiple PV name + // aliases for the same PV here. + // + // example return from this procedure: + // return pverExistsHere; // server has PV + // return pverDoesNotExistHere; // server does know of this PV + // return pverAsynchCompletion; // deferred result + // + // Return pverDoesNotExistHere if too many simultaneous + // asynchronous IO operations are pending against the server. + // The client library will retry the request at some time + // in the future. + // + epicsShareFunc virtual pvExistReturn pvExistTest ( const casCtx & ctx, + const caNetAddr & clientAddress, const char * pPVAliasName ); + + // + // pvAttach() + // + // This function is called _every_ time that a client attaches to the PV. + // The name supplied here will be either a canonical PV name or an alias + // PV name. + // + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). + // + // IMPORTANT: + // It is a responsibility of the server tool to detect attempts by + // the server library to attach to an existing PV. If the PV does not + // exist then the server tool should create it. Otherwise, the server + // tool typically will return a pointer to the preexisting PV. + // + // The server tool is encouraged to accept multiple PV name aliases + // for the same PV here. + // + // In most situations the server tool should avoid PV duplication + // by returning a pointer to an existing PV if the PV alias name + // matches a preexisting PV's name or any of its aliases. + // + // example return from this procedure: + // return pPV; // success (pass by pointer) + // return PV; // success (pass by ref) + // return S_casApp_pvNotFound; // no PV by that name here + // return S_casApp_noMemory; // no resource to create pv + // return S_casApp_asyncCompletion; // deferred completion + // + // Return S_casApp_postponeAsyncIO if too many simultaneous + // asynchronous IO operations are pending against the server. + // The server library will retry the request whenever an + // asynchronous IO operation (create or exist) completes + // against the server. + // + // In certain specialized rare situations the server tool may choose + // to create client private process variables that are not shared between + // clients. In this situation there might be several process variables + // with the same name. One for each client. For example, a server tool + // might be written that provides access to archival storage of the + // historic values of a set of process variables. Each client would + // specify its date of interest by writing into a client private process + // variable. Next the client would determine the current value of its + // subset of named process variables at its privately specified date by + // attaching to additional client private process variables. + // + epicsShareFunc virtual pvAttachReturn pvAttach ( const casCtx &ctx, + const char *pPVAliasName ); + + // + // obtain an event mask for a named event type + // to be used with casPV::postEvent() + // + epicsShareFunc casEventMask registerEvent ( const char *pName ); + + // + // common event masks + // (what is currently used by the CA clients) + // + epicsShareFunc casEventMask valueEventMask () const; // DBE_VALUE + epicsShareFunc casEventMask logEventMask () const; // DBE_LOG + epicsShareFunc casEventMask alarmEventMask () const; // DBE_ALARM + + epicsShareFunc void setDebugLevel ( unsigned level ); + epicsShareFunc unsigned getDebugLevel () const; + + // + // dump internal state of server to standard out + // + epicsShareFunc virtual void show ( unsigned level ) const; + + // + // server diagnostic counters (allowed to roll over) + // + // subscriptionEventsPosted() + // - number of events posted by server tool to the event queue + // subscriptionEventsProcessed() + // - number of events removed by server library from the event queue + // + epicsShareFunc unsigned subscriptionEventsPosted () const; + epicsShareFunc unsigned subscriptionEventsProcessed () const; + + epicsShareFunc class epicsTimer & createTimer (); + + epicsShareFunc void generateBeaconAnomaly (); + + // caStatus enableClients (); + // caStatus disableClients (); + +private: + class caServerI * pCAS; + + // deprecated interfaces (will be deleted in a future release) + epicsShareFunc virtual class pvCreateReturn createPV ( const casCtx & ctx, + const char * pPVAliasName ); + epicsShareFunc virtual pvExistReturn pvExistTest ( const casCtx & ctx, + const char * pPVAliasName ); +}; + +// +// casPV() - Channel Access Server Process Variable API Class +// +// Objects of this type are created by the caServer::pvAttach() +// object factory virtual function. +// +// Deletion Responsibility +// -------- -------------- +// o the server lib will not call "delete" directly for any +// casPV because we dont know that "new" was called to create +// the object +// o The server tool is responsible for reclaiming storage for any +// casPV it creates. The destroy() virtual function will +// assist the server tool with this responsibility. The +// virtual function casPV::destroy() does a "delete this". +// o The virtual function "destroy()" is called by the server lib +// each time that the last client attachment to the PV object is removed. +// o The destructor for this object will cancel any +// client attachment to this PV (and reclaim any resources +// allocated by the server library on its behalf) +// o If the server tool needs to asynchronously delete an object +// derived from casPV from another thread then it *must* also +// define a specialized destroy() method that prevent race conditions +// occurring when both the server library and the server tool attempt +// to destroy the same casPV derived object at the same instant. +// +// NOTE: if the server tool precreates the PV during initialization +// then it may decide to provide a "destroy()" implementation in the +// derived class which is a noop. +// +class casPV { +public: + epicsShareFunc casPV (); + + epicsShareFunc virtual ~casPV (); + + // + // This is called for each PV in the server if + // caServer::show() is called and the level is high + // enough + // + epicsShareFunc virtual void show ( unsigned level ) const; + + // + // called by the server libary each time that it wishes to + // subscribe for PV change notification from the server + // tool via postEvent() below + // + epicsShareFunc virtual 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 postEvent() below + // + epicsShareFunc virtual void interestDelete (); + + // + // called by the server library immediately before initiating + // a transaction (PV state must not be modified during a + // transaction) + // + // NOTE: there may be many read/write operations performed within + // a single transaction if a large array is being transferred + // + epicsShareFunc virtual caStatus beginTransaction (); + + // + // called by the server library immediately after completing + // a tranaction (PV state modification may resume after the + // transaction completes) + // + epicsShareFunc virtual void endTransaction (); + + // + // read + // + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). + // + // RULE: if this completes asynchronously and the server tool references + // its data into the prototype descriptor passed in the args to read() + // then this data must _not_ be modified while the reference count + // on the prototype is greater than zero. + // + // Return S_casApp_postponeAsyncIO if too many simultaneous + // asynchronous IO operations are pending aginst the PV. + // The server library will retry the request whenever an + // asynchronous IO operation (read or write) completes + // against the PV. + // + // NOTE: + // o the server tool is encouraged to change the prototype GDD's + // primitive type to whatever primitive data type is most convient + // for the server side tool. Conversion libraries in the CA server + // will convert as necessary to the client's primitive data type. + // + epicsShareFunc virtual caStatus read (const casCtx &ctx, gdd &prototype); + + // + // write + // + // The request is allowed to complete asynchronously + // (see Asynchronous IO Classes below). + // + // Return S_casApp_postponeAsyncIO if too many simultaneous + // asynchronous IO operations are pending aginst the PV. + // The server library will retry the request whenever an + // asynchronous IO operation (read or write) completes + // against the PV. + // + // NOTES: + // o The incoming GDD with application type "value" is always + // converted to the PV.bestExternalType() primitive type. + // o The time stamp in the incoming GDD is set to the time that + // the last message was received from the client. + // o Currently, no container type GDD's are passed here and + // the application type is always "value". This may change. + // o The write interface is called when the server receives + // ca_put request and the writeNotify interface is called + // when the server receives ca_put_callback request. + // o A writeNotify request is considered complete and therefore + // ready for asynchronous completion notification when any + // action that it initiates, and any cascaded actions, complete. + // o In an IOC context intermediate write requets can be discarded + // as long as the final writeRequest is always executed. In an + // IOC context intermediate writeNotify requests are never discarded. + // o If the service does not implement writeNotify then + // the base implementation of casPV :: writeNotify calls + // casPV :: write thereby preserving backwards compatibility + // with the original interface which included a virtual write + // method but not a virtual writeNotify method. + // + epicsShareFunc virtual caStatus write (const casCtx &ctx, const gdd &value); + epicsShareFunc virtual caStatus writeNotify (const casCtx &ctx, const gdd &value); + + // + // chCreate() is called each time that a PV is attached to + // by a client. The server tool may choose not to + // implement this routine (in which case the channel + // will be created by the server). If the server tool + // implements this function then it must create a casChannel object + // (or a derived class) each time that this routine is called + // + epicsShareFunc virtual casChannel * createChannel ( const casCtx &ctx, + const char * const pUserName, const char * const pHostName ); + + // + // tbe best type for clients to use when accessing the + // value of the PV + // + epicsShareFunc virtual aitEnum bestExternalType () const; + + // + // Returns the maximum bounding box for all present and + // future data stored within the PV. + // + // The virtual function "dimension()" returns the maximum + // number of dimensions in the hypercube (0=scalar, + // 1=array, 2=plane, 3=cube ...}. + // + // The virtual function "maxBound(dimension)" returns the + // maximum number of elements in a particular dimension + // of the hypercube as follows: + // + // scalar - maxDimension() returns 0 + // + // array - maxDimension() returns 1 + // maxBounds(0) supplies number of elements in array + // + // plane - maxDimension() returns 2 + // maxBounds(0) supplies number of elements in X dimension + // maxBounds(1) supplies number of elements in Y dimension + // + // cube - maxDimension() returns 3 + // maxBounds(0) supplies number of elements in X dimension + // maxBounds(1) supplies number of elements in Y dimension + // maxBounds(2) supplies number of elements in Z dimension + // . + // . + // . + // + // The default (base) "dimension()" returns zero (scalar). + // The default (base) "maxBound()" returns one (scalar bounds) + // for all dimensions. + // + // Clients will see that the PV's data is scalar if + // these routines are not supplied in the derived class. + // + // If the "dimension" argument to maxBounds() is set to + // zero then the bound on the first dimension is being + // fetched. If the "dimension" argument to maxBound() is + // set to one then the bound on the second dimension + // are being fetched... + // + epicsShareFunc virtual unsigned maxDimension () const; // return zero if scalar + epicsShareFunc virtual aitIndex maxBound ( unsigned dimension ) const; + + // + // destroy() is called + // 1) each time that a PV transitions from + // a situation where clients are attached to a situation + // where no clients are attached. + // 2) once for all PVs that exist when the server is deleted + // + // the default (base) "destroy()" executes "delete this" + // + epicsShareFunc virtual void destroy (); + + // + // Server tool calls this function to post a PV event. + // + epicsShareFunc void postEvent ( const casEventMask & select, const gdd & event ); + + // + // peek at the pv name + // + // NOTE if there are several aliases for the same PV + // this routine should return the canonical (base) + // name for the PV + // + // pointer returned must remain valid for the life time + // o fthe process variable + // +// +// !! not thread safe !! +// + epicsShareFunc virtual const char * getName () const = 0; + + // + // Find the server associated with this PV + // ****WARNING**** + // this returns NULL if the PV isnt currently installed + // into a server (this situation will exist if + // the pv isnt deleted in a derived classes replacement + // for virtual casPV::destroy() or if the PV is created + // before the server + // *************** + // + epicsShareFunc caServer * getCAS () const; + + // not to be called by the user + void destroyRequest (); + +private: + class casPVI * pPVI; + casPV & operator = ( const casPV & ); + + friend class casStrmClient; +public: + // + // This constructor has been deprecated, and is preserved for + // backwards compatibility only. Please do not use it. + // + epicsShareFunc casPV ( caServer & ); +}; + +// +// casChannel - Channel Access Server - Channel API Class +// +// Objects of this type are created by the casPV::createChannel() +// object factory virtual function. +// +// A casChannel object is created each time that a CA client +// attaches to a process variable. +// +// Deletion Responsibility +// -------- -------------- +// o the server lib will not call "delete" directly for any +// casChannel created by the server tool because we dont know +// that "new" was called to create the object +// o The server tool is responsible for reclaiming storage for any +// casChannel it creates. The destroy() virtual function will +// assist the server tool with this responsibility. The +// virtual function casChannel::destroy() does a "delete this". +// o The destructor for this object will cancel any +// client attachment to this channel (and reclaim any resources +// allocated by the server library on its behalf) +// o If the server tool needs to asynchronously delete an object +// derived from casChannel from another thread then it *must* also +// define a specialized destroy() method that prevent race conditions +// occurring when both the server library and the server tool attempt +// to destroy the same casChannel derived object at the same instant. +// +class casChannel { +public: + epicsShareFunc casChannel ( const casCtx & ctx ); + epicsShareFunc virtual ~casChannel (); + + // + // Called when the user name and the host name are changed + // for a live connection. + // + epicsShareFunc virtual void setOwner ( const char * const pUserName, + const char * const pHostName ); + + // + // the following are encouraged to change during an channel's + // lifetime + // + epicsShareFunc virtual bool readAccess () const; + epicsShareFunc virtual bool writeAccess () const; + // return true to hint that the opi should ask the operator + // for confirmation prior writing to this PV + epicsShareFunc virtual bool confirmationRequested () const; + + // + // If this function is not provided in the derived class then casPV::beginTransaction() + // is called - see casPV::beginTransaction() for additional comments. + // + epicsShareFunc virtual caStatus beginTransaction (); + + // + // If this function is not provided in the derived class then casPV::endTransaction() + // is called - see casPV::endTransaction() for additional comments. + // + epicsShareFunc virtual void endTransaction (); + + // + // read + // + // If this function is not provided in the derived class then casPV::read() + // is called - see casPV::read() for additional comments. + // + epicsShareFunc virtual caStatus read (const casCtx &ctx, gdd &prototype); + + // + // write + // + // If this function is not provided in the derived class then casPV::write() + // is called - see casPV::write() for additional comments. + // + epicsShareFunc virtual caStatus write (const casCtx &ctx, const gdd &value); + + // + // writeNotify + // + // If this function is not provided in the derived class then casPV::writeNotify() + // is called - see casPV::writeNotify() for additional comments. + // + epicsShareFunc virtual caStatus writeNotify (const casCtx &ctx, const gdd &value); + + // + // This is called for each channel in the server if + // caServer::show() is called and the level is high + // enough + // + epicsShareFunc virtual void show ( unsigned level ) const; + + // + // destroy() is called when + // 1) there is a client initiated channel delete + // 2) there is a server tool initiated PV delete + // + // the casChannel::destroy() executes a "delete this" + // + epicsShareFunc virtual void destroy (); + + // + // server tool calls this to indicate change in access + // rights has occurred + // + epicsShareFunc void postAccessRightsEvent (); + + // + // Find the PV associated with this channel + // ****WARNING**** + // this returns NULL if the channel isnt currently installed + // into a PV (this situation will exist only if + // the channel isnt deleted in a derived classes replacement + // for virtual casChannel::destroy() + // *************** + // + epicsShareFunc casPV * getPV (); + + // not to be called by the user + void destroyRequest (); + +private: + class casChannelI * pChanI; + + casChannel ( const casChannel & ); + casChannel & operator = ( const casChannel & ); + friend class casStrmClient; +}; + +// +// Asynchronous IO Classes +// +// The following virtual functions allow for asynchronous completion: +// +// Virtual Function Asynchronous IO Class +// ----------------- --------------------- +// caServer::pvExistTest() casAsyncPVExistIO +// caServer::pvAttach() casAsyncPVAttachIO +// casPV::read() casAsyncReadIO +// casPV::write() casAsyncWriteIO +// +// To initiate asynchronous completion create a corresponding +// asynchronous IO object from within one of the virtual +// functions shown in the table above and return the status code +// S_casApp_asyncCompletion. Use the member function +// "postIOCompletion()" to inform the server library that the +// requested operation has completed. +// +// +// Deletion Responsibility +// -------- -------------- +// o the server lib will not call "delete" directly for any +// casAsyncXxxIO created by the server tool because we dont know +// that "new" was called to create the object. +// o The server tool is responsible for reclaiming storage for any +// casAsyncXxxxIO it creates. The destroy virtual function will +// assist the server tool with this responsibility. The +// virtual function casAsyncXxxxIO::destroy() does a "delete this". +// o Avoid deleting the async IO object immediately after calling +// postIOCompletion(). Instead, proper operation requires that +// the server tool wait for the server lib to call destroy after +// the response is successfully queued to the client +// o If for any reason the server tool needs to cancel an IO +// operation then it should post io completion with status +// S_casApp_canceledAsyncIO. Deleting the asynchronous io +// object prior to its being allowed to forward an IO termination +// message to the client will result in NO IO CALL BACK TO THE +// CLIENT PROGRAM (in this situation a warning message will be +// printed by the server lib). +// + +// +// casAsyncReadIO +// - for use with casPV::read() +// +// **Warning** +// The server tool must reference the gdd object +// passed in the arguments to casPV::read() if it is +// necessary for this gdd object to continue to exist +// after the return from casPV::read(). If this +// is done then it is suggested that this gdd object +// be referenced in the constructor, and unreferenced +// in the destructor, for the class deriving from +// casAsyncReadIO. +// ** +class casAsyncReadIO { +public: + + // + // casAsyncReadIO() + // + epicsShareFunc casAsyncReadIO ( const casCtx & ctx ); + epicsShareFunc virtual ~casAsyncReadIO (); + + // + // place notification of IO completion on the event queue + // (this function does not delete the casAsyncReadIO object) + // + // only the first call to this function has any effect + // + epicsShareFunc caStatus postIOCompletion ( + caStatus completionStatusIn, const gdd & valueRead ); + + // + // Find the server associated with this async IO + // ****WARNING**** + // this returns NULL if the async io isnt currently installed + // into a server + // *************** + // + epicsShareFunc caServer * getCAS () const; + +private: + class casAsyncReadIOI * pAsyncReadIOI; + // + // called by the server lib after the response message + // is succesfully queued to the client or when the + // IO operation is canceled (client disconnects etc) + // + // default destroy executes a "delete this" + // + epicsShareFunc virtual void destroy (); + + casAsyncReadIO ( const casAsyncReadIO & ); + casAsyncReadIO & operator = ( const casAsyncReadIO & ); + + void serverInitiatedDestroy (); + friend class casAsyncReadIOI; +}; + +// +// casAsyncWriteIO +// - for use with casPV::write() +// +// **Warning** +// The server tool must reference the gdd object +// passed in the arguments to casPV::write() if it is +// necessary for this gdd object to continue to exist +// after the return from casPV::write(). If this +// is done then it is suggested that this gdd object +// be referenced in the constructor, and unreferenced +// in the destructor, for the class deriving from +// casAsyncWriteIO. +// ** +// +class casAsyncWriteIO { +public: + // + // casAsyncWriteIO() + // + epicsShareFunc casAsyncWriteIO ( const casCtx & ctx ); + epicsShareFunc virtual ~casAsyncWriteIO (); + + // + // place notification of IO completion on the event queue + // (this function does not delete the casAsyncWriteIO object). + // Only the first call to this function has any effect. + // + epicsShareFunc caStatus postIOCompletion ( caStatus completionStatusIn ); + + // + // Find the server associated with this async IO + // ****WARNING**** + // this returns NULL if the async io isnt currently installed + // into a server + // *************** + // + epicsShareFunc caServer * getCAS () const; + +private: + class casAsyncWriteIOI * pAsyncWriteIOI; + // + // called by the server lib after the response message + // is succesfully queued to the client or when the + // IO operation is canceled (client disconnects etc) + // + // default destroy executes a "delete this" + // + epicsShareFunc virtual void destroy (); + + casAsyncWriteIO ( const casAsyncWriteIO & ); + casAsyncWriteIO & operator = ( const casAsyncWriteIO & ); + + void serverInitiatedDestroy (); + friend class casAsyncWriteIOI; +}; + +// +// casAsyncPVExistIO +// - for use with caServer::pvExistTest() +// +class casAsyncPVExistIO { +public: + + // + // casAsyncPVExistIO() + // + epicsShareFunc casAsyncPVExistIO ( const casCtx & ctx ); + epicsShareFunc virtual ~casAsyncPVExistIO (); + + // + // place notification of IO completion on the event queue + // (this function does not delete the casAsyncPVExistIO object) + // + // only the first call to this function has any effect. + // + epicsShareFunc caStatus postIOCompletion ( const pvExistReturn & retValIn ); + + // + // Find the server associated with this async IO + // ****WARNING**** + // this returns NULL if the async io isnt currently installed + // into a server + // *************** + // + epicsShareFunc caServer * getCAS () const; + +private: + class casAsyncPVExistIOI * pAsyncPVExistIOI; + + // + // called by the server lib after the response message + // is succesfully queued to the client or when the + // IO operation is canceled (client disconnects etc) + // + // default destroy executes a "delete this" + // + epicsShareFunc virtual void destroy (); + + casAsyncPVExistIO ( const casAsyncPVExistIO & ); + casAsyncPVExistIO & operator = ( const casAsyncPVExistIO & ); + + friend class casAsyncPVExistIOI; + void serverInitiatedDestroy (); +}; + +// +// casAsyncPVAttachIO +// - for use with caServer::pvAttach() +// +class casAsyncPVAttachIO { +public: + // + // casAsyncPVAttachIO() + // + epicsShareFunc casAsyncPVAttachIO ( const casCtx & ctx ); + epicsShareFunc virtual ~casAsyncPVAttachIO (); + + // + // place notification of IO completion on the event queue + // (this function does not delete the casAsyncPVAttachIO object). + // Only the first call to this function has any effect. + // + epicsShareFunc caStatus postIOCompletion ( const pvAttachReturn & retValIn ); + + // + // Find the server associated with this async IO + // ****WARNING**** + // this returns NULL if the async io isnt currently installed + // into a server + // *************** + // + epicsShareFunc caServer * getCAS () const; + +private: + class casAsyncPVAttachIOI * pAsyncPVAttachIOI; + + // + // called by the server lib after the response message + // is succesfully queued to the client or when the + // IO operation is canceled (client disconnects etc) + // + // default destroy executes a "delete this" + // + epicsShareFunc virtual void destroy (); + + casAsyncPVAttachIO ( const casAsyncPVAttachIO & ); + casAsyncPVAttachIO & operator = ( const casAsyncPVAttachIO & ); + + friend class casAsyncPVAttachIOI; + void serverInitiatedDestroy (); +}; + +// +// casAsyncPVCreateIO (deprecated) +// (this class will be deleted in a future release) +// +class casAsyncPVCreateIO : private casAsyncPVAttachIO { +public: + epicsShareFunc casAsyncPVCreateIO ( const casCtx & ctx ); + epicsShareFunc virtual ~casAsyncPVCreateIO (); +private: + casAsyncPVCreateIO ( const casAsyncPVCreateIO & ); + casAsyncPVCreateIO & operator = ( const casAsyncPVCreateIO & ); +}; + +// +// pvCreateReturn (deprecated) +// (the class "pvCreateReturn" will be deleted in a future release) +// +class epicsShareClass pvCreateReturn : public pvAttachReturn { +public: + pvCreateReturn ( caStatus statIn ) : pvAttachReturn ( statIn ) {}; + pvCreateReturn ( casPV & pvIn ) : pvAttachReturn ( pvIn ) {}; +}; + +// TODO: +// .03 document new event types for limits change etc +// .04 certain things like native type cant be changed during +// pv id's life time or we will be required to have locking +// (doc this) +// .08 Need a mechanism by which an error detail string can be returned +// to the server from a server app (in addition to the normal +// error constant) +// .12 Should the server have an interface so that all PV names +// can be obtained (even ones created after init)? This +// would be used to implement update of directory services and +// wild cards? Problems here with knowing PV name structure and +// maximum permutations of name components. +// .16 go through this file and make sure that we are consistent about +// the use of const - use a pointer only in spots where NULL is +// allowed? +// NOTES: +// .01 When this code is used in a multi threaded situation we +// must be certain that the derived class's virtual function +// are not called between derived class destruction and base +// class destruction (or prevent problems if they are). +// Possible solutions +// 1) in the derived classes destructor set a variable which +// inhibits use of the derived classes virtual function. +// Each virtual function must check this inhibit bit and return +// an error if it is set +// 2) call a method on the base which prevents further use +// of it by the server from the derived class destructor. +// 3) some form of locking (similar to (1) above) +// + +#endif // ifdef includecasdefh (this must be the last line in this file) + diff --git a/src/cas/generic/chanIntfForPV.cc b/src/cas/generic/chanIntfForPV.cc new file mode 100644 index 000000000..c00d10771 --- /dev/null +++ b/src/cas/generic/chanIntfForPV.cc @@ -0,0 +1,67 @@ + +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "chanIntfForPV.h" +#include "casCoreClient.h" +#include "casPVI.h" + +chanIntfForPV::chanIntfForPV ( class casCoreClient & clientIn, + casChannelDestroyFromPV & destroyRefIn ) : + clientRef ( clientIn ), destroyRef ( destroyRefIn ) +{ +} + +chanIntfForPV::~chanIntfForPV () +{ + while ( casMonitor * pMon = this->monitorList.get () ) { + this->clientRef.destroyMonitor ( *pMon ); + } +} + +class casCoreClient & chanIntfForPV::client () const +{ + return this->clientRef; +} + +void chanIntfForPV::installMonitor ( casPVI & pv, casMonitor & mon ) +{ + caStatus status = pv.installMonitor ( + mon, this->monitorList ); + if ( status ) { + errMessage ( status, + "Server tool failed to register event\n" ); + } +} + +void chanIntfForPV::show ( unsigned level ) const +{ + printf ( "chanIntfForPV\n" ); + if ( level > 0 && this->monitorList.count() ) { + printf ( "List of subscriptions attached\n" ); + tsDLIterConst < casMonitor > iter = + this->monitorList.firstIter (); + while ( iter.valid () ) { + iter->show ( level - 1 ); + ++iter; + } + } +} + + diff --git a/src/cas/generic/chanIntfForPV.h b/src/cas/generic/chanIntfForPV.h new file mode 100644 index 000000000..eec31af69 --- /dev/null +++ b/src/cas/generic/chanIntfForPV.h @@ -0,0 +1,94 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef chanIntfForPVh +#define chanIntfForPVh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_chanIntfForPVh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "tsDLList.h" +#include "caProto.h" + +#ifdef epicsExportSharedSymbols_chanIntfForPVh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casStrmClient.h" +#include "casPVI.h" + +class casMonitor; +class casPVI; +class casEventMask; +class gdd; + +class casChannelDestroyFromPV { // X aCC 655 +public: + virtual void postDestroyEvent () = 0; +protected: + virtual ~casChannelDestroyFromPV() {} +}; + +class chanIntfForPV : public tsDLNode < chanIntfForPV > { +public: + chanIntfForPV ( class casCoreClient &, casChannelDestroyFromPV & ); + ~chanIntfForPV (); + class casCoreClient & client () const; + void installMonitor ( casPVI & pv, casMonitor & mon ); + casMonitor * removeMonitor ( casPVI &, ca_uint32_t monId ); + void removeSelfFromPV ( casPVI &, + tsDLList < casMonitor > & dest ); + void postEvent ( const casEventMask &, const gdd & ); + void show ( unsigned level ) const; + void postDestroyEvent (); +private: + tsDLList < casMonitor > monitorList; + class casCoreClient & clientRef; + casChannelDestroyFromPV & destroyRef; + chanIntfForPV ( const chanIntfForPV & ); + chanIntfForPV & operator = ( const chanIntfForPV & ); +}; + +inline void chanIntfForPV::postEvent ( + const casEventMask & select, const gdd & event ) +{ + this->clientRef.postEvent ( this->monitorList, select, event ); +} + +inline casMonitor * chanIntfForPV::removeMonitor ( + casPVI & pv, ca_uint32_t clientIdIn ) +{ + return pv.removeMonitor ( this->monitorList, clientIdIn ); +} + +inline void chanIntfForPV::removeSelfFromPV ( + casPVI & pv, tsDLList < casMonitor > & dest ) +{ + pv.removeChannel ( *this, this->monitorList, dest ); +} + +inline void chanIntfForPV::postDestroyEvent () +{ + this->destroyRef.postDestroyEvent (); +} + +#endif // chanIntfForPVh diff --git a/src/cas/generic/channelDestroyEvent.cpp b/src/cas/generic/channelDestroyEvent.cpp new file mode 100644 index 000000000..a375350f3 --- /dev/null +++ b/src/cas/generic/channelDestroyEvent.cpp @@ -0,0 +1,35 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + + +#define epicsExportSharedSymbols +#include "channelDestroyEvent.h" +#include "casCoreClient.h" + +caStatus channelDestroyEvent::cbFunc ( + casCoreClient & client, + epicsGuard < casClientMutex > & clientGuard, + epicsGuard < evSysMutex > & ) +{ + caStatus status = client.channelDestroyEventNotify ( + clientGuard, this->pChan, this->sid ); + if ( status != S_cas_sendBlocked ) { + delete this; + } + return status; +} + diff --git a/src/cas/generic/channelDestroyEvent.h b/src/cas/generic/channelDestroyEvent.h new file mode 100644 index 000000000..d7a82be50 --- /dev/null +++ b/src/cas/generic/channelDestroyEvent.h @@ -0,0 +1,58 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef channelDestroyEventh +#define channelDestroyEventh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_channelDestroyEventh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "caProto.h" + +#ifdef epicsExportSharedSymbols_channelDestroyEventh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casEvent.h" + +class casChannelI; + +class channelDestroyEvent : public casEvent { +public: + channelDestroyEvent ( + casChannelI * pChan, ca_uint32_t sid ); +private: + casChannelI * pChan; + ca_uint32_t sid; + caStatus cbFunc ( + casCoreClient &, + epicsGuard < casClientMutex > &, + epicsGuard < evSysMutex > & ); +}; + +inline channelDestroyEvent::channelDestroyEvent ( + casChannelI * pChanIn, ca_uint32_t sidIn ) : + pChan ( pChanIn ), sid ( sidIn ) +{ +} + +#endif // channelDestroyEventh + diff --git a/src/cas/generic/clientBufMemoryManager.cpp b/src/cas/generic/clientBufMemoryManager.cpp new file mode 100644 index 000000000..59214f526 --- /dev/null +++ b/src/cas/generic/clientBufMemoryManager.cpp @@ -0,0 +1,57 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#define epicsExportSharedSymbols +#include "clientBufMemoryManager.h" + +bufSizeT clientBufMemoryManager::maxSize () const +{ + return bufferFactory.largeBufferSize (); +} + +casBufferParm clientBufMemoryManager::allocate ( bufSizeT newMinSize ) +{ + casBufferParm parm; + if ( newMinSize <= bufferFactory.smallBufferSize () ) { + parm.pBuf = bufferFactory.newSmallBuffer (); + parm.bufSize = bufferFactory.smallBufferSize (); + } + else if ( newMinSize <= bufferFactory.largeBufferSize () ) { + parm.pBuf = bufferFactory.newLargeBuffer (); + parm.bufSize = bufferFactory.largeBufferSize (); + } + else { + parm.pBuf = static_cast < char * > ( ::operator new ( newMinSize ) ); + parm.bufSize = newMinSize; + } + return parm; +} + +void clientBufMemoryManager::release ( char * pBuf, bufSizeT bufSize ) +{ + if ( bufSize == bufferFactory.smallBufferSize () ) { + bufferFactory.destroySmallBuffer ( pBuf ); + } + else if ( bufSize == bufferFactory.largeBufferSize () ) { + bufferFactory.destroyLargeBuffer ( pBuf ); + } + else { + ::operator delete ( pBuf ); + } +} + diff --git a/src/cas/generic/clientBufMemoryManager.h b/src/cas/generic/clientBufMemoryManager.h new file mode 100644 index 000000000..9d63431e7 --- /dev/null +++ b/src/cas/generic/clientBufMemoryManager.h @@ -0,0 +1,50 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef clientBufMemoryManagerh +#define clientBufMemoryManagerh + +#include + +typedef unsigned bufSizeT; +static const unsigned bufSizeT_MAX = UINT_MAX; + +class casBufferFactory { +public: + casBufferFactory (); + ~casBufferFactory (); + unsigned smallBufferSize () const; + char * newSmallBuffer (); + void destroySmallBuffer ( char * pBuf ); + unsigned largeBufferSize () const; + char * newLargeBuffer (); + void destroyLargeBuffer ( char * pBuf ); +private: + void * smallBufFreeList; + void * largeBufFreeList; + unsigned largeBufferSizePriv; +}; + +struct casBufferParm { + char * pBuf; + bufSizeT bufSize; +}; + +class clientBufMemoryManager { +public: + casBufferParm allocate ( bufSizeT newMinSize ); + void release ( char * pBuf, bufSizeT bufSize ); + bufSizeT maxSize () const; +private: + casBufferFactory bufferFactory; +}; + +#endif // clientBufMemoryManagerh + diff --git a/src/cas/generic/inBuf.cc b/src/cas/generic/inBuf.cc new file mode 100644 index 000000000..609fc9252 --- /dev/null +++ b/src/cas/generic/inBuf.cc @@ -0,0 +1,180 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "inBuf.h" + +// +// inBuf::inBuf() +// +inBuf::inBuf ( inBufClient & clientIn, clientBufMemoryManager & memMgrIn, + bufSizeT ioMinSizeIn ) : + client ( clientIn ), memMgr ( memMgrIn ), pBuf ( 0 ), + bufSize ( 0u ), bytesInBuffer ( 0u ), nextReadIndex ( 0u ), + ioMinSize ( ioMinSizeIn ), ctxRecursCount ( 0u ) +{ + if ( this->ioMinSize == 0 ) { + this->ioMinSize = 1; + } + casBufferParm bufParm = this->memMgr.allocate ( this->ioMinSize ); + this->pBuf = bufParm.pBuf; + this->bufSize = bufParm.bufSize; +} + +// +// inBuf::~inBuf() +// (virtual destructor) +// +inBuf::~inBuf () +{ + assert ( this->ctxRecursCount == 0 ); + this->memMgr.release ( this->pBuf, this->bufSize ); +} + +// +// inBuf::show() +// +void inBuf :: show (unsigned level) const +{ + if ( level > 1u ) { + printf ( + "\tUnprocessed request bytes = %d\n", + this->bytesPresent () ); + } +} + +// +// inBuf::fill() +// +inBufClient::fillCondition inBuf::fill ( inBufClient::fillParameter parm ) +{ + bufSizeT bytesOpen; + bufSizeT bytesRecv; + inBufClient::fillCondition stat; + + // + // move back any prexisting data to the start of the buffer + // + if ( this->nextReadIndex > 0 ) { + assert ( this->bytesInBuffer >= this->nextReadIndex ); + bufSizeT unprocessedBytes = + this->bytesInBuffer - this->nextReadIndex; + // + // memmove() handles overlapping buffers + // + if (unprocessedBytes>0u) { + memmove (this->pBuf, this->pBuf+this->nextReadIndex, + unprocessedBytes); + } + this->bytesInBuffer = unprocessedBytes; + this->nextReadIndex = 0u; + } + + // + // noop if the buffer is full + // + bytesOpen = this->bufSize - this->bytesInBuffer; + if ( bytesOpen < this->ioMinSize ) { + return inBufClient::casFillNone; + } + + stat = this->client.xRecv ( &this->pBuf[this->bytesInBuffer], + bytesOpen, parm, bytesRecv ); + if ( stat == inBufClient::casFillProgress ) { + assert (bytesRecv<=bytesOpen); + this->bytesInBuffer += bytesRecv; + + if ( this->client.getDebugLevel() > 2u ) { + char buf[64]; + + this->client.hostName ( buf, sizeof ( buf ) ); + + fprintf ( stderr, "CAS Incoming: %u byte msg from %s\n", + bytesRecv, buf); + } + } + + return stat; +} + +// +// inBuf::pushCtx () +// +const inBufCtx inBuf::pushCtx ( bufSizeT headerSize, // X aCC 361 + bufSizeT bodySize ) +{ + if ( headerSize + bodySize > ( this->bytesInBuffer - this->nextReadIndex ) || + this->ctxRecursCount == UINT_MAX ) { + return inBufCtx (); + } + else { + inBufCtx result (*this); + bufSizeT effectiveNextReadIndex = this->nextReadIndex + headerSize; + this->pBuf = this->pBuf + effectiveNextReadIndex; + this->bytesInBuffer -= effectiveNextReadIndex; + this->nextReadIndex = 0; + this->bytesInBuffer = bodySize; + this->bufSize = this->bytesInBuffer; + this->ctxRecursCount++; + return result; + } +} + +// +// inBuf::popCtx () +// +bufSizeT inBuf::popCtx ( const inBufCtx &ctx ) // X aCC 361 +{ + if ( ctx.stat==inBufCtx::pushCtxSuccess ) { + bufSizeT bytesRemoved = this->nextReadIndex; + this->pBuf = ctx.pBuf; + this->bufSize = ctx.bufSize; + this->bytesInBuffer = ctx.bytesInBuffer; + this->nextReadIndex = ctx.nextReadIndex; + assert ( this->ctxRecursCount > 0 ); + this->ctxRecursCount--; + return bytesRemoved; + } + else { + return 0; + } +} + +void inBuf::expandBuffer () +{ + bufSizeT max = this->memMgr.maxSize(); + if ( this->bufSize < max ) { + casBufferParm bufParm = this->memMgr.allocate ( max ); + bufSizeT unprocessedBytes = this->bytesPresent (); + memcpy ( bufParm.pBuf, &this->pBuf[this->nextReadIndex], unprocessedBytes ); + this->bytesInBuffer = unprocessedBytes; + this->nextReadIndex = 0u; + this->memMgr.release ( this->pBuf, this->bufSize ); + this->pBuf = bufParm.pBuf; + this->bufSize = bufParm.bufSize; + } +} + +unsigned inBuf::bufferSize () const +{ + return this->bufSize; +} + + diff --git a/src/cas/generic/inBuf.h b/src/cas/generic/inBuf.h new file mode 100644 index 000000000..066a38964 --- /dev/null +++ b/src/cas/generic/inBuf.h @@ -0,0 +1,159 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef inBufh +#define inBufh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_inBufh +# undef epicsExportSharedSymbols +#endif + +#undef epicsAssertAuthor +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#include "epicsAssert.h" + +#ifdef epicsExportSharedSymbols_inBufh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "clientBufMemoryManager.h" + +class inBufCtx { + friend class inBuf; +public: + enum pushCtxResult { pushCtxNoSpace, pushCtxSuccess }; + inBufCtx ( const class inBuf & ); // success + inBufCtx (); // failure + + pushCtxResult pushResult () const; + +private: + pushCtxResult stat; + char * pBuf; + bufSizeT bufSize; + bufSizeT bytesInBuffer; + bufSizeT nextReadIndex; +}; + +class inBufClient { // X aCC 655 +public: + enum fillCondition { casFillNone, casFillProgress, + casFillDisconnect }; + // this is a hack for a Solaris IP kernel feature + enum fillParameter { fpNone, fpUseBroadcastInterface }; + virtual unsigned getDebugLevel () const = 0; + virtual fillCondition xRecv ( char *pBuf, bufSizeT nBytesToRecv, + enum fillParameter parm, bufSizeT &nByesRecv ) = 0; + virtual void hostName ( char *pBuf, unsigned bufSize ) const = 0; +protected: + virtual ~inBufClient() {} +}; + +class inBuf { + friend class inBufCtx; +public: + inBuf ( class inBufClient &, class clientBufMemoryManager &, + bufSizeT ioMinSizeIn ); + virtual ~inBuf (); + bufSizeT bytesPresent () const; + bool full () const; + inBufClient::fillCondition fill ( + inBufClient::fillParameter parm = inBufClient::fpNone ); + void show ( unsigned level ) const; + void removeMsg ( bufSizeT nBytes ); + char * msgPtr () const; + // + // This is used to create recursive protocol stacks. A subsegment + // of the buffer of max size "maxSize" is assigned to the next + // layer down in the protocol stack by pushCtx () until popCtx () + // is called. The roiutine popCtx () returns the actual number + // of bytes used by the next layer down. + // + // pushCtx() returns an outBufCtx to be restored by popCtx() + // + const inBufCtx pushCtx ( bufSizeT headerSize, bufSizeT bodySize ); + bufSizeT popCtx ( const inBufCtx & ); // returns actual size + unsigned bufferSize () const; + void expandBuffer (); +private: + class inBufClient & client; + class clientBufMemoryManager & memMgr; + char * pBuf; + bufSizeT bufSize; + bufSizeT bytesInBuffer; + bufSizeT nextReadIndex; + bufSizeT ioMinSize; + unsigned ctxRecursCount; + inBuf ( const inBuf & ); + inBuf & operator = ( const inBuf & ); +}; + +// +// inBuf::bytesPresent() +// +inline bufSizeT inBuf::bytesPresent () const +{ + return this->bytesInBuffer - this->nextReadIndex; +} + +// +// inBuf::full() +// +inline bool inBuf::full () const +{ + if (this->bufSize-this->bytesPresent()ioMinSize) { + return true; + } + return false; +} + +// +// inBuf::msgPtr() +// +inline char *inBuf::msgPtr () const +{ + return &this->pBuf[this->nextReadIndex]; +} + +// +// inBuf::removeMsg() +// +inline void inBuf::removeMsg ( bufSizeT nBytes ) +{ + this->nextReadIndex += nBytes; + assert ( this->nextReadIndex <= this->bytesInBuffer ); +} + +// +// inBufCtx::inBufCtx () +// +inline inBufCtx::inBufCtx () : + stat (pushCtxNoSpace) {} + +// +// inBufCtx::inBufCtx () +// +inline inBufCtx::inBufCtx (const inBuf &inBufIn) : + stat (pushCtxSuccess), pBuf (inBufIn.pBuf), + bufSize (inBufIn.bufSize), bytesInBuffer (inBufIn.bytesInBuffer), + nextReadIndex (inBufIn.nextReadIndex) {} + +// +// inBufCtx::pushResult +// +inline inBufCtx::pushCtxResult inBufCtx::pushResult () const +{ + return this->stat; +} + +#endif // inBufh + diff --git a/src/cas/generic/ioBlocked.h b/src/cas/generic/ioBlocked.h new file mode 100644 index 000000000..97b705df6 --- /dev/null +++ b/src/cas/generic/ioBlocked.h @@ -0,0 +1,62 @@ + +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef ioBlockedh +#define ioBlockedh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_ioBlockedh +# undef epicsExportSharedSymbols +#endif + +#include "tsDLList.h" + +#ifdef epicsExportSharedSymbols_ioBlockedh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class ioBlocked : public tsDLNode < ioBlocked > { +friend class ioBlockedList; +public: + ioBlocked (); + virtual ~ioBlocked (); + bool isBlocked (); +private: + class ioBlockedList * pList; + virtual void ioBlockedSignal (); +}; + +class ioBlockedList : private tsDLList { +friend class ioBlocked; +public: + ioBlockedList (); + virtual ~ioBlockedList (); + void signal (); + void addItemToIOBLockedList ( ioBlocked & item ); + ioBlockedList ( const ioBlockedList & ); + ioBlockedList & operator = ( const ioBlockedList & ); +}; + +inline bool ioBlocked :: isBlocked () +{ + return this->pList != NULL; +} + +#endif // ioBlockedh + diff --git a/src/cas/generic/mt/README b/src/cas/generic/mt/README new file mode 100644 index 000000000..73d5be3dd --- /dev/null +++ b/src/cas/generic/mt/README @@ -0,0 +1,5 @@ + +This directory contains files specific to the multi-threaded +version of the CA server + +- diff --git a/src/cas/generic/mt/ioBlocked.cc b/src/cas/generic/mt/ioBlocked.cc new file mode 100644 index 000000000..51e4b9e2a --- /dev/null +++ b/src/cas/generic/mt/ioBlocked.cc @@ -0,0 +1,81 @@ +/*************************************************************************\ +* 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 +// This file implements a IO blocked list NOOP for multi-threaded systems +// + +#include + +#define epicsExportSharedSymbols +#include "casdef.h" + +// +// ioBlocked::~ioBlocked() +// +ioBlocked::ioBlocked() : + pList(NULL) +{ +} + +// +// ioBlocked::~ioBlocked() +// +ioBlocked::~ioBlocked() +{ +} + +// +// ioBlocked::ioBlockedSignal () +// +void ioBlocked::ioBlockedSignal () +{ + // + // this must _not_ be pure virtual because + // there are situations where this is called + // inbetween the derived class's and this base + // class's destructors, and therefore a + // NOOP is required + // +} + +// +// ioBlockedList::ioBlockedList () +// +ioBlockedList::ioBlockedList () +{ +} + +// +// ioBlockedList::~ioBlockedList () +// (NOOP on MT system) +// +ioBlockedList::~ioBlockedList () +{ +} + +// +// ioBlockedList::signal() +// (NOOP on MT system) +// +void ioBlockedList::signal() +{ +} + +// +// ioBlockedList::addItemToIOBLockedList() +// (NOOP on MT system) +// +void ioBlockedList::addItemToIOBLockedList(ioBlocked &) +{ +} + + diff --git a/src/cas/generic/outBuf.cc b/src/cas/generic/outBuf.cc new file mode 100644 index 000000000..d639657ce --- /dev/null +++ b/src/cas/generic/outBuf.cc @@ -0,0 +1,333 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include "errlog.h" +#include "epicsTime.h" + +#define epicsExportSharedSymbols +#include "outBuf.h" +#include "osiWireFormat.h" + +const char * outBufClient :: ppFlushCondText[3] = +{ + "flushNone", + "flushProgress", + "flushDisconnect" +}; + +// +// outBuf::outBuf() +// +outBuf::outBuf ( outBufClient & clientIn, + clientBufMemoryManager & memMgrIn ) : + client ( clientIn ), memMgr ( memMgrIn ), bufSize ( 0 ), + stack ( 0u ), ctxRecursCount ( 0u ) +{ + casBufferParm bufParm = memMgr.allocate ( 1 ); + this->pBuf = bufParm.pBuf; + this->bufSize = bufParm.bufSize; + memset ( this->pBuf, '\0', this->bufSize ); +} + +// +// outBuf::~outBuf() +// +outBuf::~outBuf() +{ + assert ( this->ctxRecursCount == 0 ); + memMgr.release ( this->pBuf, this->bufSize ); +} + +// +// outBuf::allocRawMsg () +// +caStatus outBuf::allocRawMsg ( bufSizeT msgsize, void **ppMsg ) +{ + bufSizeT stackNeeded; + + msgsize = CA_MESSAGE_ALIGN ( msgsize ); + + if ( msgsize > this->bufSize ) { + this->expandBuffer (); + if ( msgsize > this->bufSize ) { + return S_cas_hugeRequest; + } + } + + stackNeeded = this->bufSize - msgsize; + + if ( this->stack > stackNeeded ) { + // + // Try to flush the output queue + // + this->flush (); + + // + // If this failed then the fd is nonblocking + // and we will let select() take care of it + // + if ( this->stack > stackNeeded ) { + this->client.sendBlockSignal(); + return S_cas_sendBlocked; + } + } + + // + // it fits so commitMsg() will move the stack pointer forward + // + *ppMsg = (void *) &this->pBuf[this->stack]; + + return S_cas_success; +} + +// code size is allowed to increase here somewhat in the +// interest of efficency since this is a very frequently +// called function +caStatus outBuf::copyInHeader ( ca_uint16_t response, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t responseSpecific, void **ppPayload ) +{ + ca_uint32_t alignedPayloadSize = CA_MESSAGE_ALIGN ( payloadSize ); + char * pPayload; + + if ( alignedPayloadSize < 0xffff && nElem < 0xffff ) { + ca_uint32_t msgSize = sizeof ( caHdr ) + alignedPayloadSize; + caHdr * pHdr; + caStatus status = this->allocRawMsg ( + msgSize, reinterpret_cast < void ** > ( & pHdr ) ); + if ( status != S_cas_success ) { + return status; + } + + AlignedWireRef < epicsUInt16 > ( pHdr->m_cmmd ) = response; + AlignedWireRef < epicsUInt16 > ( pHdr->m_dataType ) = dataType; + AlignedWireRef < epicsUInt32 > ( pHdr->m_cid ) = cid; + AlignedWireRef < epicsUInt32 > ( pHdr->m_available ) = responseSpecific; + AlignedWireRef < epicsUInt16 > ( pHdr->m_postsize ) = + static_cast < epicsUInt16 > ( alignedPayloadSize ); + AlignedWireRef < epicsUInt16 > ( pHdr->m_count ) = + static_cast < epicsUInt16 > ( nElem ); + pPayload = reinterpret_cast < char * > ( pHdr + 1 ); + } + else { + ca_uint32_t msgSize = sizeof ( caHdr ) + + 2 * sizeof (ca_uint32_t) + alignedPayloadSize; + caHdr * pHdr; + caStatus status = this->allocRawMsg ( + msgSize, reinterpret_cast < void ** > ( & pHdr ) ); + if ( status != S_cas_success ) { + return status; + } + + AlignedWireRef < epicsUInt16 > ( pHdr->m_cmmd ) = response; + AlignedWireRef < epicsUInt16 > ( pHdr->m_dataType ) = dataType; + AlignedWireRef < epicsUInt32 > ( pHdr->m_cid ) = cid; + AlignedWireRef < epicsUInt32 > ( pHdr->m_available ) = responseSpecific; + AlignedWireRef < epicsUInt16 > ( pHdr->m_postsize ) = 0xffff; + AlignedWireRef < epicsUInt16 > ( pHdr->m_count ) = 0; + ca_uint32_t * pLW = reinterpret_cast < ca_uint32_t * > ( pHdr + 1 ); + AlignedWireRef < epicsUInt32 > sizeWireRef ( pLW[0] ); + sizeWireRef = alignedPayloadSize; + AlignedWireRef < epicsUInt32 > nElemWireRef ( pLW[1] ); + nElemWireRef= nElem; + pPayload = reinterpret_cast < char * > ( pLW + 2 ); + } + + /* zero out pad bytes */ + if ( alignedPayloadSize > payloadSize ) { + memset ( pPayload + payloadSize, '\0', + alignedPayloadSize - payloadSize ); + } + + if ( ppPayload ) { + *ppPayload = pPayload; + } + + return S_cas_success; +} + +// +// outBuf::commitMsg () +// +void outBuf::commitMsg () +{ + ca_uint32_t payloadSize; + ca_uint32_t elementCount; + ca_uint32_t hdrSize; + + const caHdr * mp = ( caHdr * ) & this->pBuf[ this->stack ]; + if ( mp->m_postsize == 0xffff || mp->m_count == 0xffff ) { + const ca_uint32_t *pLW = reinterpret_cast ( mp + 1 ); + payloadSize = AlignedWireRef < const epicsUInt32 > ( pLW[0] ); + elementCount = AlignedWireRef < const epicsUInt32 > ( pLW[1] ); + hdrSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); + } + else { + payloadSize = AlignedWireRef < const epicsUInt16 > ( mp->m_postsize ); + elementCount = AlignedWireRef < const epicsUInt16 > ( mp->m_count ); + hdrSize = sizeof ( caHdr ); + } + + this->commitRawMsg ( hdrSize + payloadSize ); + + unsigned debugLevel = this->client.getDebugLevel(); + if ( debugLevel ) { + epicsUInt16 cmmd = AlignedWireRef < const epicsUInt16 > ( mp->m_cmmd ); + if ( cmmd != CA_PROTO_VERSION || debugLevel > 2 ) { + epicsUInt16 type = AlignedWireRef < const epicsUInt16 > ( mp->m_dataType ); + epicsUInt32 cid = AlignedWireRef < const epicsUInt32 > ( mp->m_cid ); + epicsUInt32 avail = AlignedWireRef < const epicsUInt32 > ( mp->m_available ); + fprintf ( stderr, + "CAS Response: cmd=%d id=%x typ=%d cnt=%d psz=%d avail=%x outBuf ptr=%p \n", + cmmd, cid, type, elementCount, payloadSize, avail, + static_cast < const void * > ( mp ) ); + } + } +} + +// +// outBuf::commitMsg () +// +void outBuf::commitMsg ( ca_uint32_t reducedPayloadSize ) +{ + caHdr * mp = ( caHdr * ) & this->pBuf[ this->stack ]; + reducedPayloadSize = CA_MESSAGE_ALIGN ( reducedPayloadSize ); + if ( mp->m_postsize == 0xffff || mp->m_count == 0xffff ) { + ca_uint32_t *pLW = reinterpret_cast ( mp + 1 ); + AlignedWireRef < epicsUInt32 > payloadSizeExtended ( pLW[0] ); + assert ( reducedPayloadSize <= payloadSizeExtended ); + payloadSizeExtended = reducedPayloadSize; + } + else { + AlignedWireRef < epicsUInt16 > payloadSizeOnWire ( mp->m_postsize ); + ca_uint32_t payloadSize = payloadSizeOnWire; + assert ( reducedPayloadSize <= payloadSize ); + payloadSizeOnWire = static_cast < ca_uint16_t > ( reducedPayloadSize ); + } + this->commitMsg (); +} + +// +// outBuf::flush () +// +outBufClient::flushCondition outBuf :: flush () +{ + if ( this->ctxRecursCount > 0 ) { + return outBufClient::flushNone; + } + + bufSizeT nBytesSent; + //epicsTime beg = epicsTime::getCurrent (); + outBufClient :: flushCondition cond = + this->client.xSend ( this->pBuf, this->stack, nBytesSent ); + //epicsTime end = epicsTime::getCurrent (); + //printf ( "send of %u bytes, stat =%s, cost us %f u sec\n", + // this->stack, this->client.ppFlushCondText[cond], ( end - beg ) * 1e6 ); + if ( cond == outBufClient::flushProgress ) { + if ( nBytesSent >= this->stack ) { + this->stack = 0u; + } + else { + bufSizeT len = this->stack - nBytesSent; + // + // memmove() is ok with overlapping buffers + // + //epicsTime beg = epicsTime::getCurrent (); + memmove ( this->pBuf, &this->pBuf[nBytesSent], len ); + //epicsTime end = epicsTime::getCurrent (); + //printf ( "mem move cost us %f nano sec\n", ( end - beg ) * 1e9 ); + this->stack = len; + } + + if ( this->client.getDebugLevel () > 2u ) { + char buf[64]; + this->client.hostName ( buf, sizeof ( buf ) ); + fprintf ( stderr, "CAS outgoing: %u byte reply to %s\n", + nBytesSent, buf ); + } + } + return cond; +} + +// +// outBuf::pushCtx () +// +const outBufCtx outBuf::pushCtx ( bufSizeT headerSize, // X aCC 361 + bufSizeT maxBodySize, + void *&pHeader ) +{ + bufSizeT totalSize = headerSize + maxBodySize; + caStatus status; + + status = this->allocRawMsg ( totalSize, & pHeader ); + if ( status != S_cas_success ) { + return outBufCtx (); + } + else if ( this->ctxRecursCount >= UINT_MAX ) { + return outBufCtx (); + } + else { + outBufCtx result ( *this ); + this->pBuf = this->pBuf + this->stack + headerSize; + this->stack = 0; + this->bufSize = maxBodySize; + this->ctxRecursCount++; + return result; + } +} + +// +// outBuf::popCtx () +// +bufSizeT outBuf::popCtx (const outBufCtx &ctx) // X aCC 361 +{ + if (ctx.stat==outBufCtx::pushCtxSuccess) { + bufSizeT bytesAdded = this->stack; + this->pBuf = ctx.pBuf; + this->bufSize = ctx.bufSize; + this->stack = ctx.stack; + assert (this->ctxRecursCount>0u); + this->ctxRecursCount--; + return bytesAdded; + } + else { + return 0; + } +} + +// +// outBuf::show (unsigned level) +// +void outBuf::show (unsigned level) const +{ + if ( level > 1u ) { + printf("\tUndelivered response bytes = %d\n", this->bytesPresent()); + } +} + +void outBuf::expandBuffer () +{ + bufSizeT max = this->memMgr.maxSize(); + if ( this->bufSize < max ) { + casBufferParm bufParm = this->memMgr.allocate ( max ); + memcpy ( bufParm.pBuf, this->pBuf, this->stack ); + this->memMgr.release ( this->pBuf, this->bufSize ); + this->pBuf = bufParm.pBuf; + this->bufSize = bufParm.bufSize; + } +} + + diff --git a/src/cas/generic/outBuf.h b/src/cas/generic/outBuf.h new file mode 100644 index 000000000..dd1e62382 --- /dev/null +++ b/src/cas/generic/outBuf.h @@ -0,0 +1,171 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef outBufh +#define outBufh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_outBufh +# undef epicsExportSharedSymbols +#endif + +#include "caProto.h" + +#ifdef epicsExportSharedSymbols_outBufh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casdef.h" +#include "clientBufMemoryManager.h" + +// +// outBufCtx +// +class outBufCtx { + friend class outBuf; +public: + enum pushCtxResult { pushCtxNoSpace, pushCtxSuccess }; + outBufCtx ( const class outBuf & ); // success + outBufCtx (); // failure + + pushCtxResult pushResult () const; + +private: + pushCtxResult stat; + char * pBuf; + bufSizeT bufSize; + bufSizeT stack; +}; + +class outBufClient { // X aCC 655 +public: + enum flushCondition { + flushNone = 0, + flushProgress = 1, + flushDisconnect = 2 + }; + static const char * ppFlushCondText[3]; + virtual unsigned getDebugLevel () const = 0; + virtual void sendBlockSignal () = 0; + virtual flushCondition xSend ( char *pBuf, bufSizeT nBytesToSend, + bufSizeT & nBytesSent ) = 0; + virtual void hostName ( char *pBuf, unsigned bufSize ) const = 0; + virtual bufSizeT osSendBufferSize () const = 0; +protected: + virtual ~outBufClient() {} +}; + +// +// outBuf +// +class outBuf { + friend class outBufCtx; +public: + outBuf ( outBufClient &, clientBufMemoryManager & ); + virtual ~outBuf (); + + bufSizeT bytesPresent () const; + + // + // flush output queue + // (returns the number of bytes sent) + // + outBufClient::flushCondition flush (); + + void show (unsigned level) const; + + unsigned bufferSize () const; + + // + // allocate message buffer space + // (leaves message buffer locked) + // + caStatus copyInHeader ( ca_uint16_t response, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t responseSpecific, void **pPayload ); + + // + // commit message created with copyInHeader + // + void commitMsg (); + void commitMsg ( ca_uint32_t reducedPayloadSize ); + + caStatus allocRawMsg ( bufSizeT msgsize, void **ppMsg ); + void commitRawMsg ( bufSizeT size ); + + // + // This is used to create recursive protocol stacks. A subsegment + // of the buffer of max size "maxSize" is assigned to the next + // layer down in the protocol stack by pushCtx () until popCtx () + // is called. The routine popCtx () returns the actual number + // of bytes used by the next layer down. + // + // pushCtx() returns an outBufCtx to be restored by popCtx() + // + const outBufCtx pushCtx ( bufSizeT headerSize, + bufSizeT maxBodySize, void *&pHeader ); + bufSizeT popCtx ( const outBufCtx & ); // returns actual size + +private: + outBufClient & client; + clientBufMemoryManager & memMgr; + char * pBuf; + bufSizeT bufSize; + bufSizeT stack; + unsigned ctxRecursCount; + + void expandBuffer (); + + outBuf ( const outBuf & ); + outBuf & operator = ( const outBuf & ); +}; + +// +// outBuf::bytesPresent () +// number of bytes in the output queue +// +inline bufSizeT outBuf::bytesPresent () const +{ + return this->stack; +} + +// +// outBuf::commitRawMsg() +// +inline void outBuf::commitRawMsg (bufSizeT size) +{ + this->stack += size; + assert ( this->stack <= this->bufSize ); +} + +// +// outBufCtx::outBufCtx () +// +inline outBufCtx::outBufCtx () : + stat (pushCtxNoSpace) {} + +// +// outBufCtx::outBufCtx () +// +inline outBufCtx::outBufCtx (const outBuf &outBufIn) : + stat (pushCtxSuccess), pBuf (outBufIn.pBuf), + bufSize (outBufIn.bufSize), stack (outBufIn.stack) {} + +// +// outBufCtx::pushResult +// +inline outBufCtx::pushCtxResult outBufCtx::pushResult () const +{ + return this->stat; +} + +#endif // outBufh + diff --git a/src/cas/generic/pvAttachReturn.cc b/src/cas/generic/pvAttachReturn.cc new file mode 100644 index 000000000..2fb175bc1 --- /dev/null +++ b/src/cas/generic/pvAttachReturn.cc @@ -0,0 +1,94 @@ + +/*************************************************************************\ +* 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: Jeffrey O. Hill +// johill@lanl.gov +// + +#define epicsExportSharedSymbols +#include "casdef.h" + +pvAttachReturn::pvAttachReturn () +{ + this->pPV = 0; + // + // A pv name is required for success + // + this->stat = S_cas_badParameter; +} + +pvAttachReturn::pvAttachReturn ( caStatus statIn ) +{ + this->pPV = NULL; + if ( statIn == S_casApp_success ) { + // + // A pv name is required for success + // + this->stat = S_cas_badParameter; + } + else { + this->stat = statIn; + } +} + +pvAttachReturn::pvAttachReturn ( casPV & pv ) +{ + this->pPV = & pv; + this->stat = S_casApp_success; +} + +const pvAttachReturn & pvAttachReturn::operator = ( caStatus rhs ) +{ + this->pPV = NULL; + if ( rhs == S_casApp_success ) { + this->stat = S_cas_badParameter; + } + else { + this->stat = rhs; + } + return *this; +} + +// +// const pvAttachReturn &operator = (casPV &pvIn) +// +// The above assignment operator is not included +// because it does not match the strict definition of an +// assignment operator unless "const casPV &" +// is passed in, and we cant use a const PV +// pointer here because the server library _will_ make +// controlled modification of the PV in the future. +// +const pvAttachReturn & pvAttachReturn::operator = ( casPV *pPVIn ) +{ + if ( pPVIn != NULL ) { + this->stat = S_casApp_success; + } + else { + this->stat = S_casApp_pvNotFound; + } + this->pPV = pPVIn; + return *this; +} + +caStatus pvAttachReturn::getStatus () const +{ + return this->stat; +} + +casPV * pvAttachReturn::getPV () const +{ + return this->pPV; +} + diff --git a/src/cas/generic/pvExistReturn.cc b/src/cas/generic/pvExistReturn.cc new file mode 100644 index 000000000..eafaa17d3 --- /dev/null +++ b/src/cas/generic/pvExistReturn.cc @@ -0,0 +1,59 @@ + +/*************************************************************************\ +* 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: Jeffrey O. Hill +// johill@lanl.gov +// + +#define epicsExportSharedSymbols +#include "casdef.h" + +pvExistReturn::pvExistReturn ( pvExistReturnEnum s ) : + status ( s ) {} + +pvExistReturn::pvExistReturn ( const caNetAddr & addrIn ) : + address ( addrIn ), status ( pverExistsHere ) {} + +pvExistReturn::~pvExistReturn () +{ +} + +const pvExistReturn & pvExistReturn::operator = ( pvExistReturnEnum rhs ) +{ + this->status = rhs; + this->address.clear (); + return * this; +} + +const pvExistReturn & pvExistReturn::operator = ( const caNetAddr & rhs ) +{ + this->status = pverExistsHere; + this->address = rhs; + return * this; +} + +pvExistReturnEnum pvExistReturn::getStatus () const +{ + return this->status; +} + +caNetAddr pvExistReturn::getAddr () const +{ + return this->address; +} + +bool pvExistReturn::addrIsValid () const +{ + return this->address.isValid (); +} diff --git a/src/cas/generic/st/README b/src/cas/generic/st/README new file mode 100644 index 000000000..f5851b1f6 --- /dev/null +++ b/src/cas/generic/st/README @@ -0,0 +1,5 @@ + +This directory contains files specific to the single-threaded +version of the CA server + +- diff --git a/src/cas/generic/st/caServerOS.cc b/src/cas/generic/st/caServerOS.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/cas/generic/st/casDGEvWakeup.h b/src/cas/generic/st/casDGEvWakeup.h new file mode 100644 index 000000000..75952bf32 --- /dev/null +++ b/src/cas/generic/st/casDGEvWakeup.h @@ -0,0 +1,33 @@ + + +/*************************************************************************\ +* 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 +// + +#ifndef casDGEvWakeuph +#define casDGEvWakeuph + +class casDGEvWakeup : public epicsTimerNotify { +public: + casDGEvWakeup (); + virtual ~casDGEvWakeup(); + void show ( unsigned level ) const; + void start ( class casDGIntfOS &osIn ); +private: + epicsTimer &timer; + class casDGIntfOS *pOS; + expireStatus expire( const epicsTime & currentTime ); + casDGEvWakeup ( const casDGEvWakeup & ); + casDGEvWakeup & operator = ( const casDGEvWakeup & ); +}; + +#endif // casDGEvWakeuph diff --git a/src/cas/generic/st/casDGIOWakeup.h b/src/cas/generic/st/casDGIOWakeup.h new file mode 100644 index 000000000..a6a2a9ca8 --- /dev/null +++ b/src/cas/generic/st/casDGIOWakeup.h @@ -0,0 +1,45 @@ + +/*************************************************************************\ +* 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 +// + +#ifndef casDGIOWakeuph +#define casDGIOWakeuph + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casDGIOWakeuph +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "epicsTimer.h" + +#ifdef epicsExportSharedSymbols_casDGIOWakeuph +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class casDGIOWakeup : public epicsTimerNotify { +public: + casDGIOWakeup (); + virtual ~casDGIOWakeup (); + void show ( unsigned level ) const; + void start ( class casDGIntfOS &osIn ); +private: + epicsTimer &timer; + class casDGIntfOS *pOS; + expireStatus expire( const epicsTime & currentTime ); + casDGIOWakeup ( const casDGIOWakeup & ); + casDGIOWakeup & operator = ( const casDGIOWakeup & ); +}; + +#endif // casDGIOWakeuph diff --git a/src/cas/generic/st/casDGIntfOS.cc b/src/cas/generic/st/casDGIntfOS.cc new file mode 100644 index 000000000..476808690 --- /dev/null +++ b/src/cas/generic/st/casDGIntfOS.cc @@ -0,0 +1,485 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// casDGIntfOS.cc +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#include "fdManager.h" +#include "errlog.h" + +#define epicsExportSharedFunc +#include "casDGIntfOS.h" + +// +// casDGReadReg +// +class casDGReadReg : public fdReg { +public: + casDGReadReg ( casDGIntfOS & osIn ) : + fdReg (osIn.getFD(), fdrRead), os (osIn) {} + ~casDGReadReg (); + void show (unsigned level) const; +private: + casDGIntfOS &os; + void callBack (); + casDGReadReg ( const casDGReadReg & ); + casDGReadReg & operator = ( const casDGReadReg & ); +}; + +// +// casDGBCastReadReg +// +class casDGBCastReadReg : public fdReg { +public: + casDGBCastReadReg (casDGIntfOS &osIn) : + fdReg (osIn.getBCastFD(), fdrRead), os (osIn) {} + ~casDGBCastReadReg (); + + void show (unsigned level) const; +private: + casDGIntfOS &os; + void callBack (); + casDGBCastReadReg ( const casDGBCastReadReg & ); + casDGBCastReadReg & operator = ( const casDGBCastReadReg & ); +}; + +// +// casDGWriteReg +// +class casDGWriteReg : public fdReg { +public: + casDGWriteReg (casDGIntfOS &osIn) : + fdReg (osIn.getFD(), fdrWrite), os (osIn) {} + ~casDGWriteReg (); + + void show (unsigned level) const; +private: + casDGIntfOS &os; + void callBack (); + casDGWriteReg ( const casDGWriteReg & ); + casDGWriteReg & operator = ( const casDGWriteReg & ); +}; + +// +// casDGIntfOS::casDGIntfOS() +// +casDGIntfOS::casDGIntfOS ( + caServerI & serverIn, clientBufMemoryManager & memMgrIn, + const caNetAddr & addr, bool autoBeaconAddr, + bool addConfigBeaconAddr ) : + casDGIntfIO ( serverIn, memMgrIn, addr, + autoBeaconAddr, addConfigBeaconAddr ), + pRdReg ( 0 ), + pBCastRdReg ( 0 ), + pWtReg ( 0 ) +{ + this->xSetNonBlocking(); + this->armRecv(); +} + +// +// casDGIntfOS::~casDGIntfOS() +// +casDGIntfOS::~casDGIntfOS() +{ + this->disarmSend(); + this->disarmRecv(); +} + +// +// casDGEvWakeup::casDGEvWakeup() +// +casDGEvWakeup::casDGEvWakeup () : + timer ( fileDescriptorManager.createTimer() ), pOS ( 0 ) +{ +} + +// +// casDGEvWakeup::~casDGEvWakeup() +// +casDGEvWakeup::~casDGEvWakeup() +{ + this->timer.destroy (); +} + +void casDGEvWakeup::start ( casDGIntfOS &os ) +{ + if ( this->pOS ) { + assert ( this->pOS == &os ); + } + else { + this->pOS = &os; + this->timer.start ( *this, 0.0 ); + } +} + +// +// casDGEvWakeup::show() +// +void casDGEvWakeup::show ( unsigned level ) const +{ + printf ( "casDGEvWakeup at %p {\n", + static_cast ( this ) ); + this->timer.show ( level ); + printf ("}\n"); +} + +// +// casDGEvWakeup::expire() +// +epicsTimerNotify::expireStatus casDGEvWakeup::expire ( const epicsTime & /* currentTime */ ) +{ + this->pOS->eventSysProcess (); + // We do not wait for any impartial, or complete, + // messages in the input queue to be processed + // because. + // A) IO postponement might be preventing the + // input queue processing from proceeding. + // B) Since both reads and events get processed + // before going back to select to find out if we + // can do a write then we naturally tend to + // combine get responses and subscription responses + // into one write. + this->pOS->armSend (); + this->pOS = 0; + return noRestart; +} + +// +// casDGIOWakeup::casDGIOWakeup() +// +casDGIOWakeup::casDGIOWakeup () : + timer ( fileDescriptorManager.createTimer() ), pOS ( 0 ) +{ +} + +// +// casDGIOWakeup::~casDGIOWakeup() +// +casDGIOWakeup::~casDGIOWakeup() +{ + this->timer.destroy (); +} + +// +// casDGIOWakeup::expire() +// +// Running this indirectly off of the timer queue +// guarantees that we will not call processDG() +// recursively +// +epicsTimerNotify :: expireStatus + casDGIOWakeup :: expire( const epicsTime & /* currentTime */ ) +{ + caStatus status = this->pOS->processDG (); + if ( status != S_cas_success && + status != S_cas_sendBlocked ) { + char pName[64u]; + this->pOS->hostName (pName, sizeof (pName)); + errPrintf (status, __FILE__, __LINE__, + "unexpected problem with UDP input from \"%s\"", pName); + } + this->pOS->armRecv (); + this->pOS->armSend (); + this->pOS = 0; + return noRestart; +} + +// +// casDGIOWakeup::show() +// +void casDGIOWakeup::start ( casDGIntfOS &os ) +{ + if ( this->pOS ) { + assert ( this->pOS == &os ); + } + else { + this->pOS = &os; + this->timer.start ( *this, 0.0 ); + } +} + +// +// casDGIOWakeup::show() +// +void casDGIOWakeup::show(unsigned level) const +{ + printf ( "casDGIOWakeup at %p {\n", + static_cast ( this ) ); + this->timer.show ( level ); + printf ( "}\n" ); +} + +// +// casDGIntfOS::armRecv () +// +void casDGIntfOS::armRecv() +{ + if ( ! this->inBufFull () ) { + if ( ! this->pRdReg ) { + this->pRdReg = new casDGReadReg ( *this ); + } + if ( this->validBCastFD() && this->pBCastRdReg == NULL ) { + this->pBCastRdReg = new casDGBCastReadReg ( *this ); + } + } +} + +// +// casDGIntfOS::disarmRecv() +// +void casDGIntfOS::disarmRecv() +{ + delete this->pRdReg; + this->pRdReg = 0; + delete this->pBCastRdReg; + this->pBCastRdReg = 0; +} + +// +// casDGIntfOS::armSend() +// +void casDGIntfOS::armSend() +{ + if ( this->outBufBytesPending () == 0u ) { + return; + } + + if ( ! this->pWtReg ) { + this->pWtReg = new casDGWriteReg ( *this ); + } +} + +// +// casDGIntfOS::disarmSend() +// +void casDGIntfOS::disarmSend () +{ + delete this->pWtReg; + this->pWtReg = 0; +} + +// +// casDGIntfOS::ioBlockedSignal() +// +void casDGIntfOS::ioBlockedSignal() +{ + this->ioWk.start ( *this ); +} + +// +// casDGIntfOS::eventSignal() +// +void casDGIntfOS::eventSignal() +{ + this->evWk.start ( *this ); +} + +// +// casDGIntfOS::show() +// +void casDGIntfOS::show(unsigned level) const +{ + printf ( "casDGIntfOS at %p\n", + static_cast ( this ) ); + if ( this->pRdReg ) { + this->pRdReg->show ( level ); + } + if ( this->pWtReg ) { + this->pWtReg->show ( level ); + } + if ( this->pBCastRdReg ) { + this->pBCastRdReg->show ( level ); + } + this->evWk.show (level); + this->ioWk.show (level); + this->casDGIntfIO::show (level); +} + +// +// casDGReadReg::callBack() +// +void casDGReadReg::callBack() +{ + this->os.recvCB ( inBufClient::fpNone ); +} + +// +// casDGReadReg::~casDGReadReg() +// +casDGReadReg::~casDGReadReg() +{ +} + +// +// casDGReadReg::show() +// +void casDGReadReg::show(unsigned level) const +{ + this->fdReg::show(level); + printf("casDGReadReg at %p\n", + static_cast ( this) ); +} + +// +// casDGBCastReadReg::callBack() +// +void casDGBCastReadReg::callBack() +{ + this->os.recvCB ( inBufClient::fpUseBroadcastInterface ); +} + +// +// casDGBCastReadReg::~casDGBCastReadReg() +// +casDGBCastReadReg::~casDGBCastReadReg() +{ +} + +// +// casDGReadReg::show() +// +void casDGBCastReadReg::show(unsigned level) const +{ + this->fdReg::show(level); + printf ( "casDGBCastReadReg at %p\n", + static_cast ( this ) ); +} + +// +// casDGWriteReg::~casDGWriteReg() +// +casDGWriteReg::~casDGWriteReg() +{ +} + +// +// casDGWriteReg::callBack() +// +void casDGWriteReg::callBack() +{ + casDGIntfOS * pDGIOS = & this->os; + pDGIOS->sendCB(); + // + // NO CODE HERE - sendCB deletes this object + // +} + +// +// casDGWriteReg::show() +// +void casDGWriteReg::show(unsigned level) const +{ + this->fdReg::show (level); + printf ( "casDGWriteReg: at %p\n", + static_cast ( this ) ); +} + +// +// casDGIntfOS::sendBlockSignal() +// +void casDGIntfOS::sendBlockSignal() +{ + this->armSend(); +} + +// +// casDGIntfOS::sendCB() +// +void casDGIntfOS::sendCB() +{ + // allows rearm to occur if required + this->disarmSend (); + + // + // attempt to flush the output buffer + // + outBufClient::flushCondition flushCond = this->flush (); + if ( flushCond == flushProgress ) { + // + // If we are unable to flush out all of the events + // in casDgEvWakeup::expire() because the + // client is slow then we must check again here when + // we _are_ able to write to see if additional events + // can be sent to the slow client. + // + this->eventSysProcess (); + + // + // If we were able to send something then we need + // to process the input queue in case we were send + // blocked. + // + caStatus status = this->processDG (); + if ( status != S_cas_success && + status != S_cas_sendBlocked ) { + char pName[64u]; + this->hostName (pName, sizeof (pName)); + errPrintf ( status, __FILE__, __LINE__, + "unexpected problem with UDP input from \"%s\"", pName); + } + } + +# if defined(DEBUG) + printf ("write attempted on %d result was %d\n", this->getFD(), flushCond ); + printf ("Recv backlog %u\n", this->inBuf::bytesPresent()); + printf ("Send backlog %u\n", this->outBuf::bytesPresent()); +# endif + + // + // this reenables receipt of incoming frames once + // the output has been flushed in case the receive + // side is blocked due to lack of buffer space + // + this->armRecv (); + + + // once we start sending we continue until done + this->armSend (); +} + +// +// casDGIntfOS::recvCB() +// +void casDGIntfOS :: recvCB ( inBufClient::fillParameter parm ) +{ + assert ( this->pRdReg ); + + // + // copy in new messages + // + this->inBufFill ( parm ); + caStatus status = this->processDG (); + if ( status != S_cas_success && + status != S_cas_sendBlocked ) { + char pName[64u]; + this->hostName (pName, sizeof (pName)); + errPrintf (status, __FILE__, __LINE__, + "unexpected problem with UDP input from \"%s\"", pName); + } + + this->armSend (); + + // + // If there isnt any space then temporarily + // stop calling this routine until problem is resolved + // either by: + // (1) sending or + // (2) a blocked IO op unblocks + // + // (casDGReadReg is _not_ a onceOnly fdReg - + // therefore an explicit delete is required here) + // + if ( this->inBufFull() ) { + this->disarmRecv(); // this deletes the casDGReadReg object + } +} + diff --git a/src/cas/generic/st/casDGIntfOS.h b/src/cas/generic/st/casDGIntfOS.h new file mode 100644 index 000000000..99bfc3505 --- /dev/null +++ b/src/cas/generic/st/casDGIntfOS.h @@ -0,0 +1,61 @@ + +/*************************************************************************\ +* 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 +// + +#ifndef casDGIntfOSh +#define casDGIntfOSh + +#include "casDGIntfIO.h" +#include "casDGIOWakeup.h" +#include "casDGEvWakeup.h" + +class casDGIntfOS : public casDGIntfIO { + friend class casDGReadReg; + friend class casDGBCastReadReg; + friend class casDGWriteReg; + friend class casDGEvWakeup; + friend class casDGIOWakeup; + friend class casStreamEvWakeup; +public: + casDGIntfOS ( caServerI &, clientBufMemoryManager &, + const caNetAddr & addr, bool autoBeaconAddr = true, + bool addConfigBeaconAddr = false); + virtual ~casDGIntfOS (); + virtual void show (unsigned level) const; +private: + casDGIOWakeup ioWk; + casDGEvWakeup evWk; + class casDGReadReg * pRdReg; + class casDGBCastReadReg * pBCastRdReg; // fix for solaris bug + class casDGWriteReg * pWtReg; + + void armRecv (); + void armSend (); + + void disarmRecv (); + void disarmSend (); + + void recvCB ( inBufClient::fillParameter parm ); + void sendCB (); + + void sendBlockSignal (); + + void ioBlockedSignal (); + + void eventSignal (); + + casDGIntfOS ( const casDGIntfOS & ); + casDGIntfOS & operator = ( const casDGIntfOS & ); +}; + +#endif // casDGIntfOSh diff --git a/src/cas/generic/st/casIntfOS.cc b/src/cas/generic/st/casIntfOS.cc new file mode 100644 index 000000000..05c52e08d --- /dev/null +++ b/src/cas/generic/st/casIntfOS.cc @@ -0,0 +1,76 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * casIntfOS.cc + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * + */ + +#include "fdManager.h" + +#define epicsExportSharedSymbols +#include "casIntfOS.h" + +class casServerReg : public fdReg { +public: + casServerReg (casIntfOS &osIn) : + fdReg (osIn.casIntfIO::getFD(), fdrRead), os (osIn) {} + ~casServerReg (); +private: + casIntfOS &os; + void callBack (); + casServerReg ( const casServerReg & ); + casServerReg & operator = ( const casServerReg & ); +}; + +casIntfOS::casIntfOS ( caServerI & casIn, clientBufMemoryManager & memMgrIn, + const caNetAddr & addrIn, bool autoBeaconAddr, bool addConfigBeaconAddr ) : + casIntfIO ( addrIn ), + casDGIntfOS ( casIn, memMgrIn, addrIn, autoBeaconAddr, + addConfigBeaconAddr ), + cas ( casIn ) +{ + this->setNonBlocking(); + + this->pRdReg = new casServerReg ( *this ); +} + +casIntfOS::~casIntfOS() +{ + delete this->pRdReg; +} + +void casServerReg::callBack() +{ + assert ( this->os.pRdReg ); + this->os.cas.connectCB ( this->os ); +} + +casServerReg::~casServerReg() +{ +} + +void casIntfOS::show ( unsigned level ) const +{ + printf ( "casIntfOS at %p\n", + static_cast < const void * > ( this ) ); + this->casDGIntfOS::show ( level ); +} + +caNetAddr casIntfOS::serverAddress () const +{ + return this->casIntfIO::serverAddress(); +} + + + diff --git a/src/cas/generic/st/casIntfOS.h b/src/cas/generic/st/casIntfOS.h new file mode 100644 index 000000000..4364c01a4 --- /dev/null +++ b/src/cas/generic/st/casIntfOS.h @@ -0,0 +1,46 @@ + +/*************************************************************************\ +* 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 +// +// casOSD.h - Channel Access Server OS dependent wrapper +// +// +// + +#ifndef casIntfOSh +#define casIntfOSh + +#include "casIntfIO.h" +#include "casDGIntfOS.h" + +// +// casIntfOS +// +class casIntfOS : public casIntfIO, public tsDLNode < casIntfOS >, + public casDGIntfOS +{ + friend class casServerReg; +public: + casIntfOS ( caServerI &, clientBufMemoryManager &, const caNetAddr &, + bool autoBeaconAddr = true, bool addConfigBeaconAddr = false ); + virtual ~casIntfOS(); + void show ( unsigned level ) const; + caNetAddr serverAddress () const; +private: + caServerI & cas; + class casServerReg * pRdReg; + + casIntfOS ( const casIntfOS & ); + casIntfOS & operator = ( const casIntfOS & ); +}; + +#endif // casIntfOSh diff --git a/src/cas/generic/st/casOSD.h b/src/cas/generic/st/casOSD.h new file mode 100644 index 000000000..7a23b9acd --- /dev/null +++ b/src/cas/generic/st/casOSD.h @@ -0,0 +1,52 @@ +/*************************************************************************\ +* 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 +// +// casOSD.h - Channel Access Server OS dependent wrapper +// +// +// + +#ifndef includeCASOSDH +#define includeCASOSDH + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_includeCASOSDH +# undef epicsExportSharedSymbols +#endif +#include "fdManager.h" +#ifdef epicsExportSharedSymbols_includeCASOSDH +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class caServerI; + +class casServerReg; + +class casDGReadReg; +class casDGBCastReadReg; +class casDGWriteReg; + + + + + + +class casStreamWriteReg; +class casStreamReadReg; + + + + +// no additions below this line +#endif // includeCASOSDH + diff --git a/src/cas/generic/st/casStreamOS.cc b/src/cas/generic/st/casStreamOS.cc new file mode 100644 index 000000000..9f9a910b8 --- /dev/null +++ b/src/cas/generic/st/casStreamOS.cc @@ -0,0 +1,633 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// +// casStreamOS.cc +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// +// TO DO: +// o armRecv() and armSend() should return bad status when +// there isnt enough memory +// + +#include "fdManager.h" +#include "errlog.h" + +#define epicsExportSharedFunc +#include "casStreamOS.h" + +#if 0 +#define DEBUG +#endif + +// +// printStatus () +// +#if defined(DEBUG) +void casStreamOS :: printStatus ( const char * pCtx ) const +{ + static epicsTime beginTime = epicsTime :: getCurrent (); + epicsTime current = epicsTime :: getCurrent (); + printf ( + "%03.3f, " + "Sock %d, %s, " + "RecvBuf %u, " + "SendBuf %u\n", + current - beginTime, + this->getFD(), + pCtx, + this->inBufBytesPending (), + this->outBufBytesPending () ); + fflush ( stdout ); +} +#else +inline void casStreamOS :: printStatus ( const char * ) const {} +#endif + +// +// casStreamReadReg +// +class casStreamReadReg : public fdReg { +public: + inline casStreamReadReg (casStreamOS &osIn); + inline ~casStreamReadReg (); + void show (unsigned level) const; +private: + casStreamOS &os; + void callBack (); + casStreamReadReg ( const casStreamReadReg & ); + casStreamReadReg & operator = ( const casStreamReadReg & ); +}; + +// +// casStreamReadReg::casStreamReadReg() +// +inline casStreamReadReg::casStreamReadReg (casStreamOS &osIn) : + fdReg (osIn.getFD(), fdrRead), os (osIn) +{ + this->os.printStatus ( "read schedualed" ); +} + +// +// casStreamReadReg::~casStreamReadReg +// +inline casStreamReadReg::~casStreamReadReg () +{ + this->os.printStatus ( "read unschedualed" ); +} + +// +// casStreamWriteReg +// +class casStreamWriteReg : public fdReg { +public: + inline casStreamWriteReg (casStreamOS &osIn); + inline ~casStreamWriteReg (); + + void show (unsigned level) const; +private: + casStreamOS &os; + void callBack (); + casStreamWriteReg ( const casStreamWriteReg & ); + casStreamWriteReg & operator = ( const casStreamWriteReg & ); +}; + +// +// casStreamWriteReg::casStreamWriteReg() +// +inline casStreamWriteReg::casStreamWriteReg (casStreamOS &osIn) : + fdReg (osIn.getFD(), fdrWrite, true), os (osIn) +{ + this->os.printStatus ( "write schedualed" ); +} + +// +// casStreamWriteReg::~casStreamWriteReg () +// +inline casStreamWriteReg::~casStreamWriteReg () +{ + this->os.printStatus ( "write unschedualed" ); +} + +// +// casStreamEvWakeup() +// +casStreamEvWakeup::casStreamEvWakeup ( casStreamOS & osIn ) : + timer ( fileDescriptorManager.createTimer() ), os ( osIn ) +{ +} + +// +// casStreamEvWakeup::~casStreamEvWakeup() +// +casStreamEvWakeup::~casStreamEvWakeup() +{ + this->timer.destroy (); +} + +// +// casStreamEvWakeup::show() +// +void casStreamEvWakeup::show(unsigned level) const +{ + printf ( "casStreamEvWakeup at %p {\n", + static_cast ( this ) ); + this->timer.show ( level ); + printf ( "}\n" ); +} + +// +// casStreamEvWakeup::expire() +// +epicsTimerNotify::expireStatus casStreamEvWakeup:: + expire ( const epicsTime & /* currentTime */ ) +{ + this->os.printStatus ( "casStreamEvWakeup tmr expire" ); + casProcCond pc = os.eventSysProcess (); + if ( pc == casProcOk ) { + // We do not wait for any impartial, or complete, + // messages in the input queue to be processed + // because. + // A) IO postponement might be preventing the + // input queue processing from proceeding. + // B) We dont want to interrupt subscription + // updates while waiting for very large arrays + // to be read in a packet at a time. + // C) Since both reads and events get processed + // before going back to select to find out if we + // can do a write then we naturally tend to + // combine get responses and subscription responses + // into one write. + // D) Its probably questionable to hold up event + // traffic (introduce latency) because a partial + // message is pending in the input queue. + this->os.armSend (); + } + else { + // + // ok to delete the client here + // because casStreamEvWakeup::expire() + // is called by the timer queue system + // and therefore we are not being + // called from a client member function + // higher up on the stack + // + delete & this->os; + + // + // must not touch the "this" pointer + // from this point on however + // + } + return noRestart; +} + +// +// casStreamEvWakeup::start() +// +// care is needed here because this is called +// asynchronously by postEvent +// +// there is some overhead here but care is taken +// in the caller of this routine to call this +// only when its the 2nd event on the queue +// +void casStreamEvWakeup::start( casStreamOS & ) +{ + this->os.printStatus ( "casStreamEvWakeup tmr start" ); + this->timer.start ( *this, 0.0 ); +} + +// +// casStreamIOWakeup::casStreamIOWakeup() +// +casStreamIOWakeup::casStreamIOWakeup () : + timer ( fileDescriptorManager.createTimer() ), pOS ( 0 ) +{ +} + +// +// casStreamIOWakeup::~casStreamIOWakeup() +// +casStreamIOWakeup::~casStreamIOWakeup() +{ + this->timer.destroy (); +} + +// +// casStreamIOWakeup::show() +// +void casStreamIOWakeup::show ( unsigned level ) const +{ + printf ( "casStreamIOWakeup at %p {\n", + static_cast ( this ) ); + this->timer.show ( level ); + printf ( "}\n" ); +} + +// +// casStreamIOWakeup::expire() +// +// This is called whenever asynchronous IO completes +// +// Running this indirectly off of the timer queue +// guarantees that we will not call processMsg() +// recursively +// +epicsTimerNotify::expireStatus casStreamIOWakeup :: + expire ( const epicsTime & /* currentTime */ ) +{ + assert ( this->pOS ); + this->pOS->printStatus ( "casStreamIOWakeup tmr expire" ); + casStreamOS & tmpOS = *this->pOS; + this->pOS = 0; + caStatus status = tmpOS.processMsg (); + if ( status == S_cas_success ) { + tmpOS.armRecv (); + if ( tmpOS._sendNeeded () ) { + tmpOS.armSend (); + } + } + else if ( status == S_cas_sendBlocked ) { + tmpOS.armSend (); + // always activate receives if space is available + // in the in buf + tmpOS.armRecv (); + } + else if ( status == S_casApp_postponeAsyncIO ) { + // we should be back on the IO blocked list + // if S_casApp_postponeAsyncIO was returned + // so this function will be called again when + // another asynchronous request completes + tmpOS.armSend (); + // always activate receives if space is available + // in the in buf + tmpOS.armRecv (); + } + else { + errMessage ( status, + "- unexpected problem with client's input - forcing disconnect"); + tmpOS.getCAS().destroyClient ( tmpOS ); + // + // must _not_ touch "tmpOS" ref + // after the destroy + // + return noRestart; + } + return noRestart; +} + +// +// casStreamIOWakeup::start() +// +void casStreamIOWakeup::start ( casStreamOS &os ) +{ + if ( this->pOS ) { + assert ( this->pOS == &os ); + } + else { + this->pOS = &os; + this->timer.start ( *this, 0.0 ); + } + this->pOS->printStatus ( "casStreamIOWakeup tmr start" ); +} + +// +// casStreamOS::armRecv () +// +inline void casStreamOS::armRecv() +{ + if ( ! this->pRdReg ) { + if ( ! this->inBufFull() ) { + this->pRdReg = new casStreamReadReg ( *this ); + } + } +} + +// +// casStreamOS::disarmRecv () +// +inline void casStreamOS::disarmRecv () +{ + delete this->pRdReg; + this->pRdReg = 0; +} + +// +// casStreamOS::armSend() +// +inline void casStreamOS::armSend() +{ + if ( this->outBufBytesPending() == 0u ) { + return; + } + + if ( ! this->pWtReg ) { + this->pWtReg = new casStreamWriteReg(*this); + } +} + +// +// casStreamOS::disarmSend() +// +inline void casStreamOS::disarmSend () +{ + delete this->pWtReg; + this->pWtReg = 0; +} + +// +// casStreamOS::ioBlockedSignal() +// (called by main thread when lock is applied) +// +void casStreamOS::ioBlockedSignal() +{ + this->ioWk.start ( *this ); +} + +// +// casStreamOS::eventSignal() +// (called by any thread asynchronously +// when an event is posted) +// +void casStreamOS::eventSignal() +{ + this->evWk.start ( *this ); +} + +// +// casStreamOS::casStreamOS() +// +casStreamOS::casStreamOS ( + caServerI & cas, clientBufMemoryManager & bufMgrIn, + const ioArgsToNewStreamIO & ioArgs ) : + casStreamIO ( cas, bufMgrIn, ioArgs ), + evWk ( *this ), + pWtReg ( 0 ), + pRdReg ( 0 ), + _sendBacklogThresh ( osSendBufferSize () / 2u ) +{ + if ( _sendBacklogThresh < MAX_TCP / 2 ) { + _sendBacklogThresh = MAX_TCP / 2; + } + this->xSetNonBlocking (); + this->armRecv (); +} + +// +// casStreamOS::~casStreamOS() +// +casStreamOS::~casStreamOS() +{ + // + // attempt to flush out any remaining messages + // + this->flush (); + + this->disarmSend (); + this->disarmRecv (); +} + +// +// casStreamOS::show() +// +void casStreamOS::show ( unsigned level ) const +{ + this->casStrmClient::show ( level ); + printf ( "casStreamOS at %p\n", + static_cast ( this ) ); + if ( this->pWtReg ) { + this->pWtReg->show ( level ); + } + if ( this->pRdReg ) { + this->pRdReg->show ( level ); + } + this->evWk.show ( level ); + this->ioWk.show ( level ); +} + +// +// casStreamReadReg::show() +// +void casStreamReadReg::show ( unsigned level ) const +{ + this->fdReg::show ( level ); + printf ( "casStreamReadReg at %p\n", + static_cast ( this ) ); +} + +// +// casStreamReadReg::callBack () +// +void casStreamReadReg::callBack () +{ + this->os.recvCB (); + // + // NO CODE HERE + // (casStreamOS::recvCB() may up indirectly deleting this object) + // +} + +// +// casStreamOS::recvCB() +// +void casStreamOS :: recvCB () +{ + assert ( this->pRdReg ); + + printStatus ( "receiving" ); + + // + // copy in new messages + // + inBufClient::fillCondition fillCond = this->inBufFill (); + if ( fillCond == casFillDisconnect ) { + this->getCAS().destroyClient ( *this ); + // + // must _not_ touch "this" pointer + // after the destroy + // + return; + } + else if ( fillCond == casFillNone ) { + if ( this->inBufFull() ) { + this->disarmRecv (); + } + } + else { + printStatus ( "recv CB req proc" ); + caStatus status = this->processMsg (); + if ( status == S_cas_success ) { + this->armRecv (); + if ( _sendNeeded () ) { + this->armSend (); + } + } + else if ( status == S_cas_sendBlocked ) { + this->armSend (); + } + else if ( status == S_casApp_postponeAsyncIO ) { + this->armSend (); + } + else { + errMessage ( status, + "- unexpected problem with client's input - forcing disconnect"); + this->getCAS().destroyClient ( *this ); + // + // must _not_ touch "this" pointer + // after the destroy + // + return; + } + } +} + +// +// casStreamOS :: sendBlockSignal() +// +void casStreamOS :: sendBlockSignal () +{ + this->armSend (); +} + +// +// casStreamWriteReg::show() +// +void casStreamWriteReg::show ( unsigned level ) const +{ + this->fdReg::show ( level ); + printf ( "casStreamWriteReg at %p\n", + static_cast ( this ) ); +} + +// +// casStreamWriteReg::callBack () +// +void casStreamWriteReg::callBack() +{ + casStreamOS * pSOS = & this->os; + pSOS->sendCB (); + // + // NO CODE HERE - sendCB deletes this object + // +} + +// +// casStreamOS::sendCB () +// +void casStreamOS::sendCB () +{ + // we know that the fdManager will destroy the write + // registration after this function returns, and that + // it is robust in situations where the callback + // deletes its fdReg derived object so delete it now, + // because we can now reschedule a send as needed + // + this->disarmSend (); + + printStatus ( "writing" ); + + // + // attempt to flush the output buffer + // + outBufClient::flushCondition flushCond = this->flush (); + if ( flushCond == outBufClient::flushDisconnect ) { + // + // ok to delete the client here + // because casStreamWriteReg::callBack() + // is called by the fdManager system + // and therefore we are not being + // called from a client member function + // higher up on the stack + // + this->getCAS().destroyClient ( *this ); + // + // must _not_ touch "this" pointer + // after the destroy + // + return; + } + + // + // If we are unable to flush out all of the events + // in casStreamEvWakeup::expire() because the + // client is slow then we must check again here when + // we _are_ able to write to see if additional events + // can be sent to the slow client. + // + casProcCond pc = this->eventSysProcess (); + if ( pc != casProcOk ) { + // + // ok to delete the client here + // because casStreamWriteReg::callBack() + // is called by the fdManager system + // and therefore we are not being + // called from a client member function + // higher up on the stack + // + this->getCAS().destroyClient ( *this ); + // + // must _not_ touch "this" pointer + // after the destroy + // + return; + } + + printStatus ( ppFlushCondText [ flushCond ] ); + + // + // If we were able to send something then we need + // to process the input queue in case we were send + // blocked. + // + + bufSizeT inBufBytesPend = this->inBufBytesPending (); + if ( flushCond == flushProgress && inBufBytesPend ) { + printStatus ( "send CB req proc" ); + caStatus status = this->processMsg (); + if ( status == S_cas_success ) { + this->armRecv (); + } + else if ( status == S_cas_sendBlocked + || status == S_casApp_postponeAsyncIO ) { + bufSizeT inBufBytesPendNew = this->inBufBytesPending (); + if ( inBufBytesPendNew < inBufBytesPend ) { + this->armRecv (); + } + } + else { + errMessage ( status, + "- unexpected problem with client's input - forcing disconnect"); + this->getCAS().destroyClient ( *this ); + // + // must _not_ touch "this" pointer + // after the destroy + // + return; + } + } + + // Once a send starts we will keep going with it until + // it flushes all of the way out. Its important to + // perform this step only after processMsg so that we + // flush out any send blocks detected by processMsg. + this->armSend (); +} + +// +// casStreamOS :: sendNeeded () +// +bool casStreamOS :: + _sendNeeded () const +{ + bool sn = this->outBufBytesPending() >= this->_sendBacklogThresh; + bufSizeT inBytesPending = this->inBufBytesPending (); + return sn || ( inBytesPending == 0u ); +} + + diff --git a/src/cas/generic/st/casStreamOS.h b/src/cas/generic/st/casStreamOS.h new file mode 100644 index 000000000..3b39b4953 --- /dev/null +++ b/src/cas/generic/st/casStreamOS.h @@ -0,0 +1,94 @@ + +/*************************************************************************\ +* 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 +// + +#ifndef casStreamOSh +#define casStreamOSh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casStreamOSh +# undef epicsExportSharedSymbols +#endif + +#undef epicsAssertAuthor +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#include "epicsAssert.h" +#include "epicsTimer.h" + +#ifdef epicsExportSharedSymbols_casStreamOSh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "casStreamIO.h" + +class casStreamIOWakeup : public epicsTimerNotify { +public: + casStreamIOWakeup (); + virtual ~casStreamIOWakeup (); + void show ( unsigned level ) const; + void start ( class casStreamOS & osIn ); +private: + epicsTimer & timer; + casStreamOS * pOS; + expireStatus expire ( const epicsTime & currentTime ); + casStreamIOWakeup ( const casStreamIOWakeup & ); + casStreamIOWakeup & operator = ( const casStreamIOWakeup & ); +}; + +class casStreamEvWakeup : public epicsTimerNotify { +public: + casStreamEvWakeup ( casStreamOS & ); + virtual ~casStreamEvWakeup (); + void show ( unsigned level ) const; + void start ( class casStreamOS & osIn ); +private: + epicsTimer & timer; + casStreamOS & os; + expireStatus expire ( const epicsTime & currentTime ); + casStreamEvWakeup ( const casStreamEvWakeup & ); + casStreamEvWakeup & operator = ( const casStreamEvWakeup & ); +}; + +class casStreamOS : public casStreamIO { +public: + casStreamOS ( caServerI &, clientBufMemoryManager &, + const ioArgsToNewStreamIO & ); + ~casStreamOS (); + void show ( unsigned level ) const; + void printStatus ( const char * pCtx ) const; +private: + casStreamEvWakeup evWk; + casStreamIOWakeup ioWk; + class casStreamWriteReg * pWtReg; + class casStreamReadReg * pRdReg; + bufSizeT _sendBacklogThresh; + void armSend (); + void armRecv (); + void disarmSend(); + void disarmRecv(); + void recvCB (); + void sendCB (); + void sendBlockSignal (); + void ioBlockedSignal (); + void eventSignal (); + bool _sendNeeded () const; + casStreamOS ( const casStreamOS & ); + casStreamOS & operator = ( const casStreamOS & ); + friend class casStreamWriteReg; + friend class casStreamReadReg; + friend class casStreamIOWakeup; + friend class casStreamEvWakeup; +}; + +#endif // casStreamOSh diff --git a/src/cas/generic/st/ioBlocked.cc b/src/cas/generic/st/ioBlocked.cc new file mode 100644 index 000000000..d355d4ade --- /dev/null +++ b/src/cas/generic/st/ioBlocked.cc @@ -0,0 +1,113 @@ +/*************************************************************************\ +* 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 +// +// IO Blocked list class +// (for single threaded version of the server) +// + +#include + +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#include "epicsAssert.h" + +#define epicsExportSharedSymbols +#include "ioBlocked.h" + +// +// ioBlocked::ioBlocked () +// +ioBlocked::ioBlocked () : + pList(NULL) +{ +} + +// +// ioBlocked::~ioBlocked () +// +ioBlocked::~ioBlocked () +{ + if (this->pList) { + this->pList->remove (*this); + this->pList = NULL; + } +} + +// +// ioBlocked::ioBlockedSignal () +// +void ioBlocked::ioBlockedSignal () +{ + // + // this must _not_ be pure virtual because + // there are situations where this is called + // inbetween the derived class's and this base + // class's destructors, and therefore a + // NOOP is required + // +} + +// +// ioBlockedList::ioBlockedList () +// +ioBlockedList::ioBlockedList () +{ +} + +// +// ioBlockedList::~ioBlockedList () +// +ioBlockedList::~ioBlockedList () +{ + for ( ioBlocked * pB = this->get (); pB; pB = this->get () ) { + pB->pList = NULL; + } +} + +// +// ioBlockedList::signal () +// +// works from a temporary list to avoid problems +// where the virtual function adds items to the +// list +// +void ioBlockedList::signal () +{ + tsDLList tmp; + + // + // move all of the items onto tmp + // + tmp.add(*this); + + for ( ioBlocked * pB = tmp.get (); pB; pB = tmp.get () ) { + pB->pList = NULL; + pB->ioBlockedSignal (); + } +} + +// +// ioBlockedList::addItemToIOBLockedList () +// +void ioBlockedList::addItemToIOBLockedList (ioBlocked &item) +{ + if (item.pList==NULL) { + this->add (item); + item.pList = this; + } + else { + assert (item.pList == this); + } +} + + diff --git a/src/cas/generic/st/osiMutexCAS.h b/src/cas/generic/st/osiMutexCAS.h new file mode 100644 index 000000000..5bcd2f335 --- /dev/null +++ b/src/cas/generic/st/osiMutexCAS.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// +// single threaded code NOOPs the mutex class +// +#include "osiMutexNOOP.h" + diff --git a/src/cas/generic/templateInstances.cpp b/src/cas/generic/templateInstances.cpp new file mode 100644 index 000000000..e1df222dc --- /dev/null +++ b/src/cas/generic/templateInstances.cpp @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "ipIgnoreEntry.h" +#include "casChannelI.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class resTable < ipIgnoreEntry, ipIgnoreEntry >; +template class resTable < casChannelI, chronIntId >; +template class resTable < casEventMaskEntry, stringId >; +template class chronIntIdResTable < casChannelI >; +template class tsFreeList < casMonEvent, 1024, epicsMutexNOOP >; +template class tsFreeList < casMonitor, 1024, epicsMutex >; + + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif diff --git a/src/cas/io/bsdSocket/README b/src/cas/io/bsdSocket/README new file mode 100644 index 000000000..2c2147c18 --- /dev/null +++ b/src/cas/io/bsdSocket/README @@ -0,0 +1,6 @@ + + +this directory contains the bsd socket dependent source for +the EPICS ca server + + diff --git a/src/cas/io/bsdSocket/caServerIO.cc b/src/cas/io/bsdSocket/caServerIO.cc new file mode 100644 index 000000000..214759143 --- /dev/null +++ b/src/cas/io/bsdSocket/caServerIO.cc @@ -0,0 +1,198 @@ +/*************************************************************************\ +* 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 is 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 +// +// verify connection state prior to doing anything in this file +// +// + +#include + +#include "epicsSignal.h" +#include "envDefs.h" +#include "caProto.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "caServerIO.h" + +static char * getToken ( const char **ppString, char *pBuf, unsigned bufSIze ); + +int caServerIO::staticInitialized; + +// +// caServerIO::caServerIO() +// +caServerIO::caServerIO () +{ + if ( ! osiSockAttach () ) { + throw S_cas_internal; + } + + caServerIO::staticInit (); +} + +// +// caServerIO::locateInterfaces() +// +void caServerIO::locateInterfaces () +{ + char buf[64u]; + const char *pStr; + char *pToken; + caStatus stat; + unsigned short port; + struct sockaddr_in saddr; + bool autoBeaconAddr; + + // + // first try for the server's private port number env var. + // If this available use the CA server port number (used by + // clients to find the server). If this also isnt available + // then use a hard coded default - CA_SERVER_PORT. + // + if ( envGetConfigParamPtr ( & EPICS_CAS_SERVER_PORT ) ) { + port = envGetInetPortConfigParam ( + &EPICS_CAS_SERVER_PORT, + static_cast ( CA_SERVER_PORT ) ); + } + else { + port = envGetInetPortConfigParam ( + & EPICS_CA_SERVER_PORT, + static_cast ( CA_SERVER_PORT ) ); + } + + memset ((char *)&saddr,0,sizeof(saddr)); + + pStr = envGetConfigParam ( &EPICS_CAS_AUTO_BEACON_ADDR_LIST, sizeof(buf), buf ); + if ( ! pStr ) { + pStr = envGetConfigParam ( &EPICS_CA_AUTO_ADDR_LIST, sizeof(buf), buf ); + } + if (pStr) { + if (strstr(pStr,"no")||strstr(pStr,"NO")) { + autoBeaconAddr = false; + } + else if (strstr(pStr,"yes")||strstr(pStr,"YES")) { + autoBeaconAddr = true; + } + else { + fprintf(stderr, + "CAS: EPICS_CA(S)_AUTO_ADDR_LIST = \"%s\"? Assuming \"YES\"\n", pStr); + autoBeaconAddr = true; + } + } + else { + autoBeaconAddr = true; + } + + // + // bind to the the interfaces specified - otherwise wildcard + // with INADDR_ANY and allow clients to attach from any interface + // + pStr = envGetConfigParamPtr ( & EPICS_CAS_INTF_ADDR_LIST ); + if (pStr) { + bool configAddrOnceFlag = true; + stat = S_cas_noInterface; + while ( (pToken = getToken ( & pStr, buf, sizeof ( buf ) ) ) ) { + int status; + + status = aToIPAddr (pToken, port, &saddr); + if (status) { + errlogPrintf( + "%s: Parsing '%s'\n", + __FILE__, + EPICS_CAS_INTF_ADDR_LIST.name); + errlogPrintf( + "\tBad internet address or host name: '%s'\n", + pToken); + continue; + } + stat = this->attachInterface (caNetAddr(saddr), autoBeaconAddr, configAddrOnceFlag); + if (stat) { + errMessage(stat, "unable to attach explicit interface"); + break; + } + configAddrOnceFlag = false; + } + } + else { + saddr.sin_family = AF_INET; + saddr.sin_port = ntohs (port); + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + stat = this->attachInterface (caNetAddr(saddr), autoBeaconAddr, true); + if (stat) { + errMessage(stat, "unable to attach any interface"); + } + } +} + +// +// caServerIO::~caServerIO() +// +caServerIO::~caServerIO() +{ + osiSockRelease(); +} + +// +// caServerIO::staticInit() +// +inline void caServerIO::staticInit() +{ + if ( caServerIO::staticInitialized ) { + return; + } + + epicsSignalInstallSigPipeIgnore (); + + caServerIO::staticInitialized = true; +} + +// +// caServerIO::show() +// +void caServerIO::show (unsigned /* level */) const +{ + printf ( "caServerIO at %p\n", + static_cast ( this ) ); +} + +// +// getToken() +// +static char *getToken(const char **ppString, // X aCC 361 + char *pBuf, unsigned bufSIze) +{ + const char *pToken; + unsigned i; + + pToken = *ppString; + while(isspace(*pToken)&&*pToken){ + pToken++; + } + + for (i=0u; i ( ellFirst ( pList ) ); + while ( pNode ) { + if ( pNode->addr.sa.sa_family == AF_INET ) { + pNode->addr.ia.sin_port = htons (port); + } + pNode = reinterpret_cast < osiSockAddrNode * > ( ellNext ( &pNode->node ) ); + } +} + + +casDGIntfIO::casDGIntfIO ( caServerI & serverIn, clientBufMemoryManager & memMgr, + const caNetAddr & addr, bool autoBeaconAddr, bool addConfigBeaconAddr ) : + casDGClient ( serverIn, memMgr ) +{ + ELLLIST BCastAddrList; + osiSockAddr serverAddr; + osiSockAddr serverBCastAddr; + unsigned short beaconPort; + int status; + + ellInit ( &BCastAddrList ); + ellInit ( &this->beaconAddrList ); + + if ( ! osiSockAttach () ) { + throw S_cas_internal; + } + + this->sock = casDGIntfIO::makeSockDG(); + if (this->sock==INVALID_SOCKET) { + throw S_cas_internal; + } + + this->beaconSock = casDGIntfIO::makeSockDG(); + if (this->beaconSock==INVALID_SOCKET) { + epicsSocketDestroy (this->sock); + throw S_cas_internal; + } + + { + // this connect is to supress a warning message on Linux + // when we shutdown the read side of the socket. If it + // fails (and it will on old ip kernels) we just ignore + // the failure. + osiSockAddr sockAddr; + sockAddr.ia.sin_family = AF_UNSPEC; + sockAddr.ia.sin_port = htons ( 0 ); + sockAddr.ia.sin_addr.s_addr = htonl ( 0 ); + connect ( this->beaconSock, + & sockAddr.sa, sizeof ( sockAddr.sa ) ); + shutdown ( this->beaconSock, SHUT_RD ); + } + + // + // Fetch port configuration from EPICS environment variables + // + if (envGetConfigParamPtr(&EPICS_CAS_SERVER_PORT)) { + this->dgPort = envGetInetPortConfigParam (&EPICS_CAS_SERVER_PORT, + static_cast (CA_SERVER_PORT)); + } + else { + this->dgPort = envGetInetPortConfigParam (&EPICS_CA_SERVER_PORT, + static_cast (CA_SERVER_PORT)); + } + if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) { + beaconPort = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT, + static_cast (CA_REPEATER_PORT)); + } + else { + beaconPort = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, + static_cast (CA_REPEATER_PORT)); + } + + // + // set up the primary address of the server + // + serverAddr.ia = addr; + serverAddr.ia.sin_port = htons (this->dgPort); + + // + // discover beacon addresses associated with this interface + // + { + osiSockAddrNode *pAddr; + ELLLIST tmpList; + + ellInit ( &tmpList ); + osiSockDiscoverBroadcastAddresses (&tmpList, + this->sock, &serverAddr); // match addr + forcePort ( &tmpList, beaconPort ); + removeDuplicateAddresses ( &BCastAddrList, &tmpList, 1 ); + if (ellCount(&BCastAddrList)<1) { + errMessage (S_cas_noInterface, "- unable to continue"); + epicsSocketDestroy (this->sock); + throw S_cas_noInterface; + } + pAddr = reinterpret_cast < osiSockAddrNode * > ( ellFirst ( &BCastAddrList ) ); + serverBCastAddr.ia = pAddr->addr.ia; + serverBCastAddr.ia.sin_port = htons (this->dgPort); + + if ( ! autoBeaconAddr ) { + // avoid use of ellFree because problems on windows occur if the + // free is in a different DLL than the malloc + while ( ELLNODE * pnode = ellGet ( & BCastAddrList ) ) { + free ( pnode ); + } + } + } + + status = bind ( this->sock, &serverAddr.sa, sizeof (serverAddr) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + char buf[64]; + ipAddrToA ( &serverAddr.ia, buf, sizeof ( buf ) ); + errPrintf ( S_cas_bindFail, __FILE__, __LINE__, + "- bind UDP IP addr=%s failed because %s", buf, sockErrBuf ); + epicsSocketDestroy (this->sock); + throw S_cas_bindFail; + } + + if ( addConfigBeaconAddr ) { + + // + // by default use EPICS_CA_ADDR_LIST for the + // beacon address list + // + const ENV_PARAM *pParam; + + if ( envGetConfigParamPtr ( & EPICS_CAS_INTF_ADDR_LIST ) || + envGetConfigParamPtr ( & EPICS_CAS_BEACON_ADDR_LIST ) ) { + pParam = & EPICS_CAS_BEACON_ADDR_LIST; + } + else { + pParam = & EPICS_CA_ADDR_LIST; + } + + // + // add in the configured addresses + // + addAddrToChannelAccessAddressList ( + & BCastAddrList, pParam, beaconPort, pParam == & EPICS_CA_ADDR_LIST ); + } + + removeDuplicateAddresses ( & this->beaconAddrList, & BCastAddrList, 0 ); + + { + ELLLIST parsed, filtered; + ellInit ( & parsed ); + ellInit ( & filtered ); + // we dont care what port they are coming from + addAddrToChannelAccessAddressList ( & parsed, & EPICS_CAS_IGNORE_ADDR_LIST, 0, false ); + removeDuplicateAddresses ( & filtered, & parsed, true ); + + while ( ELLNODE * pRawNode = ellGet ( & filtered ) ) { + STATIC_ASSERT ( offsetof (osiSockAddrNode, node) == 0 ); + osiSockAddrNode * pNode = reinterpret_cast < osiSockAddrNode * > ( pRawNode ); + if ( pNode->addr.sa.sa_family == AF_INET ) { + ipIgnoreEntry * pIPI = new ( this->ipIgnoreEntryFreeList ) + ipIgnoreEntry ( pNode->addr.ia.sin_addr.s_addr ); + this->ignoreTable.add ( * pIPI ); + } + else { + errlogPrintf ( + "Expected IP V4 address - EPICS_CAS_IGNORE_ADDR_LIST entry ignored\n" ); + } + free ( pNode ); + } + } + + // + // Solaris specific: + // If they are binding to a particular interface then + // we will also need to bind to the broadcast address + // for that interface (if it has one). This allows + // broadcast packets to be received, but we must reply + // through the "normal" UDP binding or the client will + // appear to receive replies from the broadcast address. + // Since it should never be valid to fill in the UDP + // source address as the broadcast address, then we must + // conclude that the Solaris implementation is at least + // partially broken. + // + // WIN32 specific: + // On windows this appears to only create problems because + // they allow broadcast to be received when + // binding to a particular interface's IP address, and + // always fill in this interface's address as the reply + // address. + // +#if !defined(_WIN32) + if (serverAddr.ia.sin_addr.s_addr != htonl(INADDR_ANY)) { + + this->bcastRecvSock = casDGIntfIO::makeSockDG (); + if (this->bcastRecvSock==INVALID_SOCKET) { + epicsSocketDestroy (this->sock); + throw S_cas_internal; + } + + status = bind ( this->bcastRecvSock, &serverBCastAddr.sa, + sizeof (serverBCastAddr.sa) ); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + char buf[64]; + ipAddrToA ( & serverBCastAddr.ia, buf, sizeof ( buf ) ); + errPrintf ( S_cas_bindFail, __FILE__, __LINE__, + "- bind UDP IP addr=%s failed because %s", + buf, sockErrBuf ); + epicsSocketDestroy ( this->sock ); + epicsSocketDestroy ( this->bcastRecvSock ); + throw S_cas_bindFail; + } + } + else { + this->bcastRecvSock=INVALID_SOCKET; + } +#else + this->bcastRecvSock=INVALID_SOCKET; +#endif +} + +// +// use an initialize routine ? +// +casDGIntfIO::~casDGIntfIO() +{ + if ( this->sock != INVALID_SOCKET ) { + epicsSocketDestroy ( this->sock ); + } + + if ( this->bcastRecvSock != INVALID_SOCKET ) { + epicsSocketDestroy ( this->bcastRecvSock ); + } + + if ( this->beaconSock != INVALID_SOCKET ) { + epicsSocketDestroy ( this->beaconSock ); + } + + // avoid use of ellFree because problems on windows occur if the + // free is in a different DLL than the malloc + ELLNODE * nnode = this->beaconAddrList.node.next; + while ( nnode ) + { + ELLNODE * pnode = nnode; + nnode = nnode->next; + free ( pnode ); + } + + tsSLList < ipIgnoreEntry > tmp; + this->ignoreTable.removeAll ( tmp ); + while ( ipIgnoreEntry * pEntry = tmp.get() ) { + pEntry->~ipIgnoreEntry (); + this->ipIgnoreEntryFreeList.release ( pEntry ); + } + + osiSockRelease (); +} + +void casDGIntfIO::show (unsigned level) const +{ + printf ( "casDGIntfIO at %p\n", + static_cast ( this ) ); + printChannelAccessAddressList (&this->beaconAddrList); + this->casDGClient::show (level); +} + +void casDGIntfIO::xSetNonBlocking() +{ + osiSockIoctl_t yes = true; + + int status = socket_ioctl ( this->sock, FIONBIO, &yes ); // X aCC 392 + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "%s:CAS: UDP non blocking IO set fail because \"%s\"\n", + __FILE__, sockErrBuf ); + } + + if ( this->bcastRecvSock != INVALID_SOCKET ) { + yes = true; + int status = socket_ioctl ( this->bcastRecvSock, // X aCC 392 + FIONBIO, &yes ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "%s:CAS: Broadcast receive UDP non blocking IO set failed because \"%s\"\n", + __FILE__, sockErrBuf ); + } + } +} + +inBufClient::fillCondition +casDGIntfIO::osdRecv ( char * pBufIn, bufSizeT size, // X aCC 361 + fillParameter parm, bufSizeT & actualSize, caNetAddr & fromOut ) +{ + int status; + osiSocklen_t addrSize; + sockaddr addr; + SOCKET sockThisTime; + + if ( parm == fpUseBroadcastInterface ) { + sockThisTime = this->bcastRecvSock; + } + else { + sockThisTime = this->sock; + } + + addrSize = ( osiSocklen_t ) sizeof ( addr ); + status = recvfrom ( sockThisTime, pBufIn, size, 0, + &addr, &addrSize ); + if ( status <= 0 ) { + if ( status < 0 ) { + if ( SOCKERRNO != SOCK_EWOULDBLOCK ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAS: UDP recv error was \"%s\"\n", sockErrBuf ); + } + } + return casFillNone; + } + else { + // filter out and discard frames received from the ignore list + if ( this->ignoreTable.numEntriesInstalled () > 0 ) { + if ( addr.sa_family == AF_INET ) { + sockaddr_in * pIP = + reinterpret_cast < sockaddr_in * > ( & addr ); + ipIgnoreEntry comapre ( pIP->sin_addr.s_addr ); + if ( this->ignoreTable.lookup ( comapre ) ) { + return casFillNone; + } + } + } + fromOut = addr; + actualSize = static_cast < bufSizeT > ( status ); + return casFillProgress; + } +} + +outBufClient::flushCondition +casDGIntfIO::osdSend ( const char * pBufIn, bufSizeT size, // X aCC 361 + const caNetAddr & to ) +{ + int status; + + // + // (char *) cast below is for brain dead wrs prototype + // + struct sockaddr dest = to; + status = sendto ( this->sock, (char *) pBufIn, size, 0, + & dest, sizeof ( dest ) ); + if ( status >= 0 ) { + assert ( size == (unsigned) status ); + return outBufClient::flushProgress; + } + else { + if ( SOCKERRNO != SOCK_EWOULDBLOCK ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + char buf[64]; + sockAddrToA ( & dest, buf, sizeof ( buf ) ); + errlogPrintf ( + "CAS: UDP socket send to \"%s\" failed because \"%s\"\n", + buf, sockErrBuf ); + } + return outBufClient::flushNone; + } +} + +bufSizeT casDGIntfIO :: + dgInBytesPending () const +{ + int status; + osiSockIoctl_t nchars = 0; + + status = socket_ioctl ( this->sock, FIONREAD, & nchars ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAS: FIONREAD failed because \"%s\"\n", + sockErrBuf ); + return 0u; + } + else if ( nchars < 0 ) { + return 0u; + } + else { + return ( bufSizeT ) nchars; + } +} + +void casDGIntfIO::sendBeaconIO ( char & msg, unsigned length, + aitUint16 & portField, aitUint32 & addrField ) +{ + caNetAddr addr = this->serverAddress (); + struct sockaddr_in inetAddr = addr.getSockIP(); + osiSockAddrNode *pAddr; + int status; + char buf[64]; + + portField = inetAddr.sin_port; // the TCP port + + for (pAddr = reinterpret_cast ( ellFirst ( & this->beaconAddrList ) ); + pAddr; pAddr = reinterpret_cast ( ellNext ( & pAddr->node ) ) ) { + status = connect ( this->beaconSock, &pAddr->addr.sa, sizeof ( pAddr->addr.sa ) ); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + ipAddrToDottedIP ( & pAddr->addr.ia, buf, sizeof ( buf ) ); + errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n", + __FILE__, buf, sockErrBuf ); + } + else { + osiSockAddr sockAddr; + osiSocklen_t size = ( osiSocklen_t ) sizeof ( sockAddr.sa ); + status = getsockname ( this->beaconSock, &sockAddr.sa, &size ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n", + __FILE__, sockErrBuf ); + } + else if ( sockAddr.sa.sa_family == AF_INET ) { + addrField = sockAddr.ia.sin_addr.s_addr; + + status = send ( this->beaconSock, &msg, length, 0 ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + ipAddrToA ( &pAddr->addr.ia, buf, sizeof(buf) ); + errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n", + __FILE__, buf, sockErrBuf ); + } + else { + unsigned statusAsLength = static_cast < unsigned > ( status ); + assert ( statusAsLength == length ); + } + } + } + } +} + +bufSizeT casDGIntfIO::optimumInBufferSize () +{ + +#if 1 + // + // must update client before the message size can be + // increased here + // + return ETHERNET_MAX_UDP; +#else + int n; + int size; + int status; + + /* fetch the TCP send buffer size */ + n = sizeof(size); + status = getsockopt( + this->sock, + SOL_SOCKET, + SO_RCVBUF, + (char *)&size, + &n); + if(status < 0 || n != sizeof(size)){ + size = ETEHRNET_MAX_UDP; + } + + if (size<=0) { + size = ETHERNET_MAX_UDP; + } + return (bufSizeT) size; +#endif +} + +bufSizeT casDGIntfIO :: + osSendBufferSize () const +{ + /* fetch the TCP send buffer size */ + int size = MAX_UDP_SEND; + osiSocklen_t n = sizeof ( size ); + int status = getsockopt( this->sock, SOL_SOCKET, SO_SNDBUF, + reinterpret_cast < char * > ( & size ), & n ); + if ( status < 0 || n != sizeof(size) ) { + size = MAX_UDP_SEND; + } + if ( size <= MAX_UDP_SEND ) { + size = MAX_UDP_SEND; + } + return static_cast < bufSizeT > ( size ); +} + +SOCKET casDGIntfIO::makeSockDG () +{ + int yes = true; + int status; + SOCKET newSock; + + newSock = epicsSocketCreate (AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (newSock == INVALID_SOCKET) { + errMessage(S_cas_noMemory, "CAS: unable to create cast socket\n"); + return INVALID_SOCKET; + } + + status = setsockopt( + newSock, + SOL_SOCKET, + SO_BROADCAST, + (char *)&yes, + sizeof(yes)); + if (status<0) { + epicsSocketDestroy (newSock); + errMessage(S_cas_internal, + "CAS: unable to set up cast socket\n"); + return INVALID_SOCKET; + } + + // + // some concern that vxWorks will run out of mBuf's + // if this change is made + // + // joh 11-10-98 + // +#if 0 + { + // + // + // this allows for faster connects by queuing + // additional incoming UDP search frames + // + // this allocates a 32k buffer + // (uses a power of two) + // + int size = 1u<<15u; + status = setsockopt( + newSock, + SOL_SOCKET, + SO_RCVBUF, + (char *)&size, + sizeof(size)); + if (status<0) { + epicsSocketDestroy (newSock); + errMessage(S_cas_internal, + "CAS: unable to set cast socket size\n"); + return INVALID_SOCKET; + } + } +#endif + + // + // release the port in case we exit early. Also if + // on a kernel with MULTICAST mods then we can have + // two UDP servers on the same port number (requires + // setting SO_REUSEADDR prior to the bind step below). + // + epicsSocketEnableAddressUseForDatagramFanout ( newSock ); + + return newSock; +} + +int casDGIntfIO::getFD() const +{ + return this->sock; +} + diff --git a/src/cas/io/bsdSocket/casDGIntfIO.h b/src/cas/io/bsdSocket/casDGIntfIO.h new file mode 100644 index 000000000..e0df44040 --- /dev/null +++ b/src/cas/io/bsdSocket/casDGIntfIO.h @@ -0,0 +1,72 @@ + +/*************************************************************************\ +* 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 +// + +#ifndef casDGIntfIOh +#define casDGIntfIOh + +#include "casDGClient.h" +#include "ipIgnoreEntry.h" + +class casDGIntfIO : public casDGClient { +public: + casDGIntfIO ( caServerI & serverIn, clientBufMemoryManager &, + const caNetAddr & addr, bool autoBeaconAddr = true, + bool addConfigBeaconAddr = false ); + virtual ~casDGIntfIO(); + + int getFD () const; + int getBCastFD () const; + bool validBCastFD () const; + + void xSetNonBlocking (); + void sendBeaconIO ( char & msg, bufSizeT length, + aitUint16 &portField, aitUint32 & addrField ); + + outBufClient::flushCondition osdSend ( const char * pBuf, bufSizeT nBytesReq, + const caNetAddr & addr); + inBufClient::fillCondition osdRecv ( char * pBuf, bufSizeT nBytesReq, + inBufClient::fillParameter parm, bufSizeT & nBytesActual, caNetAddr & addr ); + virtual void show ( unsigned level ) const; + + static bufSizeT optimumInBufferSize (); + + bufSizeT dgInBytesPending () const; + bufSizeT osSendBufferSize () const ; + +private: + tsFreeList < ipIgnoreEntry, 128 > ipIgnoreEntryFreeList; + resTable < ipIgnoreEntry, ipIgnoreEntry > ignoreTable; + ELLLIST beaconAddrList; + SOCKET sock; + SOCKET bcastRecvSock; // fix for solaris bug + SOCKET beaconSock; // allow connect + unsigned short dgPort; + + static SOCKET makeSockDG (); + casDGIntfIO ( const casDGIntfIO & ); + casDGIntfIO & operator = ( const casDGIntfIO & ); +}; + +inline int casDGIntfIO::getBCastFD() const +{ + return this->bcastRecvSock; +} + +inline bool casDGIntfIO::validBCastFD() const +{ + return this->bcastRecvSock != INVALID_SOCKET; +} + +#endif // casDGIntfIOh + diff --git a/src/cas/io/bsdSocket/casIOD.h b/src/cas/io/bsdSocket/casIOD.h new file mode 100644 index 000000000..bb1991afd --- /dev/null +++ b/src/cas/io/bsdSocket/casIOD.h @@ -0,0 +1,40 @@ +/*************************************************************************\ +* 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 +// + +#ifndef includeCASIODH +#define includeCASIODH + +#ifdef epicsExportSharedSymbols +# define ipIgnoreEntryEpicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "envDefs.h" +#include "resourceLib.h" +#include "tsFreeList.h" +#include "osiSock.h" +#include "inetAddrID.h" +#include "compilerDependencies.h" + +#ifdef ipIgnoreEntryEpicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class caServerIO; +class casStreamOS; + + +// no additions below this line +#endif // includeCASIODH + diff --git a/src/cas/io/bsdSocket/casIntfIO.cc b/src/cas/io/bsdSocket/casIntfIO.cc new file mode 100644 index 000000000..16cd908e3 --- /dev/null +++ b/src/cas/io/bsdSocket/casIntfIO.cc @@ -0,0 +1,236 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// Author: Jeff Hill +// + +#include + +#include "errlog.h" +#define epicsAssertAuthor "Jeff Hill johill@lanl.gov" +#include "epicsAssert.h" + +#define epicsExportSharedSymbols +#include "casdef.h" +#include "caNetAddr.h" +#include "casIntfIO.h" +#include "casStreamIO.h" +#include "casStreamOS.h" + +// +// 5 appears to be a TCP/IP built in maximum +// +const unsigned caServerConnectPendQueueSize = 5u; + +// +// casIntfIO::casIntfIO() +// +casIntfIO::casIntfIO ( const caNetAddr & addrIn ) : + sock ( INVALID_SOCKET ), + addr ( addrIn.getSockIP() ) +{ + int status; + osiSocklen_t addrSize; + bool portChange; + + if ( ! osiSockAttach () ) { + throw S_cas_internal; + } + + /* + * Setup the server socket + */ + this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if (this->sock==INVALID_SOCKET) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + printf ( "No socket error was %s\n", sockErrBuf ); + throw S_cas_noFD; + } + + epicsSocketEnableAddressReuseDuringTimeWaitState ( this->sock ); + + status = bind ( this->sock, + reinterpret_cast (&this->addr), + sizeof(this->addr) ); + if (status<0) { + if (SOCKERRNO == SOCK_EADDRINUSE) { + // + // enable assignment of a default port + // (so the getsockname() call below will + // work correctly) + // + this->addr.sin_port = ntohs (0); + status = bind( + this->sock, + reinterpret_cast (&this->addr), + sizeof(this->addr) ); + } + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + char buf[64]; + ipAddrToA (&this->addr, buf, sizeof(buf)); + errPrintf ( S_cas_bindFail, + __FILE__, __LINE__, + "- bind TCP IP addr=%s failed because %s", + buf, sockErrBuf ); + epicsSocketDestroy (this->sock); + throw S_cas_bindFail; + } + portChange = true; + } + else { + portChange = false; + } + + addrSize = ( osiSocklen_t ) sizeof (this->addr); + status = getsockname ( + this->sock, + reinterpret_cast ( &this->addr ), + &addrSize ); + if (status) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAS: getsockname() error %s\n", + sockErrBuf ); + epicsSocketDestroy (this->sock); + throw S_cas_internal; + } + + // + // be sure of this now so that we can fetch the IP + // address and port number later + // + assert (this->addr.sin_family == AF_INET); + + if ( portChange ) { + errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n"); + errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", + ntohs (this->addr.sin_port) ); + errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n"); + errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" ); + errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" ); + } + + status = listen(this->sock, caServerConnectPendQueueSize); + if(status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAS: listen() error %s\n", sockErrBuf ); + epicsSocketDestroy (this->sock); + throw S_cas_internal; + } +} + +// +// casIntfIO::~casIntfIO() +// +casIntfIO::~casIntfIO() +{ + if (this->sock != INVALID_SOCKET) { + epicsSocketDestroy (this->sock); + } + + osiSockRelease (); +} + +// +// newStreamIO::newStreamClient() +// +casStreamOS *casIntfIO::newStreamClient ( caServerI & cas, + clientBufMemoryManager & bufMgr ) const +{ + static bool oneMsgFlag = false; + + struct sockaddr newClientAddr; + osiSocklen_t length = ( osiSocklen_t ) sizeof ( newClientAddr ); + SOCKET newSock = epicsSocketAccept ( this->sock, & newClientAddr, & length ); + if ( newSock == INVALID_SOCKET ) { + int errnoCpy = SOCKERRNO; + if ( errnoCpy != SOCK_EWOULDBLOCK && ! oneMsgFlag ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAS: %s accept error \"%s\"\n", + __FILE__, sockErrBuf ); + oneMsgFlag = true; + } + return NULL; + } + else if ( sizeof ( newClientAddr ) > (size_t) length ) { + epicsSocketDestroy ( newSock ); + errlogPrintf ( "CAS: accept returned bad address len?\n" ); + return NULL; + } + oneMsgFlag = false; + ioArgsToNewStreamIO args; + args.clientAddr = newClientAddr; + args.sock = newSock; + casStreamOS * pOS = new casStreamOS ( cas, bufMgr, args ); + if ( ! pOS ) { + errMessage ( S_cas_noMemory, + "unable to create data structures for a new client" ); + epicsSocketDestroy ( newSock ); + } + else { + if ( cas.getDebugLevel() > 0u ) { + char pName[64u]; + + pOS->hostName ( pName, sizeof ( pName ) ); + errlogPrintf ( "CAS: allocated client object for \"%s\"\n", pName ); + } + } + return pOS; +} + +// +// casIntfIO::setNonBlocking() +// +void casIntfIO::setNonBlocking() +{ + int status; + osiSockIoctl_t yes = true; + + status = socket_ioctl(this->sock, FIONBIO, &yes); // X aCC 392 + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( + "%s:CAS: server non blocking IO set fail because \"%s\"\n", + __FILE__, sockErrBuf ); + } +} + +// +// casIntfIO::getFD() +// +int casIntfIO::getFD() const +{ + return this->sock; +} + +// +// casIntfIO::show() +// +void casIntfIO::show(unsigned level) const +{ + if (level>2u) { + printf(" casIntfIO::sock = %d\n", this->sock); + } +} + +// +// casIntfIO::serverAddress () +// (avoid problems with GNU inliner) +// +caNetAddr casIntfIO::serverAddress () const +{ + return caNetAddr (this->addr); +} diff --git a/src/cas/io/bsdSocket/casIntfIO.h b/src/cas/io/bsdSocket/casIntfIO.h new file mode 100644 index 000000000..2e74729a3 --- /dev/null +++ b/src/cas/io/bsdSocket/casIntfIO.h @@ -0,0 +1,62 @@ + +/*************************************************************************\ +* 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 +// + +#ifndef casIntfIOh +#define casIntfIOh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_casIntfIOh +# undef epicsExportSharedSymbols +#endif + +// external headers included here +#include "osiSock.h" + +#ifdef epicsExportSharedSymbols_casIntfIOh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class caNetAddr; +class caServerI; +class clientBufMemoryManager; + +// +// casIntfIO +// +class casIntfIO { +public: + casIntfIO ( const caNetAddr & addr ); + virtual ~casIntfIO (); + void show ( unsigned level ) const; + + int getFD () const; + + void setNonBlocking (); + + // + // called when we expect that a virtual circuit for a + // client can be created + // + class casStreamOS * newStreamClient ( caServerI & cas, + clientBufMemoryManager & ) const; + + caNetAddr serverAddress () const; + +private: + SOCKET sock; + struct sockaddr_in addr; +}; + +#endif // casIntfIOh diff --git a/src/cas/io/bsdSocket/casStreamIO.cc b/src/cas/io/bsdSocket/casStreamIO.cc new file mode 100644 index 000000000..8513f7bc2 --- /dev/null +++ b/src/cas/io/bsdSocket/casStreamIO.cc @@ -0,0 +1,314 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// Author: Jeff Hill +// + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "casStreamIO.h" + +// casStreamIO::casStreamIO() +casStreamIO::casStreamIO ( caServerI & cas, clientBufMemoryManager & bufMgr, + const ioArgsToNewStreamIO & args ) : + casStrmClient ( cas, bufMgr, args.clientAddr ), + sock ( args.sock ), + _osSendBufferSize ( MAX_TCP ), + blockingFlag ( xIsBlocking ), + sockHasBeenShutdown ( false ) +{ + assert ( sock >= 0 ); + int yes = true; + int status; + + /* + * see TCP(4P) this seems to make unsollicited single events much + * faster. I take care of queue up as load increases. + */ + status = setsockopt ( this->sock, IPPROTO_TCP, TCP_NODELAY, + ( char * ) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( + "CAS: %s TCP_NODELAY option set failed %s\n", + __FILE__, sockErrBuf ); + throw S_cas_internal; + } + + /* + * turn on KEEPALIVE so if the client crashes + * this task will find out and exit + */ + status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, + (char *) & yes, sizeof ( yes ) ); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( + "CAS: %s SO_KEEPALIVE option set failed %s\n", + __FILE__, sockErrBuf ); + throw S_cas_internal; + } + + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made + * + * joh 11-10-98 + */ +#if 0 + int i; + + /* + * set TCP buffer sizes to be synergistic + * with CA internal buffering + */ + i = MAX_MSG_SIZE; + status = setsockopt( + sock, + SOL_SOCKET, + SO_SNDBUF, + (char *)&i, + sizeof(i)); + if(status < 0){ + errlogPrintf("CAS: SO_SNDBUF set failed\n"); + throw S_cas_internal; + } + i = MAX_MSG_SIZE; + status = setsockopt( + sock, + SOL_SOCKET, + SO_RCVBUF, + (char *)&i, + sizeof(i)); + if(status < 0){ + errlogPrintf("CAS: SO_RCVBUF set failed\n"); + throw S_cas_internal; + } +#endif + + /* cache the TCP send buffer size */ + int size = MAX_TCP; + osiSocklen_t n = sizeof ( size ) ; + status = getsockopt ( this->sock, SOL_SOCKET, + SO_SNDBUF, reinterpret_cast < char * > ( & size ), & n ); + if ( status < 0 || n != sizeof ( size ) ) { + size = MAX_TCP; + } + if ( size <= MAX_TCP ) { + size = MAX_TCP; + } + _osSendBufferSize = static_cast < bufSizeT > ( size ); +} + +// casStreamIO::~casStreamIO() +casStreamIO::~casStreamIO() +{ + epicsSocketDestroy ( this->sock ); +} + +// casStreamIO::osdSend() +outBufClient::flushCondition casStreamIO::osdSend ( const char *pInBuf, bufSizeT nBytesReq, + bufSizeT &nBytesActual ) +{ + int status; + + if ( nBytesReq == 0 ) { + nBytesActual = 0; + return outBufClient::flushNone; + } + + status = send (this->sock, (char *) pInBuf, nBytesReq, 0); + if (status == 0) { + return outBufClient::flushDisconnect; + } + else if (status<0) { + int anerrno = SOCKERRNO; + + if ( anerrno == SOCK_EINTR || + anerrno == SOCK_EWOULDBLOCK ) { + return outBufClient::flushNone; + } + + if ( anerrno == SOCK_ENOBUFS ) { + errlogPrintf ( + "cas: system low on network buffers - hybernating for 1 second\n" ); + epicsThreadSleep ( 1.0 ); + return outBufClient::flushNone; + } + + if ( + anerrno != SOCK_ECONNABORTED && + anerrno != SOCK_ECONNRESET && + anerrno != SOCK_EPIPE && + anerrno != SOCK_ETIMEDOUT ) { + + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + char buf[64]; + this->hostName ( buf, sizeof ( buf ) ); + errlogPrintf ( +"CAS: TCP socket send to \"%s\" failed because \"%s\"\n", + buf, sockErrBuf ); + } + return outBufClient::flushDisconnect; + } + nBytesActual = (bufSizeT) status; + return outBufClient::flushProgress; +} + +// casStreamIO::osdRecv() +inBufClient::fillCondition +casStreamIO::osdRecv ( char * pInBuf, bufSizeT nBytes, // X aCC 361 + bufSizeT & nBytesActual ) +{ + int nchars; + + nchars = recv (this->sock, pInBuf, nBytes, 0); + if ( nchars == 0 ) { + return casFillDisconnect; + } + else if ( nchars < 0 ) { + int myerrno = SOCKERRNO; + char buf[64]; + + if ( myerrno == SOCK_EWOULDBLOCK || + myerrno == SOCK_EINTR ) { + return casFillNone; + } + + if ( myerrno == SOCK_ENOBUFS ) { + errlogPrintf ( + "CAS: system low on network buffers - hybernating for 1 second\n" ); + epicsThreadSleep ( 1.0 ); + return casFillNone; + } + + if ( + myerrno != SOCK_ECONNABORTED && + myerrno != SOCK_ECONNRESET && + myerrno != SOCK_EPIPE && + myerrno != SOCK_ETIMEDOUT ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + this->hostName ( buf, sizeof ( buf ) ); + errlogPrintf( + "CAS: client %s disconnected because \"%s\"\n", + buf, sockErrBuf ); + } + return casFillDisconnect; + } + else { + nBytesActual = (bufSizeT) nchars; + return casFillProgress; + } +} + +// casStreamIO::forceDisconnect() +void casStreamIO::forceDisconnect () +{ + // !!!! other OS specific wakeup will be required here when we + // !!!! switch to a threaded implementation + if ( ! this->sockHasBeenShutdown ) { + int status = ::shutdown ( this->sock, SHUT_RDWR ); + if ( status == 0 ) { + this->sockHasBeenShutdown = true; + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("CAC TCP socket shutdown error was %s\n", + sockErrBuf ); + } + } +} + +// casStreamIO::show() +void casStreamIO::osdShow (unsigned level) const +{ + printf ( "casStreamIO at %p\n", + static_cast ( this ) ); + if (level>1u) { + char buf[64]; + this->hostName ( buf, sizeof ( buf ) ); + printf ( "client = \"%s\"\n", buf ); + } +} + +// casStreamIO::xSsetNonBlocking() +void casStreamIO::xSetNonBlocking() +{ + int status; + osiSockIoctl_t yes = true; + + status = socket_ioctl(this->sock, FIONBIO, &yes); // X aCC 392 + if (status>=0) { + this->blockingFlag = xIsntBlocking; + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "%s:CAS: TCP non blocking IO set fail because \"%s\"\n", + __FILE__, sockErrBuf ); + throw S_cas_internal; + } +} + +// casStreamIO::blockingState() +xBlockingStatus casStreamIO::blockingState() const +{ + return this->blockingFlag; +} + +// casStreamIO :: inCircuitBytesPending() +bufSizeT casStreamIO :: inCircuitBytesPending () const +{ + int status; + osiSockIoctl_t nchars = 0; + + status = socket_ioctl ( this->sock, FIONREAD, &nchars ); + if ( status < 0 ) { + int localError = SOCKERRNO; + if ( + localError != SOCK_ECONNABORTED && + localError != SOCK_ECONNRESET && + localError != SOCK_EPIPE && + localError != SOCK_ETIMEDOUT ) + { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + char buf[64]; + this->hostName ( buf, sizeof ( buf ) ); + errlogPrintf ("CAS: FIONREAD for %s failed because \"%s\"\n", + buf, sockErrBuf ); + } + return 0u; + } + else if ( nchars < 0 ) { + return 0u; + } + else { + return ( bufSizeT ) nchars; + } +} + +// casStreamIO :: osSendBufferSize () +bufSizeT casStreamIO :: osSendBufferSize () const +{ + return _osSendBufferSize; +} + +// casStreamIO::getFD() +int casStreamIO::getFD() const +{ + return this->sock; +} diff --git a/src/cas/io/bsdSocket/casStreamIO.h b/src/cas/io/bsdSocket/casStreamIO.h new file mode 100644 index 000000000..e5d488549 --- /dev/null +++ b/src/cas/io/bsdSocket/casStreamIO.h @@ -0,0 +1,50 @@ +/*************************************************************************\ +* 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 is 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 +// + +#ifndef casStreamIOh +#define casStreamIOh + +#include "casStrmClient.h" + +struct ioArgsToNewStreamIO { + caNetAddr clientAddr; + SOCKET sock; +}; + +class casStreamIO : public casStrmClient { +public: + casStreamIO ( caServerI &, clientBufMemoryManager &, + const ioArgsToNewStreamIO & ); + ~casStreamIO (); + int getFD () const; + void xSetNonBlocking (); + bufSizeT inCircuitBytesPending () const; + bufSizeT osSendBufferSize () const; +private: + SOCKET sock; + bufSizeT _osSendBufferSize; + xBlockingStatus blockingFlag; + + bool sockHasBeenShutdown; + xBlockingStatus blockingState() const; + void osdShow ( unsigned level ) const; + outBufClient::flushCondition osdSend ( const char *pBuf, bufSizeT nBytesReq, + bufSizeT & nBytesActual ); + inBufClient::fillCondition osdRecv ( char *pBuf, bufSizeT nBytesReq, + bufSizeT & nBytesActual ); + void forceDisconnect (); + casStreamIO ( const casStreamIO & ); + casStreamIO & operator = ( const casStreamIO & ); +}; + +#endif // casStreamIOh diff --git a/src/cas/io/bsdSocket/ipIgnoreEntry.cpp b/src/cas/io/bsdSocket/ipIgnoreEntry.cpp new file mode 100644 index 000000000..f40006a71 --- /dev/null +++ b/src/cas/io/bsdSocket/ipIgnoreEntry.cpp @@ -0,0 +1,90 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// +// +// Author: Jeffrey O. Hill +// johill@lanl.gov +// + +#include +#include + +#include "osiSock.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#define caNetAddrSock +#include "ipIgnoreEntry.h" + +void ipIgnoreEntry::show ( unsigned /* level */ ) const +{ + char buf[256]; + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = this->ipAddr; + addr.sin_port = 0; + ipAddrToDottedIP ( & addr, buf, sizeof ( buf ) ); + printf ( "ipIgnoreEntry: %s\n", buf ); +} + +bool ipIgnoreEntry::operator == ( const ipIgnoreEntry & rhs ) const +{ + return this->ipAddr == rhs.ipAddr; +} + +resTableIndex ipIgnoreEntry::hash () const +{ + const unsigned inetAddrMinIndexBitWidth = 8u; + const unsigned inetAddrMaxIndexBitWidth = 32u; + return integerHash ( inetAddrMinIndexBitWidth, + inetAddrMaxIndexBitWidth, this->ipAddr ); +} + +ipIgnoreEntry::ipIgnoreEntry ( unsigned ipAddrIn ) : + ipAddr ( ipAddrIn ) +{ +} + +void * ipIgnoreEntry::operator new ( size_t size, + tsFreeList < class ipIgnoreEntry, 128 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +void ipIgnoreEntry::operator delete ( void * pCadaver, + tsFreeList < class ipIgnoreEntry, 128 > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +void * ipIgnoreEntry::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void ipIgnoreEntry::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + + + + diff --git a/src/cas/io/bsdSocket/ipIgnoreEntry.h b/src/cas/io/bsdSocket/ipIgnoreEntry.h new file mode 100644 index 000000000..a0088632c --- /dev/null +++ b/src/cas/io/bsdSocket/ipIgnoreEntry.h @@ -0,0 +1,52 @@ + + +/*************************************************************************\ +* 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 +// + +#ifndef ipIgnoreEntryh +#define ipIgnoreEntryh + +#ifdef epicsExportSharedSymbols +# define epicsExportSharedSymbols_ipIgnoreEntryh +# undef epicsExportSharedSymbols +#endif + +#include "tsSLList.h" +#include "tsFreeList.h" +#include "resourceLib.h" + +#ifdef epicsExportSharedSymbols_ipIgnoreEntryh +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +class ipIgnoreEntry : public tsSLNode < ipIgnoreEntry > { +public: + ipIgnoreEntry ( unsigned ipAddr ); + void show ( unsigned level ) const; + bool operator == ( const ipIgnoreEntry & ) const; + resTableIndex hash () const; + void * operator new ( size_t size, + tsFreeList < class ipIgnoreEntry, 128 > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < class ipIgnoreEntry, 128 > & )) +private: + unsigned ipAddr; + ipIgnoreEntry ( const ipIgnoreEntry & ); + ipIgnoreEntry & operator = ( const ipIgnoreEntry & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +#endif // ipIgnoreEntryh + diff --git a/src/cas/os/vms/BUILD_VMS.COM b/src/cas/os/vms/BUILD_VMS.COM new file mode 100644 index 000000000..5a56baedc --- /dev/null +++ b/src/cas/os/vms/BUILD_VMS.COM @@ -0,0 +1,203 @@ +$!======================================================================== +$! +$! Name : BUILD_VMS +$! +$! Purpose : To build the CA server library and test programs for +$! VAX/VMS. This procedure assumes the following: +$! - You have copied *.c and *.h from the Epics channel access +$! server source directory (base/src/cas) into this VMS directory +$! - You have copied *.c from the Epics +$! base/src/libCom directory into this VMS directory +$! - You have copied *.h from the base/include directory into this +$! VMS directory +$! - You are using Multinet for TCP/IP access. If not, the logical +$! name definitions below will need to be changed +$! +$! +$! Arguments : None +$! +$! Created 16-NOV-1993 Mark L. Rivers +$! 05-MAY-1994 Jeff O. Hill Updated for EPICS 3.12 +$! +$!======================================================================== +$! +$! Example FTP script moves sources from UNIX to VMS +$! (remove "$!" comment delimeters) +$! +$! open +$! user +$! mkdir [.cas] +$! cd [.cas] +$! prompt +$! lcd ~/epics/base/src/cas +$! mput *.c +$! mput *.h +$! put BUILD_VMS.COM +$! lcd vms +$! mput *.c +$! mput *.h +$! lcd ../../libCom +$! mput *.c +$! mput *.h +$! lcd ../ca +$! mput *.h +$! mput *.c +$! lcd ../../include +$! mput *.h +$!======================================================================== +$! +$ cwd = f$logical("sys$disk") + f$directory() +$ define /nolog sys multinet_root:[multinet.include.sys] +$ define /nolog vms multinet_root:[multinet.include.vms] +$ define /nolog net multinet_root:[multinet.include.net] +$ define /nolog netinet multinet_root:[multinet.include.netinet] +$ define /nolog arpa multinet_root:[multinet.include.arpa] +$ define /nolog tcp multinet_root:[multinet.include] +$! +$! Compile the functions and test programs +$! Define symbol for the CC command +$ call set_cc_command +$ if (p1 .nes. "") +$ then +$ cc_command 'p1' +$ else +$ cc_command casCreateCvrt +$ call linktmp casCreateCvrt +$ casCreateCvrt == "$" + cwd + "casCreateCvrt.exe" +$ casCreateCvrt casCvrt.c +$ cc_command - +exampleCaServer, - +singleThread, - +camessage, - +camsgtask, - +caserverio, - +caservertask, - +cast_server, - +online_notify, - +caEventQueue, - +caMemory, - +casAccess, - +casCvrt, - +mitfp, - +pvRead, - +pvWrite, - +cvtfast, - +errPrintfUNIX, - +fdmgr +$ cc_command - +ACCESS, - +CONN, - +CONVERT, - +FLOW_CONTROL, - +IOCINF, - +REPEATER, - +CAREPEATER, - +SERVICE, - +SYNCGRP,- +TEST_EVENT, - +BSD_DEPEN, - +IF_DEPEN, - +VMS_DEPEN, - +ELLLIB, - +BUCKETLIB, - +ENVSUBR, - +TSSUBR, - +NEXTFIELDSUBR, - +ASSERTUNIX, - +CATIME, - +ACCTST +$ endif +$ +$! +$! Build an object library +$ library /create ca_server_library - +casAccess, - +singleThread, - +camessage, - +camsgtask, - +caserverio, - +caservertask, - +cast_server, - +online_notify, - +caEventQueue, - +casCvrt, - +mitfp, - +caMemory, - +pvRead, - +pvWrite, - +cvtfast, - +errPrintfUNIX, - +fdmgr +$ library /create ca_client_library - +IOCINF, - +ACCESS, - +CONN, - +CONVERT, - +FLOW_CONTROL, - +REPEATER, - +TEST_EVENT, - +SYNCGRP, - +SERVICE, - +IF_DEPEN, - +VMS_DEPEN, - +BSD_DEPEN, - +BUCKETLIB, - +TSSUBR, - +ENVSUBR, - +NEXTFIELDSUBR, - +ASSERTUNIX, - +ELLLIB +$! Link the example server +$ call link exampleCaServer +$ +$! Setup DCL Foreign Command for UNIX cmd line params +$ excas == "$" + cwd + "exampleCaServer.exe" +$ +$! +$ link: subroutine +$! Link differently for VAX and AXP +$ if f$getsyi("HW_MODEL") .ge. 1024 +$ then +$ link 'p1', sys$input/options + ca_server_library/lib + ca_client_library/lib + multinet_socket_library/share +$ else +$ link 'p1', sys$input/options + ca_server_library/lib + ca_client_library/lib + multinet_socket_library/share + sys$share:vaxcrtl/share +$ endif +$ endsubroutine +$ +$ +$ linktmp: subroutine +$! Link differently for VAX and AXP +$ if f$getsyi("HW_MODEL") .ge. 1024 +$ then +$ link 'p1' +$ else +$ link 'p1', sys$input/options + sys$share:vaxcrtl/share +$ endif +$ endsubroutine +$ +$ +$! This subroutine sets up "cc_command" to use different switches for +$! VAX (assumes VAX C compiler) and AXP (DEC C compiler). +$ set_cc_command : subroutine +$ if f$getsyi("HW_MODEL") .ge. 1024 +$ then +$! turn of no prototype messages because MULTINET does not +$! supply prototypes. +$ cc_command:== cc /warn/float=d_float - + /include=([], [-.include], [-.libcom]) - + /define=(MULTINET=1) +$ else +$ cc_command:== cc /include=([], [-.include], [-.libcom]) - + /define=(MULTINET=1) +$ endif +$ endsubroutine +$! ************************************************************ + diff --git a/src/cas/os/vms/README b/src/cas/os/vms/README new file mode 100644 index 000000000..6602707af --- /dev/null +++ b/src/cas/os/vms/README @@ -0,0 +1,7 @@ + +WORK IN PROGRESS + +this directory contains the vms os dependent source for +the EPICS ca server + + diff --git a/src/cas/os/vms/casSpecificOS.h b/src/cas/os/vms/casSpecificOS.h new file mode 100644 index 000000000..7aad5d828 --- /dev/null +++ b/src/cas/os/vms/casSpecificOS.h @@ -0,0 +1,57 @@ +/*************************************************************************\ +* 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 + +#include + +#if defined (MULTINET) +# include "multinet_root:[multinet.include]errno.h" +#elif defined (WINTCP) +# include +#else +# include +#endif + +#include +#include +#include +#include + +#if defined(UCX) && 0 +# include +# include +#else +# include +#endif + +#include + +typedef int SOCKET; + +#if defined(WINTCP) + extern int uerrno; +# define SOCKERRNO uerrno +#elif defined(MULTINET) +# define SOCKERRNO socket_errno +#else +# define SOCKERRNO errno +#endif + +#if defined (UCX) +# define socket_close(S) close (S) +# define socket_ioctl(A,B,C) ioctl (A,B,C) +#elif defined (WINTCP) +# define socket_close(S) netclose (S) +# define socket_ioctl(A,B,C) ioctl (A,B,C) +#endif /* UCX */ + +#include + diff --git a/src/cas/os/vms/login.com b/src/cas/os/vms/login.com new file mode 100644 index 000000000..8c0e2c6d6 --- /dev/null +++ b/src/cas/os/vms/login.com @@ -0,0 +1,3 @@ + +set display/create/node=xxxx.atdiv.lanl.gov/trans=tcpip + diff --git a/src/cas/os/vms/mitfp.c b/src/cas/os/vms/mitfp.c new file mode 100644 index 000000000..0cbce11f4 --- /dev/null +++ b/src/cas/os/vms/mitfp.c @@ -0,0 +1,271 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * mitfp.c - routines to convert between VAX float and big endian + * IEEE float + * + * Author: Jeffrey O. Hill + * + * + */ + +/************************************************************************/ +/* float convert */ +/* (THIS ASSUMES IEEE IS THE NETWORK FLOATING POINT FORMAT) */ +/************************************************************************/ +struct ieeeflt{ + unsigned mant :23; + unsigned exp :8; + unsigned sign :1; +}; + +/* Exponent sign bias */ +#define IEEE_SB 127 + +/* Conversion Range */ +/* -126sign; + if( ((short) pMIT->exp) < (EXPMINIEEE + MIT_SB) ){ + exp = 0; + mant = 0; + sign = 0; + } + else{ + exp = ((short) pMIT->exp) - (MIT_SB + IEEE_SB); + mant = (pMIT->mant1<<16) | pMIT->mant2; + } + pIEEE->mant = mant; + pIEEE->exp = exp; + pIEEE->sign = sign; + + ptmp = (short *)pIEEE; + *ptmp = htons(*ptmp); + + return ieee; +} + + +/* + * ntohf() + * + * sign must be forced to zero if the exponent is zero to prevent a reserved + * operand fault- joh 9-13-90 + */ +float ntohf(float ieee) +{ + float mit; + struct ieeeflt *pIEEE; + struct mitflt *pMIT; + short *ptmp; + long exp; + long mant2; + long mant1; + long sign; + + pMIT = (struct mitflt *) &mit; + pIEEE = (struct ieeeflt *) &ieee; + + ptmp = (short *)pIEEE; + *ptmp = htonl(*ptmp); + + if( ((short) pIEEE->exp) > EXPMAXMIT + IEEE_SB){ + sign = pIEEE->sign; + exp = EXPMAXMIT + MIT_SB; + mant2 = ~0; + mant1 = ~0; + } + else if( pIEEE->exp == 0){ + sign = 0; + exp = 0; + mant2 = 0; + mant1 = 0; + } + else{ + sign = pIEEE->sign; + exp = pIEEE->exp+MIT_SB-IEEE_SB; + mant2 = pIEEE->mant; + mant1 = pIEEE->mant>>(unsigned)16; + } + pMIT->exp = exp; + pMIT->mant2 = mant2; + pMIT->mant1 = mant1; + pMIT->sign = sign; + + return mit; +} + + +/************************************************************************/ +/* double convert */ +/* (THIS ASSUMES IEEE IS THE NETWORK FLOATING POINT FORMAT) */ +/************************************************************************/ + +/* (this includes mapping of fringe reals to zero or infinity) */ +/* (byte swaps included in conversion */ + +struct ieeedbl{ + unsigned int mant2 : 32; + unsigned int mant1 : 20; + unsigned int exp : 11; + unsigned int sign : 1; +}; + +#define IEEE_DBL_SB 1023 + +/* Conversion Range */ +/* -1022exp) < (DBLEXPMINMIT+MIT_DBL_SB) ){ + pIEEE->mant1 = 0; + pIEEE->mant2 = 0; + pIEEE->exp = 0; + pIEEE->sign = 0; + } + else{ + pIEEE->exp = ((int)pMIT->exp)+(IEEE_DBL_SB-MIT_DBL_SB); + pIEEE->mant1 = (pMIT->mant1<<13) | (pMIT->mant2>>3); + pIEEE->mant2 = (pMIT->mant2<<29) | (pMIT->mant3<<13) | + (pMIT->mant4>>3); + pIEEE->sign = pMIT->sign; + } + + /* + * byte swap to net order + */ + ptmp = (long *) pIEEE; + tmp = htonl(ptmp[0]); + ptmp[0] = htonl(ptmp[1]); + ptmp[1] = tmp; + + return ieee; +} + + +/* + * sign must be forced to zero if the exponent is zero to prevent a reserved + * operand fault- joh 9-13-90 + */ +double ntohd(double ieee) +{ + double mit; + struct mitdbl *pMIT; + struct ieeedbl *pIEEE; + long *ptmp; + long tmp; + + pMIT = (struct mitdbl *)&mit; + pIEEE = (struct ieeedbl *)&ieee; + + /* + * Byte swap from net order to host order + */ + ptmp = (long *) pIEEE; + tmp = htonl(ptmp[0]); + ptmp[0] = htonl(ptmp[1]); + ptmp[1] = tmp; + + if( ((int)pIEEE->exp) > (DBLEXPMAXMIT + IEEE_DBL_SB) ){ + pMIT->sign = pIEEE->sign; + pMIT->exp = DBLEXPMAXMIT + MIT_DBL_SB; + pMIT->mant1 = ~0; + pMIT->mant2 = ~0; + pMIT->mant3 = ~0; + pMIT->mant4 = ~0; + } + else if( ((int)pIEEE->exp) < (DBLEXPMINMIT + IEEE_DBL_SB) ){ + pMIT->sign = 0; + pMIT->exp = 0; + pMIT->mant1 = 0; + pMIT->mant2 = 0; + pMIT->mant3 = 0; + pMIT->mant4 = 0; + } + else{ + pMIT->sign = pIEEE->sign; + pMIT->exp = ((int)pIEEE->exp)+(MIT_DBL_SB-IEEE_DBL_SB); + pMIT->mant1 = pIEEE->mant1>>13; + pMIT->mant2 = (pIEEE->mant1<<3) | (pIEEE->mant2>>29); + pMIT->mant3 = pIEEE->mant2>>13; + pMIT->mant4 = pIEEE->mant2<<3; + } + return mit; +} + diff --git a/src/cas/os/vms/mitfp.cc b/src/cas/os/vms/mitfp.cc new file mode 100644 index 000000000..5dd1903ff --- /dev/null +++ b/src/cas/os/vms/mitfp.cc @@ -0,0 +1,284 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * mitfp.c - routines to convert between VAX float and big endian + * IEEE float + * + * Author: Jeffrey O. Hill + * + * + */ + +/* + * include htons() etc. + */ +#include +#include + +double htond(double mit); +double ntohd(double ieee); +float htonf(float mit); +float ntohf(float ieee); + + + +/************************************************************************/ +/* float convert */ +/* (THIS ASSUMES IEEE IS THE NETWORK FLOATING POINT FORMAT) */ +/************************************************************************/ +struct ieeeflt{ + unsigned mant :23; + unsigned exp :8; + unsigned sign :1; +}; + +/* Exponent sign bias */ +#define IEEE_SB 127 + +/* Conversion Range */ +/* -126sign; + if( ((short) pMIT->exp) < (EXPMINIEEE + MIT_SB) ){ + exp = 0; + mant = 0; + sign = 0; + } + else{ + exp = ((short) pMIT->exp) - (MIT_SB + IEEE_SB); + mant = (pMIT->mant1<<16) | pMIT->mant2; + } + pIEEE->mant = mant; + pIEEE->exp = exp; + pIEEE->sign = sign; + + ptmp = (short *)pIEEE; + *ptmp = htons(*ptmp); + + return ieee; +} + + +/* + * ntohf() + * + * sign must be forced to zero if the exponent is zero to prevent a reserved + * operand fault- joh 9-13-90 + */ +float ntohf(float ieee) +{ + float mit; + struct ieeeflt *pIEEE; + struct mitflt *pMIT; + short *ptmp; + long exp; + long mant2; + long mant1; + long sign; + + pMIT = (struct mitflt *) &mit; + pIEEE = (struct ieeeflt *) &ieee; + + ptmp = (short *)pIEEE; + *ptmp = htonl(*ptmp); + + if( ((short) pIEEE->exp) > EXPMAXMIT + IEEE_SB){ + sign = pIEEE->sign; + exp = EXPMAXMIT + MIT_SB; + mant2 = ~0; + mant1 = ~0; + } + else if( pIEEE->exp == 0){ + sign = 0; + exp = 0; + mant2 = 0; + mant1 = 0; + } + else{ + sign = pIEEE->sign; + exp = pIEEE->exp+MIT_SB-IEEE_SB; + mant2 = pIEEE->mant; + mant1 = pIEEE->mant>>(unsigned)16; + } + pMIT->exp = exp; + pMIT->mant2 = mant2; + pMIT->mant1 = mant1; + pMIT->sign = sign; + + return mit; +} + + +/************************************************************************/ +/* double convert */ +/* (THIS ASSUMES IEEE IS THE NETWORK FLOATING POINT FORMAT) */ +/************************************************************************/ + +/* (this includes mapping of fringe reals to zero or infinity) */ +/* (byte swaps included in conversion */ + +struct ieeedbl{ + unsigned int mant2 : 32; + unsigned int mant1 : 20; + unsigned int exp : 11; + unsigned int sign : 1; +}; + +#define IEEE_DBL_SB 1023 + +/* Conversion Range */ +/* -1022exp) < (DBLEXPMINMIT+MIT_DBL_SB) ){ + pIEEE->mant1 = 0; + pIEEE->mant2 = 0; + pIEEE->exp = 0; + pIEEE->sign = 0; + } + else{ + pIEEE->exp = ((int)pMIT->exp)+(IEEE_DBL_SB-MIT_DBL_SB); + pIEEE->mant1 = (pMIT->mant1<<13) | (pMIT->mant2>>3); + pIEEE->mant2 = (pMIT->mant2<<29) | (pMIT->mant3<<13) | + (pMIT->mant4>>3); + pIEEE->sign = pMIT->sign; + } + + /* + * byte swap to net order + */ + ptmp = (long *) pIEEE; + tmp = htonl(ptmp[0]); + ptmp[0] = htonl(ptmp[1]); + ptmp[1] = tmp; + + return ieee; +} + + +/* + * sign must be forced to zero if the exponent is zero to prevent a reserved + * operand fault- joh 9-13-90 + */ +double ntohd(double ieee) +{ + double mit; + struct mitdbl *pMIT; + struct ieeedbl *pIEEE; + long *ptmp; + long tmp; + + pMIT = (struct mitdbl *)&mit; + pIEEE = (struct ieeedbl *)&ieee; + + /* + * Byte swap from net order to host order + */ + ptmp = (long *) pIEEE; + tmp = htonl(ptmp[0]); + ptmp[0] = htonl(ptmp[1]); + ptmp[1] = tmp; + + if( ((int)pIEEE->exp) > (DBLEXPMAXMIT + IEEE_DBL_SB) ){ + pMIT->sign = pIEEE->sign; + pMIT->exp = DBLEXPMAXMIT + MIT_DBL_SB; + pMIT->mant1 = ~0; + pMIT->mant2 = ~0; + pMIT->mant3 = ~0; + pMIT->mant4 = ~0; + } + else if( ((int)pIEEE->exp) < (DBLEXPMINMIT + IEEE_DBL_SB) ){ + pMIT->sign = 0; + pMIT->exp = 0; + pMIT->mant1 = 0; + pMIT->mant2 = 0; + pMIT->mant3 = 0; + pMIT->mant4 = 0; + } + else{ + pMIT->sign = pIEEE->sign; + pMIT->exp = ((int)pIEEE->exp)+(MIT_DBL_SB-IEEE_DBL_SB); + pMIT->mant1 = pIEEE->mant1>>13; + pMIT->mant2 = (pIEEE->mant1<<3) | (pIEEE->mant2>>29); + pMIT->mant3 = pIEEE->mant2>>13; + pMIT->mant4 = pIEEE->mant2<<3; + } + return mit; +} + diff --git a/src/cas/os/vms/mitfp.h b/src/cas/os/vms/mitfp.h new file mode 100644 index 000000000..dce0ad99d --- /dev/null +++ b/src/cas/os/vms/mitfp.h @@ -0,0 +1,17 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +double htond(double mit); +double ntohd(double ieee); +float htonf(float mit); +float ntohf(float ieee); + + diff --git a/src/cas/os/vms/vms_depen.h b/src/cas/os/vms/vms_depen.h new file mode 100644 index 000000000..630de4441 --- /dev/null +++ b/src/cas/os/vms/vms_depen.h @@ -0,0 +1,45 @@ +/*************************************************************************\ +* 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 +#include +#include +#include + + +#if defined(WINTCP) /* Wallangong */ +# define socket_close(S) netclose(S) +# define socket_ioctl(A,B,C) ioctl(A,B,C) +# include +# include +# include +# include +# define SOCKERRNO uerrno +#elif defined(UCX) /* GeG 09-DEC-1992 */ +# include +# include +# define socket_close(S) close(S) +# define socket_ioctl(A,B,C) ioctl(A,B,C) +# include +# define SOCKERRNO errno +#else /* MULTINET */ +# include +# include +# include +# include +# define SOCKERRNO socket_errno +#endif + +/* + * NOOP out task watch dog for now + */ +#define taskwdInsert(A,B,C) +#define taskwdRemove(A) diff --git a/src/cas/test/gddAppFuncTableTest.cc b/src/cas/test/gddAppFuncTableTest.cc new file mode 100644 index 000000000..2c95ebeda --- /dev/null +++ b/src/cas/test/gddAppFuncTableTest.cc @@ -0,0 +1,74 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// gddAppFuncTable.h +// +// Author: Jeff Hill +// +// +// +// + +#include +#include "gddAppFuncTable.h" + +class casPV { +public: + casPV(const char * const pNameIn) : pName(pNameIn) {}; + gddAppFuncTableStatus cb(gdd &val); +protected: + const char * const pName; +}; + +class casPVII : public casPV { +public: + casPVII(const char * const pNameIn) : casPV(pNameIn) {}; + gddAppFuncTableStatus cb0(gdd &val); + gddAppFuncTableStatus cb1(gdd &val); +}; + +main () +{ + gddAppFuncTableTable pvAttrDB; + unsigned appPrecision= pvAttrDB.RegisterApplicationType ("precision"); + unsigned appValue = pvAttrDB.RegisterApplicationType ("value"); + gddScalar *pPrec = new gddScalar (appPrecision, aitEnumFloat32); + gddScalar *pVal = new gddScalar (appValue, aitEnumFloat32); + casPV pv ("jane"); + casPVII pvII ("fred"); + casPVII pvIIa ("albert"); + + pvAttrDB.installReadFunc (appPrecision, casPVII::cb0); + pvAttrDB.installReadFunc (appValue, casPVII::cb1); + + pvAttrDB.read (pvII, *pPrec); + pvAttrDB.read (pvII, *pVal); + pvAttrDB.read (pvIIa, *pPrec); +} + +unsigned casPV::cb(gdd &value) +{ + printf("Here for %s\n", pName); + return 0; +} + +unsigned casPVII::cb0(gdd &value) +{ + printf("Here in cb0 for %s\n", pName); + return 0; +} + +unsigned casPVII::cb1(gdd &value) +{ + printf("Here in cb1 for %s\n", pName); + return 0; +} + + diff --git a/src/catools/Makefile b/src/catools/Makefile new file mode 100644 index 000000000..2d18c9411 --- /dev/null +++ b/src/catools/Makefile @@ -0,0 +1,32 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +# Synchrotronstrahlung. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../.. + +include $(TOP)/configure/CONFIG + +SHARED_LIBRARIES = NO + +# Build but don't install catools as a static library +LIBRARY += catools +INSTALL_LIBS = + +catools_SRCS += tool_lib.c +catools_LIBS += ca Com + +# Build and link programs against the catools library +PROD_DEFAULT += caget camonitor cainfo caput +PROD_vxWorks = -nil- +PROD_RTEMS = -nil- + +PROD_LIBS += catools ca Com +catools_DIR = . + +include $(TOP)/configure/RULES diff --git a/src/catools/caget.c b/src/catools/caget.c new file mode 100644 index 000000000..8489ecbcb --- /dev/null +++ b/src/catools/caget.c @@ -0,0 +1,558 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2006 Diamond Light Source Ltd. +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange (BESSY) + * + * Modification History + * 2006/01/17 Malcolm Walters (Tessella/Diamond Light Source) + * Fixed problem with "-c -w 0" hanging forever + * 2008/04/16 Ralph Lange (BESSY) + * Updated usage info + * 2009/03/31 Larry Hoff (BNL) + * Added field separators + * 2009/04/01 Ralph Lange (HZB/BESSY) + * Added support for long strings (array of char) and quoting of nonprintable characters + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "tool_lib.h" + +#define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */ +#define PEND_EVENT_SLICES 5 /* No. of pend_event slices for callback requests */ + +/* Different output formats */ +typedef enum { plain, terse, all, specifiedDbr } OutputT; + +/* Different request types */ +typedef enum { get, callback } RequestT; + +static int nConn = 0; /* Number of connected PVs */ +static int nRead = 0; /* Number of channels that were read */ +static int floatAsString = 0; /* Flag: fetch floats as string */ + + +static void usage (void) +{ + fprintf (stderr, "\nUsage: caget [options] ...\n\n" + " -h: Help: Print this message\n" + "Channel Access options:\n" + " -w : Wait time, specifies CA timeout, default is %f second(s)\n" + " -c: Asynchronous get (use ca_get_callback and wait for completion)\n" + " -p : CA priority (0-%u, default 0=lowest)\n" + "Format options:\n" + " Default output format is \"name value\"\n" + " -t: Terse mode - print only value, without name\n" + " -a: Wide mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n" + " -d : Request specific dbr type; use string (DBR_ prefix may be omitted)\n" + " or number of one of the following types:\n" + " DBR_STRING 0 DBR_STS_FLOAT 9 DBR_TIME_LONG 19 DBR_CTRL_SHORT 29\n" + " DBR_INT 1 DBR_STS_ENUM 10 DBR_TIME_DOUBLE 20 DBR_CTRL_INT 29\n" + " DBR_SHORT 1 DBR_STS_CHAR 11 DBR_GR_STRING 21 DBR_CTRL_FLOAT 30\n" + " DBR_FLOAT 2 DBR_STS_LONG 12 DBR_GR_SHORT 22 DBR_CTRL_ENUM 31\n" + " DBR_ENUM 3 DBR_STS_DOUBLE 13 DBR_GR_INT 22 DBR_CTRL_CHAR 32\n" + " DBR_CHAR 4 DBR_TIME_STRING 14 DBR_GR_FLOAT 23 DBR_CTRL_LONG 33\n" + " DBR_LONG 5 DBR_TIME_INT 15 DBR_GR_ENUM 24 DBR_CTRL_DOUBLE 34\n" + " DBR_DOUBLE 6 DBR_TIME_SHORT 15 DBR_GR_CHAR 25 DBR_STSACK_STRING 37\n" + " DBR_STS_STRING 7 DBR_TIME_FLOAT 16 DBR_GR_LONG 26 DBR_CLASS_NAME 38\n" + " DBR_STS_SHORT 8 DBR_TIME_ENUM 17 DBR_GR_DOUBLE 27\n" + " DBR_STS_INT 8 DBR_TIME_CHAR 18 DBR_CTRL_STRING 28\n" + "Enum format:\n" + " -n: Print DBF_ENUM value as number (default is enum string)\n" + "Arrays: Value format: print number of requested values, then list of values\n" + " Default: Print all values\n" + " -# : Print first elements of an array\n" + " -S: Print array of char as a string (long string)\n" + "Floating point type format:\n" + " Default: Use %%g format\n" + " -e : Use %%e format, with a precision of digits\n" + " -f : Use %%f format, with a precision of digits\n" + " -g : Use %%g format, with a precision of digits\n" + " -s: Get value as string (honors server-side precision)\n" + " -lx: Round to long integer and print as hex number\n" + " -lo: Round to long integer and print as octal number\n" + " -lb: Round to long integer and print as binary number\n" + "Integer number format:\n" + " Default: Print as decimal number\n" + " -0x: Print as hex number\n" + " -0o: Print as octal number\n" + " -0b: Print as binary number\n" + "Alternate output field separator:\n" + " -F : Use as an alternate output field separator\n" + "\nExample: caget -a -f8 my_channel another_channel\n" + " (uses wide output format, doubles are printed as %%f with precision of 8)\n\n" + , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); +} + + + +/*+************************************************************************** + * + * Function: event_handler + * + * Description: CA event_handler for request type callback + * Allocates the dbr structure and copies the data + * + * Arg(s) In: args - event handler args (see CA manual) + * + **************************************************************************-*/ + +static void event_handler (evargs args) +{ + pv* ppv = args.usr; + + ppv->status = args.status; + if (args.status == ECA_NORMAL) + { + ppv->dbrType = args.type; + ppv->value = calloc(1, dbr_size_n(args.type, args.count)); + memcpy(ppv->value, args.dbr, dbr_size_n(args.type, args.count)); + ppv->onceConnected = 1; + ppv->nElems = args.count; + nRead++; + } +} + + + +/*+************************************************************************** + * + * Function: caget + * + * Description: Issue read requests, wait for incoming data + * and print the data according to the selected format + * + * Arg(s) In: pvs - Pointer to an array of pv structures + * nPvs - Number of elements in the pvs array + * request - Request type + * format - Output format + * dbrType - Requested dbr type + * reqElems - Requested number of (array) elements + * + * Return(s): Error code: 0 = OK, 1 = Error + * + **************************************************************************-*/ + +static int caget (pv *pvs, int nPvs, RequestT request, OutputT format, + chtype dbrType, unsigned long reqElems) +{ + unsigned int i; + int n, result; + + for (n = 0; n < nPvs; n++) { + + /* Set up pvs structure */ + /* -------------------- */ + + /* Get natural type and array count */ + pvs[n].nElems = ca_element_count(pvs[n].chid); + pvs[n].dbfType = ca_field_type(pvs[n].chid); + pvs[n].dbrType = dbrType; + + /* Set up value structures */ + if (format != specifiedDbr) + { + pvs[n].dbrType = dbf_type_to_DBR_TIME(pvs[n].dbfType); /* Use native type */ + if (dbr_type_is_ENUM(pvs[n].dbrType)) /* Enums honour -n option */ + { + if (enumAsNr) pvs[n].dbrType = DBR_TIME_INT; + else pvs[n].dbrType = DBR_TIME_STRING; + } + else if (floatAsString && + (dbr_type_is_FLOAT(pvs[n].dbrType) || dbr_type_is_DOUBLE(pvs[n].dbrType))) + { + pvs[n].dbrType = DBR_TIME_STRING; + } + } + /* Adjust array count */ + if (reqElems > pvs[n].nElems) + reqElems = pvs[n].nElems; + pvs[n].reqElems = reqElems; + + /* Issue CA request */ + /* ---------------- */ + + if (ca_state(pvs[n].chid) == cs_conn) + { + nConn++; + pvs[n].onceConnected = 1; + if (request == callback) + { /* Event handler will allocate value */ + result = ca_array_get_callback(pvs[n].dbrType, + pvs[n].reqElems, + pvs[n].chid, + event_handler, + (void*)&pvs[n]); + } else { + /* Allocate value structure */ + pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].nElems)); + if(!pvs[n].value) { + fprintf(stderr,"Allocation failed\n"); + return 1; + } + result = ca_array_get(pvs[n].dbrType, + pvs[n].nElems, + pvs[n].chid, + pvs[n].value); + } + pvs[n].status = result; + } else { + pvs[n].status = ECA_DISCONN; + } + } + if (!nConn) return 1; /* No connection? We're done. */ + + /* Wait for completion */ + /* ------------------- */ + + result = ca_pend_io(caTimeout); + if (result == ECA_TIMEOUT) + fprintf(stderr, "Read operation timed out: some PV data was not read.\n"); + + if (request == callback) /* Also wait for callbacks */ + { + if (caTimeout != 0) + { + double slice = caTimeout / PEND_EVENT_SLICES; + for (n = 0; n < PEND_EVENT_SLICES; n++) + { + ca_pend_event(slice); + if (nRead >= nConn) break; + } + if (nRead < nConn) + fprintf(stderr, "Read operation timed out: some PV data was not read.\n"); + } else { + /* For 0 timeout keep waiting until all are done */ + while (nRead < nConn) { + ca_pend_event(1.0); + } + } + } + + /* Print the data */ + /* -------------- */ + + for (n = 0; n < nPvs; n++) { + /* Truncate the data printed to what was requested. */ + if (pvs[n].reqElems != 0 && pvs[n].nElems > pvs[n].reqElems) + pvs[n].nElems = pvs[n].reqElems; + + switch (format) { + case plain: /* Emulate old caget behaviour */ + if (pvs[n].nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name); + else printf("%s", pvs[n].name); + printf("%c", fieldSeparator); + case terse: + if (pvs[n].status == ECA_DISCONN) + printf("*** not connected\n"); + else if (pvs[n].status == ECA_NORDACCESS) + printf("*** no read access\n"); + else if (pvs[n].status != ECA_NORMAL) + printf("*** CA error %s\n", ca_message(pvs[n].status)); + else if (pvs[n].value == 0) + printf("*** no data available (timeout)\n"); + else + { + if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].nElems > 1)) { + dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType); + int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); + char *d = calloc(dlen+1, sizeof(char)); + if(d) { + epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s)); + printf("%s", d); + free(d); + } else { + fprintf(stderr,"Failed to allocate space for escaped string\n"); + } + } else { + if (reqElems || pvs[n].nElems > 1) printf("%lu%c", pvs[n].nElems, fieldSeparator); + for (i=0; i 1)) { + dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType); + int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); + char *d = calloc(dlen+1, sizeof(char)); + if(d) { + epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s)); + printf("%s", d); + free(d); + } else { + fprintf(stderr,"Failed to allocate space for escaped string\n"); + } + } else { + for (i=0; i DBR_DOUBLE) /* Extended type extra info */ + printf("%s\n", dbr2str(pvs[n].value, pvs[n].dbrType)); + } + } + break; + default : + break; + } + } + return 0; +} + + + +/*+************************************************************************** + * + * Function: main + * + * Description: caget main() + * Evaluate command line options, set up CA, connect the + * channels, collect and print the data as requested + * + * Arg(s) In: [options] ... + * + * Arg(s) Out: none + * + * Return(s): Standard return code (0=success, 1=error) + * + **************************************************************************-*/ + +static void complainIfNotPlainAndSet (OutputT *current, const OutputT requested) +{ + if (*current != plain) + fprintf(stderr, + "Options t,d,a are mutually exclusive. " + "('caget -h' for help.)\n"); + *current = requested; +} + +int main (int argc, char *argv[]) +{ + int n = 0; + int result; /* CA result */ + OutputT format = plain; /* User specified format */ + RequestT request = get; /* User specified request type */ + IntFormatT outType; /* Output type */ + + int count = 0; /* 0 = not specified by -# option */ + int opt; /* getopt() current option */ + int type = -1; /* getopt() data type argument */ + int digits = 0; /* getopt() no. of float digits */ + + int nPvs; /* Number of PVs */ + pv* pvs = 0; /* Array of PV structures */ + + setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ + + while ((opt = getopt(argc, argv, ":taicnhsSe:f:g:l:#:d:0:w:p:F:")) != -1) { + switch (opt) { + case 'h': /* Print usage */ + usage(); + return 0; + case 't': /* Terse output mode */ + complainIfNotPlainAndSet(&format, terse); + break; + case 'a': /* Wide output mode */ + complainIfNotPlainAndSet(&format, all); + break; + case 'c': /* Callback mode */ + request = callback; + break; + case 'd': /* Data type specification */ + complainIfNotPlainAndSet(&format, specifiedDbr); + /* Argument (type) may be text or number */ + if (sscanf(optarg, "%d", &type) != 1) + { + dbr_text_to_type(optarg, type); + if (type == -1) /* Invalid? Try prefix DBR_ */ + { + char str[30] = "DBR_"; + strncat(str, optarg, 25); + dbr_text_to_type(str, type); + } + } + if (type < DBR_STRING || type > DBR_CLASS_NAME + || type == DBR_PUT_ACKT || type == DBR_PUT_ACKS) + { + fprintf(stderr, "Requested dbr type out of range " + "or invalid - ignored. ('caget -h' for help.)\n"); + format = plain; + } + break; + case 'n': /* Print ENUM as index numbers */ + enumAsNr = 1; + break; + case 'w': /* Set CA timeout value */ + if(epicsScanDouble(optarg, &caTimeout) != 1) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "- ignored. ('caget -h' for help.)\n", optarg); + caTimeout = DEFAULT_TIMEOUT; + } + break; + case '#': /* Array count */ + if (sscanf(optarg,"%d", &count) != 1) + { + fprintf(stderr, "'%s' is not a valid array element count " + "- ignored. ('caget -h' for help.)\n", optarg); + count = 0; + } + break; + case 'p': /* CA priority */ + if (sscanf(optarg,"%u", &caPriority) != 1) + { + fprintf(stderr, "'%s' is not a valid CA priority " + "- ignored. ('caget -h' for help.)\n", optarg); + caPriority = DEFAULT_CA_PRIORITY; + } + if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; + break; + case 's': /* Select string dbr for floating type data */ + floatAsString = 1; + break; + case 'S': /* Treat char array as (long) string */ + charArrAsStr = 1; + break; + case 'e': /* Select %e/%f/%g format, using digits */ + case 'f': + case 'g': + if (sscanf(optarg, "%d", &digits) != 1) + fprintf(stderr, + "Invalid precision argument '%s' " + "for option '-%c' - ignored.\n", optarg, opt); + else + { + if (digits>=0 && digits<=VALID_DOUBLE_DIGITS) + sprintf(dblFormatStr, "%%-.%d%c", digits, opt); + else + fprintf(stderr, "Precision %d for option '-%c' " + "out of range - ignored.\n", digits, opt); + } + break; + case 'l': /* Convert to long and use integer format */ + case '0': /* Select integer format */ + switch ((char) *optarg) { + case 'x': outType = hex; break; /* x print Hex */ + case 'b': outType = bin; break; /* b print Binary */ + case 'o': outType = oct; break; /* o print Octal */ + default : + outType = dec; + fprintf(stderr, "Invalid argument '%s' " + "for option '-%c' - ignored.\n", optarg, opt); + } + if (outType != dec) { + if (opt == '0') { + type = DBR_LONG; + outTypeI = outType; + } else { + outTypeF = outType; + } + } + break; + case 'F': /* Store this for output and tool_lib formatting */ + fieldSeparator = (char) *optarg; + break; + case '?': + fprintf(stderr, + "Unrecognized option: '-%c'. ('caget -h' for help.)\n", + optopt); + return 1; + case ':': + fprintf(stderr, + "Option '-%c' requires an argument. ('caget -h' for help.)\n", + optopt); + return 1; + default : + usage(); + return 1; + } + } + + nPvs = argc - optind; /* Remaining arg list are PV names */ + + if (nPvs < 1) + { + fprintf(stderr, "No pv name specified. ('caget -h' for help.)\n"); + return 1; + } + /* Start up Channel Access */ + + result = ca_context_create(ca_disable_preemptive_callback); + if (result != ECA_NORMAL) { + fprintf(stderr, "CA error %s occurred while trying " + "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + return 1; + } + /* Allocate PV structure array */ + + pvs = calloc (nPvs, sizeof(pv)); + if (!pvs) + { + fprintf(stderr, "Memory allocation for channel structures failed.\n"); + return 1; + } + /* Connect channels */ + + for (n = 0; optind < argc; n++, optind++) + pvs[n].name = argv[optind] ; /* Copy PV names from command line */ + + connect_pvs(pvs, nPvs); + + /* Read and print data */ + + result = caget(pvs, nPvs, request, format, type, count); + + /* Shut down Channel Access */ + ca_context_destroy(); + + return result; +} diff --git a/src/catools/cainfo.c b/src/catools/cainfo.c new file mode 100644 index 000000000..e86cdb4d5 --- /dev/null +++ b/src/catools/cainfo.c @@ -0,0 +1,224 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* 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. +\*************************************************************************/ + +/* + * Author: Ralph Lange (BESSY) + * + * Modification History + * 2008/04/16 Ralph Lange (BESSY) + * Updated usage info + * 2009/04/01 Ralph Lange (HZB/BESSY) + * Clarified output for native data type + * + */ + +#include +#include + +#include +#include + +#include "tool_lib.h" + +static unsigned statLevel = 0; /* ca_client_status() interest level */ + + +void usage (void) +{ + fprintf (stderr, "\nUsage: cainfo [options] ...\n\n" + " -h: Help: Print this message\n" + "Channel Access options:\n" + " -w : Wait time, specifies CA timeout, default is %f second(s)\n" + " -s : Call ca_client_status with the specified interest level\n" + " -p : CA priority (0-%u, default 0=lowest)\n" + "\nExample: cainfo my_channel another_channel\n\n" + , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); +} + + + +/*+************************************************************************** + * + * Function: cainfo + * + * Description: Print CA info data or call ca_client_status + * + * Arg(s) In: pvs - Pointer to an array of pv structures + * nPvs - Number of elements in the pvs array + * + * Return(s): Error code: 0 = OK, 1 = Error + * + **************************************************************************-*/ + +int cainfo (pv *pvs, int nPvs) +{ + int n; + long dbfType; + long dbrType; + unsigned long nElems; + enum channel_state state; + char *stateStrings[] = { + "never connected", "previously connected", "connected", "closed" }; + char *boolStrings[] = { "no ", "" }; + + if (statLevel) { + ca_client_status(statLevel); + + } else { + + for (n = 0; n < nPvs; n++) { + + /* Print the status data */ + /* --------------------- */ + + state = ca_state(pvs[n].chid); + nElems = ca_element_count(pvs[n].chid); + dbfType = ca_field_type(pvs[n].chid); + dbrType = dbf_type_to_DBR(dbfType); + + printf("%s\n" + " State: %s\n" + " Host: %s\n" + " Access: %sread, %swrite\n" + " Native data type: %s\n" + " Request type: %s\n" + " Element count: %lu\n" + , pvs[n].name, + stateStrings[state], + ca_host_name(pvs[n].chid), + boolStrings[ca_read_access(pvs[n].chid)], + boolStrings[ca_write_access(pvs[n].chid)], + dbf_type_to_text(dbfType), + dbr_type_to_text(dbrType), + nElems + ); + } + } + + return 0; +} + + + +/*+************************************************************************** + * + * Function: main + * + * Description: cainfo main() + * Evaluate command line options, set up CA, connect the + * channels, print the data as requested + * + * Arg(s) In: [options] ... + * + * Arg(s) Out: none + * + * Return(s): Standard return code (0=success, 1=error) + * + **************************************************************************-*/ + +int main (int argc, char *argv[]) +{ + int n = 0; + int result; /* CA result */ + + int opt; /* getopt() current option */ + + int nPvs; /* Number of PVs */ + pv* pvs = 0; /* Array of PV structures */ + + setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ + + while ((opt = getopt(argc, argv, ":nhw:s:p:")) != -1) { + switch (opt) { + case 'h': /* Print usage */ + usage(); + return 0; + case 'w': /* Set CA timeout value */ + if(epicsScanDouble(optarg, &caTimeout) != 1) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "- ignored. ('cainfo -h' for help.)\n", optarg); + caTimeout = DEFAULT_TIMEOUT; + } + break; + case 's': /* ca_client_status interest level */ + if (sscanf(optarg,"%du", &statLevel) != 1) + { + fprintf(stderr, "'%s' is not a valid interest level " + "- ignored. ('cainfo -h' for help.)\n", optarg); + statLevel = 0; + } + break; + case 'p': /* CA priority */ + if (sscanf(optarg,"%u", &caPriority) != 1) + { + fprintf(stderr, "'%s' is not a valid CA priority " + "- ignored. ('cainfo -h' for help.)\n", optarg); + caPriority = DEFAULT_CA_PRIORITY; + } + if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; + break; + case '?': + fprintf(stderr, + "Unrecognized option: '-%c'. ('cainfo -h' for help.)\n", + optopt); + return 1; + case ':': + fprintf(stderr, + "Option '-%c' requires an argument. ('cainfo -h' for help.)\n", + optopt); + return 1; + default : + usage(); + return 1; + } + } + + nPvs = argc - optind; /* Remaining arg list are PV names */ + + if (!statLevel && nPvs < 1) + { + fprintf(stderr, "No pv name specified. ('cainfo -h' for help.)\n"); + return 1; + } + /* Start up Channel Access */ + + result = ca_context_create(ca_disable_preemptive_callback); + if (result != ECA_NORMAL) { + fprintf(stderr, "CA error %s occurred while trying " + "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + return 1; + } + /* Allocate PV structure array */ + + pvs = calloc (nPvs, sizeof(pv)); + if (!pvs) + { + fprintf(stderr, "Memory allocation for channel structures failed.\n"); + return 1; + } + /* Connect channels */ + + for (n = 0; optind < argc; n++, optind++) + pvs[n].name = argv[optind] ; /* Copy PV names from command line */ + + connect_pvs(pvs, nPvs); + + /* Print data */ + result = cainfo(pvs, nPvs); + + /* Shut down Channel Access */ + ca_context_destroy(); + + return result; +} diff --git a/src/catools/camonitor.c b/src/catools/camonitor.c new file mode 100644 index 000000000..b11334b91 --- /dev/null +++ b/src/catools/camonitor.c @@ -0,0 +1,398 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* 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. +\*************************************************************************/ + +/* + * Author: Ralph Lange (BESSY) + * + * Modification History + * 2008/04/16 Ralph Lange (BESSY) + * Updated usage info + * 2009/03/31 Larry Hoff (BNL) + * Added field separators + * 2009/04/01 Ralph Lange (HZB/BESSY) + * Added support for long strings (array of char) and quoting of nonprintable characters + * + */ + +#include +#include +#include + +#include +#include + +#include "tool_lib.h" + +#define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */ + +static unsigned long reqElems = 0; +static unsigned long eventMask = DBE_VALUE | DBE_ALARM; /* Event mask used */ +static int floatAsString = 0; /* Flag: fetch floats as string */ +static int nConn = 0; /* Number of connected PVs */ + + +void usage (void) +{ + fprintf (stderr, "\nUsage: camonitor [options] ...\n\n" + " -h: Help: Print this message\n" + "Channel Access options:\n" + " -w : Wait time, specifies CA timeout, default is %f second(s)\n" + " -m : Specify CA event mask to use, with being any combination of\n" + " 'v' (value), 'a' (alarm), 'l' (log/archive), 'p' (property). Default: va\n" + " -p : CA priority (0-%u, default 0=lowest)\n" + "Timestamps:\n" + " Default: Print absolute timestamps (as reported by CA server)\n" + " -t : Specify timestamp source(s) and type, with containing\n" + " 's' = CA server (remote) timestamps\n" + " 'c' = CA client (local) timestamps (shown in '()'s)\n" + " 'n' = no timestamps\n" + " 'r' = relative timestamps (time elapsed since start of program)\n" + " 'i' = incremental timestamps (time elapsed since last update)\n" + " 'I' = incremental timestamps (time elapsed since last update, by channel)\n" + "Enum format:\n" + " -n: Print DBF_ENUM values as number (default are enum string values)\n" + "Arrays: Value format: print number of requested values, then list of values\n" + " Default: Print all values\n" + " -# : Print first elements of an array\n" + " -S: Print array of char as a string (long string)\n" + "Floating point type format:\n" + " Default: Use %%g format\n" + " -e : Use %%e format, with a precision of digits\n" + " -f : Use %%f format, with a precision of digits\n" + " -g : Use %%g format, with a precision of digits\n" + " -s: Get value as string (honors server-side precision)\n" + " -lx: Round to long integer and print as hex number\n" + " -lo: Round to long integer and print as octal number\n" + " -lb: Round to long integer and print as binary number\n" + "Integer number format:\n" + " Default: Print as decimal number\n" + " -0x: Print as hex number\n" + " -0o: Print as octal number\n" + " -0b: Print as binary number\n" + "Alternate output field separator:\n" + " -F : Use as an alternate output field separator\n" + "\nExample: camonitor -f8 my_channel another_channel\n" + " (doubles are printed as %%f with precision of 8)\n\n" + , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); +} + + + +/*+************************************************************************** + * + * Function: event_handler + * + * Description: CA event_handler for request type callback + * Allocates the dbr structure and copies the data + * + * Arg(s) In: args - event handler args (see CA manual) + * + **************************************************************************-*/ + +static void event_handler (evargs args) +{ + pv* pv = args.usr; + + pv->status = args.status; + if (args.status == ECA_NORMAL) + { + pv->dbrType = args.type; + pv->nElems = args.count; + memcpy(pv->value, args.dbr, dbr_size_n(args.type, args.count)); + + print_time_val_sts(pv, reqElems); + fflush(stdout); + } +} + + +/*+************************************************************************** + * + * Function: connection_handler + * + * Description: CA connection_handler + * + * Arg(s) In: args - connection_handler_args (see CA manual) + * + **************************************************************************-*/ + +static void connection_handler ( struct connection_handler_args args ) +{ + pv *ppv = ( pv * ) ca_puser ( args.chid ); + if ( args.op == CA_OP_CONN_UP ) { + /* Set up pv structure */ + /* ------------------- */ + + /* Get natural type and array count */ + ppv->nElems = ca_element_count(ppv->chid); + ppv->dbfType = ca_field_type(ppv->chid); + + /* Set up value structures */ + ppv->dbrType = dbf_type_to_DBR_TIME(ppv->dbfType); /* Use native type */ + if (dbr_type_is_ENUM(ppv->dbrType)) /* Enums honour -n option */ + { + if (enumAsNr) ppv->dbrType = DBR_TIME_INT; + else ppv->dbrType = DBR_TIME_STRING; + } + + else if (floatAsString && + (dbr_type_is_FLOAT(ppv->dbrType) || dbr_type_is_DOUBLE(ppv->dbrType))) + { + ppv->dbrType = DBR_TIME_STRING; + } + /* Adjust array count */ + if (reqElems > ppv->nElems) + reqElems = ppv->nElems; + ppv->reqElems = reqElems; + + ppv->onceConnected = 1; + nConn++; + /* Issue CA request */ + /* ---------------- */ + /* install monitor once with first connect */ + if ( ! ppv->value ) { + /* Allocate value structure */ + ppv->value = calloc(1, dbr_size_n(ppv->dbrType, ppv->nElems)); + if ( ppv->value ) { + ppv->status = ca_create_subscription(ppv->dbrType, + ppv->reqElems, + ppv->chid, + eventMask, + event_handler, + (void*)ppv, + NULL); + if ( ppv->status != ECA_NORMAL ) { + free ( ppv->value ); + } + } + } + } + else if ( args.op == CA_OP_CONN_DOWN ) { + nConn--; + ppv->status = ECA_DISCONN; + print_time_val_sts(ppv, reqElems); + } +} + + +/*+************************************************************************** + * + * Function: main + * + * Description: camonitor main() + * Evaluate command line options, set up CA, connect the + * channels, collect and print the data as requested + * + * Arg(s) In: [options] ... + * + * Arg(s) Out: none + * + * Return(s): Standard return code (0=success, 1=error) + * + **************************************************************************-*/ + +int main (int argc, char *argv[]) +{ + int returncode = 0; + int n = 0; + int result; /* CA result */ + IntFormatT outType; /* Output type */ + + int opt; /* getopt() current option */ + int digits = 0; /* getopt() no. of float digits */ + + int nPvs; /* Number of PVs */ + pv* pvs = 0; /* Array of PV structures */ + + setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ + + while ((opt = getopt(argc, argv, ":nhm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { + switch (opt) { + case 'h': /* Print usage */ + usage(); + return 0; + case 'n': /* Print ENUM as index numbers */ + enumAsNr=1; + break; + case 't': /* Select timestamp source(s) and type */ + tsSrcServer = 0; + tsSrcClient = 0; + { + int i = 0; + char c; + while ((c = optarg[i++])) + switch (c) { + case 's': tsSrcServer = 1; break; + case 'c': tsSrcClient = 1; break; + case 'n': break; + case 'r': tsType = relative; break; + case 'i': tsType = incremental; break; + case 'I': tsType = incrementalByChan; break; + default : + fprintf(stderr, "Invalid argument '%c' " + "for option '-t' - ignored.\n", c); + } + } + break; + case 'w': /* Set CA timeout value */ + if(epicsScanDouble(optarg, &caTimeout) != 1) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "- ignored. ('camonitor -h' for help.)\n", optarg); + caTimeout = DEFAULT_TIMEOUT; + } + break; + case '#': /* Array count */ + if (sscanf(optarg,"%ld", &reqElems) != 1) + { + fprintf(stderr, "'%s' is not a valid array element count " + "- ignored. ('camonitor -h' for help.)\n", optarg); + reqElems = 0; + } + break; + case 'p': /* CA priority */ + if (sscanf(optarg,"%u", &caPriority) != 1) + { + fprintf(stderr, "'%s' is not a valid CA priority " + "- ignored. ('camonitor -h' for help.)\n", optarg); + caPriority = DEFAULT_CA_PRIORITY; + } + if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; + break; + case 'm': /* Select CA event mask */ + eventMask = 0; + { + int i = 0; + char c, err = 0; + while ((c = optarg[i++]) && !err) + switch (c) { + case 'v': eventMask |= DBE_VALUE; break; + case 'a': eventMask |= DBE_ALARM; break; + case 'l': eventMask |= DBE_LOG; break; + case 'p': eventMask |= DBE_PROPERTY; break; + default : + fprintf(stderr, "Invalid argument '%s' " + "for option '-m' - ignored.\n", optarg); + eventMask = DBE_VALUE | DBE_ALARM; + err = 1; + } + } + break; + case 's': /* Select string dbr for floating type data */ + floatAsString = 1; + break; + case 'S': /* Treat char array as (long) string */ + charArrAsStr = 1; + break; + case 'e': /* Select %e/%f/%g format, using digits */ + case 'f': + case 'g': + if (sscanf(optarg, "%d", &digits) != 1) + fprintf(stderr, + "Invalid precision argument '%s' " + "for option '-%c' - ignored.\n", optarg, opt); + else + { + if (digits>=0 && digits<=VALID_DOUBLE_DIGITS) + sprintf(dblFormatStr, "%%-.%d%c", digits, opt); + else + fprintf(stderr, "Precision %d for option '-%c' " + "out of range - ignored.\n", digits, opt); + } + break; + case 'l': /* Convert to long and use integer format */ + case '0': /* Select integer format */ + switch ((char) *optarg) { + case 'x': outType = hex; break; /* x print Hex */ + case 'b': outType = bin; break; /* b print Binary */ + case 'o': outType = oct; break; /* o print Octal */ + default : + outType = dec; + fprintf(stderr, "Invalid argument '%s' " + "for option '-%c' - ignored.\n", optarg, opt); + } + if (outType != dec) { + if (opt == '0') outTypeI = outType; + else outTypeF = outType; + } + break; + case 'F': /* Store this for output and tool_lib formatting */ + fieldSeparator = (char) *optarg; + break; + case '?': + fprintf(stderr, + "Unrecognized option: '-%c'. ('camonitor -h' for help.)\n", + optopt); + return 1; + case ':': + fprintf(stderr, + "Option '-%c' requires an argument. ('camonitor -h' for help.)\n", + optopt); + return 1; + default : + usage(); + return 1; + } + } + + nPvs = argc - optind; /* Remaining arg list are PV names */ + + if (nPvs < 1) + { + fprintf(stderr, "No pv name specified. ('camonitor -h' for help.)\n"); + return 1; + } + /* Start up Channel Access */ + + result = ca_context_create(ca_disable_preemptive_callback); + if (result != ECA_NORMAL) { + fprintf(stderr, "CA error %s occurred while trying " + "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + return 1; + } + /* Allocate PV structure array */ + + pvs = calloc (nPvs, sizeof(pv)); + if (!pvs) + { + fprintf(stderr, "Memory allocation for channel structures failed.\n"); + return 1; + } + /* Connect channels */ + + /* Copy PV names from command line */ + for (n = 0; optind < argc; n++, optind++) + { + pvs[n].name = argv[optind]; + } + /* Create CA connections */ + returncode = create_pvs(pvs, nPvs, connection_handler); + if ( returncode ) { + return returncode; + } + /* Check for channels that didn't connect */ + ca_pend_event(caTimeout); + for (n = 0; n < nPvs; n++) + { + if (!pvs[n].onceConnected) + print_time_val_sts(&pvs[n], reqElems); + } + + /* Read and print data forever */ + ca_pend_event(0); + + /* Shut down Channel Access */ + ca_context_destroy(); + + return result; +} diff --git a/src/catools/caput.c b/src/catools/caput.c new file mode 100644 index 000000000..cf72ae298 --- /dev/null +++ b/src/catools/caput.c @@ -0,0 +1,560 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2006 Diamond Light Source Ltd. +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange (BESSY) + * + * Modification History + * 2006/01/17 Malcolm Walters (Tessella/Diamond Light Source) + * Added put_callback option - heavily based on caget + * 2008/03/06 Andy Foster (OSL/Diamond Light Source) + * Remove timeout dependency of ca_put_callback by using an EPICS event + * (semaphore), i.e. remove ca_pend_event time slicing. + * 2008/04/16 Ralph Lange (BESSY) + * Updated usage info + * 2009/03/31 Larry Hoff (BNL) + * Added field separators + * 2009/04/01 Ralph Lange (HZB/BESSY) + * Added support for long strings (array of char) and quoting of nonprintable characters + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "tool_lib.h" + +#define VALID_DOUBLE_DIGITS 18 /* Max usable precision for a double */ + +/* Different output formats */ +typedef enum { plain, terse, all } OutputT; + +/* Different request types */ +typedef enum { get, callback } RequestT; + +/* Valid EPICS string */ +typedef char EpicsStr[MAX_STRING_SIZE]; + +static int nConn = 0; /* Number of connected PVs */ +static epicsEventId epId; + +void usage (void) +{ + fprintf (stderr, "\nUsage: caput [options] \n" + " caput -a [options] ...\n\n" + " -h: Help: Print this message\n" + "Channel Access options:\n" + " -w : Wait time, specifies CA timeout, default is %f second(s)\n" + " -c: Asynchronous put (use ca_put_callback and wait for completion)\n" + " -p : CA priority (0-%u, default 0=lowest)\n" + "Format options:\n" + " -t: Terse mode - print only sucessfully written value, without name\n" + " -l: Long mode \"name timestamp value stat sevr\" (read PVs as DBR_TIME_xxx)\n" + "Enum format:\n" + " Default: Auto - try value as ENUM string, then as index number\n" + " -n: Force interpretation of values as numbers\n" + " -s: Force interpretation of values as strings\n" + "Arrays:\n" + " -a: Put array\n" + " Value format: number of requested values, then list of values\n" + " -S: Put string as an array of char (long string)\n" + "\nExample: caput my_channel 1.2\n" + " (puts 1.2 to my_channel)\n\n" + , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); +} + + +/*+************************************************************************** + * + * Function: put_event_handler + * + * Description: CA event_handler for request type callback + * Sets status flags and marks as done. + * + * Arg(s) In: args - event handler args (see CA manual) + * + **************************************************************************-*/ + +void put_event_handler ( struct event_handler_args args ) +{ + /* Retrieve pv from event handler structure */ + pv* pPv = args.usr; + + /* Store status, then give EPICS event */ + pPv->status = args.status; + epicsEventSignal( epId ); +} + + +/*+************************************************************************** + * + * Function: caget + * + * Description: Issue read request, wait for incoming data + * and print the data + * + * Arg(s) In: pvs - Pointer to an array of pv structures + * nPvs - Number of elements in the pvs array + * format - Output format + * dbrType - Requested dbr type + * reqElems - Requested number of (array) elements + * + * Return(s): Error code: 0 = OK, 1 = Error + * + **************************************************************************-*/ + +int caget (pv *pvs, int nPvs, OutputT format, + chtype dbrType, unsigned long reqElems) +{ + unsigned int i; + int n, result; + + for (n = 0; n < nPvs; n++) { + + /* Set up pvs structure */ + /* -------------------- */ + + /* Get natural type and array count */ + pvs[n].nElems = ca_element_count(pvs[n].chid); + pvs[n].dbfType = ca_field_type(pvs[n].chid); + pvs[n].dbrType = dbrType; + + /* Set up value structures */ + pvs[n].dbrType = dbf_type_to_DBR_TIME(pvs[n].dbfType); /* Use native type */ + if (dbr_type_is_ENUM(pvs[n].dbrType)) /* Enums honour -n option */ + { + if (enumAsNr) pvs[n].dbrType = DBR_TIME_INT; + else pvs[n].dbrType = DBR_TIME_STRING; + } + + if (reqElems == 0 || pvs[n].nElems < reqElems) /* Adjust array count */ + pvs[n].reqElems = pvs[n].nElems; + else + pvs[n].reqElems = reqElems; + + /* Issue CA request */ + /* ---------------- */ + + if (ca_state(pvs[n].chid) == cs_conn) + { + nConn++; + pvs[n].onceConnected = 1; + /* Allocate value structure */ + pvs[n].value = calloc(1, dbr_size_n(pvs[n].dbrType, pvs[n].reqElems)); + if(!pvs[n].value){ + fprintf(stderr,"Allocation failed\n"); + exit(1); + } + result = ca_array_get(pvs[n].dbrType, + pvs[n].reqElems, + pvs[n].chid, + pvs[n].value); + pvs[n].status = result; + } else { + pvs[n].status = ECA_DISCONN; + } + } + if (!nConn) return 1; /* No connection? We're done. */ + + /* Wait for completion */ + /* ------------------- */ + + result = ca_pend_io(caTimeout); + if (result == ECA_TIMEOUT) + fprintf(stderr, "Read operation timed out: PV data was not read.\n"); + + /* Print the data */ + /* -------------- */ + + for (n = 0; n < nPvs; n++) { + + switch (format) { + case plain: /* Emulate old caput behaviour */ + if (pvs[n].reqElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name); + else printf("%s", pvs[n].name); + printf("%c", fieldSeparator); + case terse: + if (pvs[n].status == ECA_DISCONN) + printf("*** not connected\n"); + else if (pvs[n].status == ECA_NORDACCESS) + printf("*** no read access\n"); + else if (pvs[n].status != ECA_NORMAL) + printf("*** CA error %s\n", ca_message(pvs[n].status)); + else if (pvs[n].value == 0) + printf("*** no data available (timeout)\n"); + else + { + if (charArrAsStr && dbr_type_is_CHAR(pvs[n].dbrType) && (reqElems || pvs[n].reqElems > 1)) { + dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pvs[n].value, pvs[n].dbrType); + int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); + char *d = calloc(dlen+1, sizeof(char)); + if(!d){ + fprintf(stderr,"Allocation failed\n"); + exit(1); + } + epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s)); + printf("%s", d); + free(d); + } else { + if (reqElems || pvs[n].nElems > 1) printf("%lu%c", pvs[n].reqElems, fieldSeparator); + for (i=0; i ... + * + * Arg(s) Out: none + * + * Return(s): Standard return code (0=success, 1=error) + * + **************************************************************************-*/ + +int main (int argc, char *argv[]) +{ + int n = 0; + int i; + int result; /* CA result */ + OutputT format = plain; /* User specified format */ + RequestT request = get; /* User specified request type */ + int isArray = 0; /* Flag for array operation */ + int enumAsString = 0; /* Force ENUM values to be strings */ + + int count = 1; + int opt; /* getopt() current option */ + chtype dbrType = DBR_STRING; + char *pend; + EpicsStr *sbuf; + double *dbuf; + char *cbuf = 0; + char *ebuf = 0; + void *pbuf; + int len = 0; + int waitStatus; + struct dbr_gr_enum bufGrEnum; + + int nPvs; /* Number of PVs */ + pv* pvs = 0; /* Array of PV structures */ + + setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ + putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */ + + while ((opt = getopt(argc, argv, ":cnlhatsS#:w:p:F:")) != -1) { + switch (opt) { + case 'h': /* Print usage */ + usage(); + return 0; + case 'n': /* Force interpret ENUM as index number */ + enumAsNr = 1; + enumAsString = 0; + break; + case 's': /* Force interpret ENUM as menu string */ + enumAsString = 1; + enumAsNr = 0; + break; + case 'S': /* Treat char array as (long) string */ + charArrAsStr = 1; + isArray = 0; + break; + case 't': /* Select terse output format */ + format = terse; + break; + case 'l': /* Select long output format */ + format = all; + break; + case 'a': /* Select array mode */ + isArray = 1; + charArrAsStr = 0; + break; + case 'c': /* Select put_callback mode */ + request = callback; + break; + case 'w': /* Set CA timeout value */ + if(epicsScanDouble(optarg, &caTimeout) != 1) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "- ignored. ('caput -h' for help.)\n", optarg); + caTimeout = DEFAULT_TIMEOUT; + } + break; + case '#': /* Array count */ + if (sscanf(optarg,"%d", &count) != 1) + { + fprintf(stderr, "'%s' is not a valid array element count " + "- ignored. ('caput -h' for help.)\n", optarg); + count = 0; + } + break; + case 'p': /* CA priority */ + if (sscanf(optarg,"%u", &caPriority) != 1) + { + fprintf(stderr, "'%s' is not a valid CA priority " + "- ignored. ('caget -h' for help.)\n", optarg); + caPriority = DEFAULT_CA_PRIORITY; + } + if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; + break; + case 'F': /* Store this for output and tool_lib formatting */ + fieldSeparator = (char) *optarg; + break; + case '?': + fprintf(stderr, + "Unrecognized option: '-%c'. ('caput -h' for help.)\n", + optopt); + return 1; + case ':': + fprintf(stderr, + "Option '-%c' requires an argument. ('caput -h' for help.)\n", + optopt); + return 1; + default : + usage(); + return 1; + } + } + + nPvs = argc - optind; /* Remaining arg list are PV names and values */ + + if (nPvs < 1) { + fprintf(stderr, "No pv name specified. ('caput -h' for help.)\n"); + return 1; + } + if (nPvs == 1) { + fprintf(stderr, "No value specified. ('caput -h' for help.)\n"); + return 1; + } + + nPvs = 1; /* One PV - the rest is value(s) */ + + epId = epicsEventCreate(epicsEventEmpty); /* Create empty EPICS event (semaphore) */ + + /* Start up Channel Access */ + + result = ca_context_create(ca_enable_preemptive_callback); + if (result != ECA_NORMAL) { + fprintf(stderr, "CA error %s occurred while trying " + "to start channel access '%s'.\n", ca_message(result), pvs[n].name); + return 1; + } + /* Allocate PV structure array */ + + pvs = calloc (nPvs, sizeof(pv)); + if (!pvs) { + fprintf(stderr, "Memory allocation for channel structure failed.\n"); + return 1; + } + /* Connect channels */ + + pvs[0].name = argv[optind] ; /* Copy PV name from command line */ + + result = connect_pvs(pvs, nPvs); /* If the connection fails, we're done */ + if (result) { + ca_context_destroy(); + return result; + } + + /* Get values from command line */ + optind++; + + if (isArray) { + optind++; /* In case of array skip first value (nr + * of elements) - actual number of values is used */ + count = argc - optind; + + } else { /* Concatenate the remaining line to one string + * (sucks but is compatible to the former version) */ + for (i = optind; i < argc; i++) { + len += strlen(argv[i]); + len++; + } + cbuf = calloc(len, sizeof(char)); + if (!cbuf) { + fprintf(stderr, "Memory allocation failed.\n"); + return 1; + } + strcpy(cbuf, argv[optind]); + + if (argc > optind+1) { + for (i = optind + 1; i < argc; i++) { + strcat(cbuf, " "); + strcat(cbuf, argv[i]); + } + } + + if ((argc - optind) >= 1) + count = 1; + argv[optind] = cbuf; + } + + sbuf = calloc (count, sizeof(EpicsStr)); + dbuf = calloc (count, sizeof(double)); + if(!sbuf || !dbuf) { + fprintf(stderr, "Memory allocation failed\n"); + return 1; + } + + /* ENUM? Special treatment */ + + if (ca_field_type(pvs[0].chid) == DBR_ENUM) { + + /* Get the ENUM strings */ + + result = ca_array_get (DBR_GR_ENUM, 1, pvs[0].chid, &bufGrEnum); + result = ca_pend_io(caTimeout); + if (result == ECA_TIMEOUT) { + fprintf(stderr, "Read operation timed out: ENUM data was not read.\n"); + return 1; + } + + if (enumAsNr) { /* Interpret values as numbers */ + + for (i = 0; i < count; ++i) { + dbuf[i] = epicsStrtod(*(argv+optind+i), &pend); + if (*(argv+optind+i) == pend) { /* Conversion didn't work */ + fprintf(stderr, "Enum index value '%s' is not a number.\n", + *(argv+optind+i)); + return 1; + } + if (dbuf[i] >= bufGrEnum.no_str) { + fprintf(stderr, "Warning: enum index value '%s' may be too large.\n", + *(argv+optind+i)); + } + } + dbrType = DBR_DOUBLE; + + } else { /* Interpret values as strings */ + + for (i = 0; i < count; ++i) { + epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr)); + *( sbuf[i]+sizeof(EpicsStr)-1 ) = '\0'; + dbrType = DBR_STRING; + + /* Compare to ENUM strings */ + for (len = 0; len < bufGrEnum.no_str; len++) + if (!strcmp(sbuf[i], bufGrEnum.strs[len])) + break; + + if (len >= bufGrEnum.no_str) { + /* Not a string? Try as number */ + dbuf[i] = epicsStrtod(sbuf[i], &pend); + if (sbuf[i] == pend || enumAsString) { + fprintf(stderr, "Enum string value '%s' invalid.\n", sbuf[i]); + return 1; + } + if (dbuf[i] >= bufGrEnum.no_str) { + fprintf(stderr, "Warning: enum index value '%s' may be too large.\n", sbuf[i]); + } + dbrType = DBR_DOUBLE; + } + } + } + + } else { /* Not an ENUM */ + + if (charArrAsStr) { + count = len; + dbrType = DBR_CHAR; + ebuf = calloc(strlen(cbuf), sizeof(char)); + if(!ebuf) { + fprintf(stderr, "Memory allocation failed\n"); + return 1; + } + epicsStrnRawFromEscaped(ebuf, strlen(cbuf), cbuf, strlen(cbuf)); + } else { + for (i = 0; i < count; ++i) { + epicsStrnRawFromEscaped(sbuf[i], sizeof(EpicsStr), *(argv+optind+i), sizeof(EpicsStr)); + *( sbuf[i]+sizeof(EpicsStr)-1 ) = '\0'; + } + dbrType = DBR_STRING; + } + } + + /* Read and print old data */ + if (format != terse) { + printf("Old : "); + result = caget(pvs, nPvs, format, 0, 0); + } + + /* Write new data */ + if (dbrType == DBR_STRING) pbuf = sbuf; + else if (dbrType == DBR_CHAR) pbuf = ebuf; + else pbuf = dbuf; + + if (request == callback) { + /* Use callback version of put */ + pvs[0].status = ECA_NORMAL; /* All ok at the moment */ + result = ca_array_put_callback ( + dbrType, count, pvs[0].chid, pbuf, put_event_handler, (void *) pvs); + } else { + /* Use standard put with defined timeout */ + result = ca_array_put (dbrType, count, pvs[0].chid, pbuf); + } + result = ca_pend_io(caTimeout); + if (result == ECA_TIMEOUT) { + fprintf(stderr, "Write operation timed out: Data was not written.\n"); + return 1; + } + if (request == callback) { /* Also wait for callbacks */ + waitStatus = epicsEventWaitWithTimeout( epId, caTimeout ); + if (waitStatus) + fprintf(stderr, "Write callback operation timed out\n"); + + /* retrieve status from callback */ + result = pvs[0].status; + } + + if (result != ECA_NORMAL) { + fprintf(stderr, "Error occured writing data.\n"); + return 1; + } + + /* Read and print new data */ + if (format != terse) + printf("New : "); + + result = caget(pvs, nPvs, format, 0, 0); + + /* Shut down Channel Access */ + ca_context_destroy(); + + return result; +} diff --git a/src/catools/tool_lib.c b/src/catools/tool_lib.c new file mode 100644 index 000000000..13d6330cf --- /dev/null +++ b/src/catools/tool_lib.c @@ -0,0 +1,620 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange (BESSY) + * + * Modification History + * 2009/03/31 Larry Hoff (BNL) + * Added field separators + * 2009/04/01 Ralph Lange (HZB/BESSY) + * Added support for long strings (array of char) and quoting of nonprintable characters + * + */ + +#include +#include +#include + +#define epicsAlarmGLOBAL +#include +#undef epicsAlarmGLOBAL +#include +#include +#include + +#include "tool_lib.h" + +/* Time stamps for program start, first incoming monitor, + previous value (client and server stamp): + used for relative resp. incremental timestamps with monitors */ +static epicsTimeStamp tsStart, tsFirst, tsPreviousC, tsPreviousS; + +static int tsInitS = 0; /* Flag: Server timestamps init'd */ +static int tsInitC = 0; /* Flag: Client timestamps init'd */ + +TimeT tsType = absolute; /* Timestamp type flag (-t option) */ +int tsSrcServer = 1; /* Timestamp source flag (-t option) */ +int tsSrcClient = 0; /* Timestamp source flag (-t option) */ +IntFormatT outTypeI = dec; /* For -0.. output format option */ +IntFormatT outTypeF = dec; /* For -l.. output format option */ + +char dblFormatStr[30] = "%g"; /* Format string to print doubles (-efg options) */ +char timeFormatStr[30] = "%Y-%m-%d %H:%M:%S.%06f"; /* Time format string */ +char fieldSeparator = ' '; /* OFS default is whitespace */ + +int enumAsNr = 0; /* used for -n option - get DBF_ENUM as number */ +int charArrAsStr = 0; /* used for -S option - treat char array as (long) string */ +double caTimeout = 1.0; /* wait time default (see -w option) */ +capri caPriority = DEFAULT_CA_PRIORITY; /* CA Priority */ + +#define TIMETEXTLEN 28 /* Length of timestamp text buffer */ + + + +void sprint_long (char *ret, long val, IntFormatT outType) +{ + long i, bit, skip=-1L; /* used only for printing bits */ + switch (outType) { + case hex: sprintf(ret, "0x%lX", val); break; + case oct: sprintf(ret, "0o%lo", val); break; + case bin: + for (i=31; i>=0 ; i--) + { + bit = (val>>i) & 0x1L; + if (skip<0 && bit) + { + skip = 31 - i; /* skip leading 0's */ + ret[i+1] = '\0'; + } + if (skip >= 0) + { + ret[31-i-skip] = (bit) ? '1' : '0'; + } + } + break; + default: sprintf(ret, "%ld", val); /* decimal */ + } +} + + + +/*+************************************************************************** + * + * Function: val2str + * + * Description: Print (convert) value to a string + * + * Arg(s) In: v - Pointer to dbr_... structure + * type - Numeric dbr type + * index - Index of element to print (for arrays) + * + * Return(s): Pointer to static output string + * + **************************************************************************-*/ + +char *val2str (const void *v, unsigned type, int index) +{ +#define STR 500 + static char str[STR]; + char ch; + void *val_ptr; + unsigned base_type; + dbr_long_t val_long; + + if (!dbr_type_is_valid(type)) { + strcpy (str, "*** invalid type"); + return str; + } + + base_type = type % (LAST_TYPE+1); + + if (type == DBR_STSACK_STRING || type == DBR_CLASS_NAME) + base_type = DBR_STRING; + + val_ptr = dbr_value_ptr(v, type); + + switch (base_type) { + case DBR_STRING: + epicsStrnEscapedFromRaw(str, STR, ((dbr_string_t*) val_ptr)[index], strlen(((dbr_string_t*) val_ptr)[index])); + break; + case DBR_FLOAT: + if (outTypeF == dec) { + sprintf(str, dblFormatStr, ((dbr_float_t*) val_ptr)[index]); + } else { + if (((dbr_float_t*) val_ptr)[index] > 0.0) + val_long = ((dbr_float_t*) val_ptr)[index] + 0.5; + else + val_long = ((dbr_float_t*) val_ptr)[index] - 0.5; + sprint_long(str, val_long, outTypeF); + } + break; + case DBR_DOUBLE: + if (outTypeF == dec) { + sprintf(str, dblFormatStr, ((dbr_double_t*) val_ptr)[index]); + } else { + if (((dbr_double_t*) val_ptr)[index] > 0.0) + val_long = ((dbr_double_t*) val_ptr)[index] + 0.5; + else + val_long = ((dbr_double_t*) val_ptr)[index] - 0.5; + sprint_long(str, val_long, outTypeF); + } + break; + case DBR_CHAR: + ch = ((dbr_char_t*) val_ptr)[index]; + sprintf(str, "%d", ch); + break; + case DBR_INT: + sprint_long(str, ((dbr_int_t*) val_ptr)[index], outTypeI); + break; + case DBR_LONG: + sprint_long(str, ((dbr_long_t*) val_ptr)[index], outTypeI); + break; + case DBR_ENUM: + { + dbr_enum_t *val = (dbr_enum_t *)val_ptr; + if (dbr_type_is_GR(type) && !enumAsNr) + sprintf(str, "%s", ((struct dbr_gr_enum *)v)->strs[val[index]]); + else if (dbr_type_is_CTRL(type) && !enumAsNr) + sprintf(str, "%s", ((struct dbr_ctrl_enum *)v)->strs[val[index]]); + else + sprintf(str, "%d", val[index]); + } + } + return str; +} + + + +/*+************************************************************************** + * + * Function: dbr2str + * + * Description: Print (convert) additional information contained in dbr_... + * + * Arg(s) In: value - Pointer to dbr_... structure + * type - Numeric dbr type + * + * Return(s): Pointer to static output string + * + **************************************************************************-*/ + +/* Definitions for sprintf format strings and matching argument lists */ + +#define FMT_TIME \ + " Timestamp: %s" + +#define ARGS_TIME(T) \ + timeText + +#define FMT_STS \ + " Status: %s\n" \ + " Severity: %s" + +#define ARGS_STS(T) \ + stat_to_str(((struct T *)value)->status), \ + sevr_to_str(((struct T *)value)->severity) + +#define ARGS_STS_UNSIGNED(T) \ + stat_to_str_unsigned(((struct T *)value)->status), \ + sevr_to_str_unsigned(((struct T *)value)->severity) + +#define FMT_ACK \ + " Ack transient?: %s\n" \ + " Ack severity: %s" + +#define ARGS_ACK(T) \ + ((struct T *)value)->ackt ? "YES" : "NO", \ + sevr_to_str_unsigned(((struct T *)value)->acks) + +#define FMT_UNITS \ + " Units: %s" + +#define ARGS_UNITS(T) \ + ((struct T *)value)->units + +#define FMT_PREC \ + " Precision: %d" + +#define ARGS_PREC(T) \ + ((struct T *)value)->precision + +#define FMT_GR(FMT) \ + " Lo disp limit: " #FMT "\n" \ + " Hi disp limit: " #FMT "\n" \ + " Lo alarm limit: " #FMT "\n" \ + " Lo warn limit: " #FMT "\n" \ + " Hi warn limit: " #FMT "\n" \ + " Hi alarm limit: " #FMT + +#define ARGS_GR(T,F) \ + (F)((struct T *)value)->lower_disp_limit, \ + (F)((struct T *)value)->upper_disp_limit, \ + (F)((struct T *)value)->lower_alarm_limit, \ + (F)((struct T *)value)->lower_warning_limit, \ + (F)((struct T *)value)->upper_warning_limit, \ + (F)((struct T *)value)->upper_alarm_limit + +#define FMT_CTRL(FMT) \ + " Lo ctrl limit: " #FMT "\n" \ + " Hi ctrl limit: " #FMT + +#define ARGS_CTRL(T,F) \ + (F)((struct T *)value)->lower_ctrl_limit, \ + (F)((struct T *)value)->upper_ctrl_limit + + +/* Definitions for the actual sprintf calls */ + +#define PRN_DBR_STS(T) \ + sprintf(str, \ + FMT_STS, \ + ARGS_STS(T)) + +#define PRN_DBR_TIME(T) \ + epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, \ + &(((struct T *)value)->stamp)); \ + sprintf(str, \ + FMT_TIME "\n" FMT_STS, \ + ARGS_TIME(T), ARGS_STS(T)) + +#define PRN_DBR_GR(T,F,FMT) \ + sprintf(str, \ + FMT_STS "\n" FMT_UNITS "\n" FMT_GR(FMT), \ + ARGS_STS(T), ARGS_UNITS(T), ARGS_GR(T,F)) + +#define PRN_DBR_GR_PREC(T,F,FMT) \ + sprintf(str, \ + FMT_STS "\n" FMT_UNITS "\n" FMT_PREC "\n" FMT_GR(FMT), \ + ARGS_STS(T), ARGS_UNITS(T), ARGS_PREC(T), ARGS_GR(T,F)) + +#define PRN_DBR_CTRL(T,F,FMT) \ + sprintf(str, \ + FMT_STS "\n" FMT_UNITS "\n" FMT_GR(FMT) "\n" FMT_CTRL(FMT), \ + ARGS_STS(T), ARGS_UNITS(T), ARGS_GR(T,F), ARGS_CTRL(T,F)) + +#define PRN_DBR_CTRL_PREC(T,F,FMT) \ + sprintf(str, \ + FMT_STS "\n" FMT_UNITS "\n" FMT_PREC "\n" FMT_GR(FMT) "\n" FMT_CTRL(FMT), \ + ARGS_STS(T), ARGS_UNITS(T), ARGS_PREC(T), ARGS_GR(T,F), ARGS_CTRL(T,F)) + +#define PRN_DBR_STSACK(T) \ + sprintf(str, \ + FMT_STS "\n" FMT_ACK, \ + ARGS_STS_UNSIGNED(T), ARGS_ACK(T)) + +#define PRN_DBR_X_ENUM(T) \ + n = ((struct T *)value)->no_str; \ + PRN_DBR_STS(T); \ + sprintf(str+strlen(str), \ + "\n Enums: (%2d)", n); \ + for (i=0; istrs[i]); + + +/* Make a good guess how long the dbr_... stuff might get as worst case */ +#define DBR_PRINT_BUFFER_SIZE \ + 50 /* timestamp */ \ + + 2 * 30 /* status / Severity */ \ + + 2 * 30 /* acks / ackt */ \ + + 20 + MAX_UNITS_SIZE /* units */ \ + + 30 /* precision */ \ + + 6 * 45 /* graphic limits */ \ + + 2 * 45 /* control limits */ \ + + 30 + (MAX_ENUM_STATES * (20 + MAX_ENUM_STRING_SIZE)) /* enums */ \ + + 50 /* just to be sure */ + +char *dbr2str (const void *value, unsigned type) +{ + static char str[DBR_PRINT_BUFFER_SIZE]; + char timeText[TIMETEXTLEN]; + int n, i; + + switch (type) { + case DBR_STRING: /* no additional information for basic data types */ + case DBR_INT: + case DBR_FLOAT: + case DBR_ENUM: + case DBR_CHAR: + case DBR_LONG: + case DBR_DOUBLE: break; + + case DBR_CTRL_STRING: /* see db_access.h: not implemented */ + case DBR_GR_STRING: /* see db_access.h: not implemented */ + case DBR_STS_STRING: PRN_DBR_STS(dbr_sts_string); break; + case DBR_STS_SHORT: PRN_DBR_STS(dbr_sts_short); break; + case DBR_STS_FLOAT: PRN_DBR_STS(dbr_sts_float); break; + case DBR_STS_ENUM: PRN_DBR_STS(dbr_sts_enum); break; + case DBR_STS_CHAR: PRN_DBR_STS(dbr_sts_char); break; + case DBR_STS_LONG: PRN_DBR_STS(dbr_sts_long); break; + case DBR_STS_DOUBLE: PRN_DBR_STS(dbr_sts_double); break; + + case DBR_TIME_STRING: PRN_DBR_TIME(dbr_time_string); break; + case DBR_TIME_SHORT: PRN_DBR_TIME(dbr_time_short); break; + case DBR_TIME_FLOAT: PRN_DBR_TIME(dbr_time_float); break; + case DBR_TIME_ENUM: PRN_DBR_TIME(dbr_time_enum); break; + case DBR_TIME_CHAR: PRN_DBR_TIME(dbr_time_char); break; + case DBR_TIME_LONG: PRN_DBR_TIME(dbr_time_long); break; + case DBR_TIME_DOUBLE: PRN_DBR_TIME(dbr_time_double); break; + + case DBR_GR_CHAR: + PRN_DBR_GR(dbr_gr_char, char, %8d); break; + case DBR_GR_INT: + PRN_DBR_GR(dbr_gr_int, int, %8d); break; + case DBR_GR_LONG: + PRN_DBR_GR(dbr_gr_long, long int, %8ld); break; + case DBR_GR_FLOAT: + PRN_DBR_GR_PREC(dbr_gr_float, float, %g); break; + case DBR_GR_DOUBLE: + PRN_DBR_GR_PREC(dbr_gr_double, double, %g); break; + case DBR_GR_ENUM: + PRN_DBR_X_ENUM(dbr_gr_enum); break; + case DBR_CTRL_CHAR: + PRN_DBR_CTRL(dbr_ctrl_char, char, %8d); break; + case DBR_CTRL_INT: + PRN_DBR_CTRL(dbr_ctrl_int, int, %8d); break; + case DBR_CTRL_LONG: + PRN_DBR_CTRL(dbr_ctrl_long, long int, %8ld); break; + case DBR_CTRL_FLOAT: + PRN_DBR_CTRL_PREC(dbr_ctrl_float, float, %g); break; + case DBR_CTRL_DOUBLE: + PRN_DBR_CTRL_PREC(dbr_ctrl_double, double, %g); break; + case DBR_CTRL_ENUM: + PRN_DBR_X_ENUM(dbr_ctrl_enum); break; + case DBR_STSACK_STRING: + PRN_DBR_STSACK(dbr_stsack_string); break; + default : strcpy (str, "can't print data type"); + } + return str; +} + + + +/*+************************************************************************** + * + * Function: print_time_val_sts + * + * Description: Print (to stdout) one wide output line + * (name, timestamp, value, status, severity) + * + * Arg(s) In: pv - Pointer to pv structure + * nElems - Number of elements (array) + * + **************************************************************************-*/ + +#define PRN_TIME_VAL_STS(TYPE,TYPE_ENUM) \ + printAbs = !pv->firstStampPrinted; \ + \ + ptsNewS = &((struct TYPE *)value)->stamp; \ + ptsNewC = &tsNow; \ + \ + switch (tsType) { \ + case relative: \ + ptsRefC = &tsStart; \ + ptsRefS = &tsFirst; \ + break; \ + case incremental: \ + ptsRefC = &tsPreviousC; \ + ptsRefS = &tsPreviousS; \ + break; \ + case incrementalByChan: \ + ptsRefC = &pv->tsPreviousC; \ + ptsRefS = &pv->tsPreviousS; \ + break; \ + default : \ + printAbs = 1; \ + } \ + \ + if (printAbs) { \ + if (tsSrcServer) { \ + epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, ptsNewS); \ + printf("%s", timeText); \ + } \ + if (tsSrcClient) { \ + epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, ptsNewC); \ + printf("(%s)", timeText); \ + } \ + pv->firstStampPrinted = 1; \ + } else { \ + if (tsSrcServer) { \ + printf(" %+12.6f", epicsTimeDiffInSeconds(ptsNewS, ptsRefS) ); \ + } \ + if (tsSrcClient) { \ + printf(" (%+12.6f)", epicsTimeDiffInSeconds(ptsNewC, ptsRefC) ); \ + } \ + } \ + \ + if (tsType == incrementalByChan) { \ + pv->tsPreviousC = *ptsNewC; \ + pv->tsPreviousS = *ptsNewS; \ + } \ + \ + tsPreviousC = *ptsNewC; \ + tsPreviousS = *ptsNewS; \ + \ + if (charArrAsStr && dbr_type_is_CHAR(TYPE_ENUM) && (reqElems || pv->nElems > 1)) { \ + dbr_char_t *s = (dbr_char_t*) dbr_value_ptr(pv->value, pv->dbrType); \ + int dlen = epicsStrnEscapedFromRawSize((char*)s, strlen((char*)s)); \ + char *d = calloc(dlen+1, sizeof(char)); \ + if(d) { \ + epicsStrnEscapedFromRaw(d, dlen+1, (char*)s, strlen((char*)s));\ + printf("%c%s", fieldSeparator, d); \ + free(d); \ + } else { \ + printf("Failed to allocate for print_time_val_sts\n"); \ + } \ + } else { \ + if (reqElems || pv->nElems > 1) printf("%c%lu", fieldSeparator, pv->nElems); \ + for (i=0; inElems; ++i) { \ + printf("%c%s", fieldSeparator, val2str(value, TYPE_ENUM, i)); \ + } \ + } \ + /* Print Status, Severity - if not NO_ALARM */ \ + if ( ((struct TYPE *)value)->status || ((struct TYPE *)value)->severity ) \ + { \ + printf("%c%s%c%s\n", \ + fieldSeparator, \ + stat_to_str(((struct TYPE *)value)->status), \ + fieldSeparator, \ + sevr_to_str(((struct TYPE *)value)->severity)); \ + } else { \ + printf("%c%c\n", \ + fieldSeparator, fieldSeparator); \ + } + + +void print_time_val_sts (pv* pv, unsigned long reqElems) +{ + char timeText[2*TIMETEXTLEN+2]; + int i, printAbs; + void* value = pv->value; + epicsTimeStamp *ptsRefC, *ptsRefS; /* Reference timestamps (client, server) */ + epicsTimeStamp *ptsNewC, *ptsNewS; /* Update timestamps (client, server) */ + epicsTimeStamp tsNow; + + epicsTimeGetCurrent(&tsNow); + epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, &tsNow); + + if (!tsInitS) + { + tsFirst = tsNow; + tsInitS = 1; + } + + if (pv->nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pv->name); + else printf("%s", pv->name); + printf("%c", fieldSeparator); + if (!pv->onceConnected) + printf("*** Not connected (PV not found)\n"); + else if (pv->status == ECA_DISCONN) + printf("%s *** disconnected\n", timeText); + else if (pv->status == ECA_NORDACCESS) + printf("%s *** no read access\n", timeText); + else if (pv->status != ECA_NORMAL) + printf("%s *** CA error %s\n", timeText, ca_message(pv->status)); + else if (pv->value == 0) + printf("%s *** no data available (timeout)\n", timeText); + else + switch (pv->dbrType) { + case DBR_TIME_STRING: + PRN_TIME_VAL_STS(dbr_time_string, DBR_TIME_STRING); + break; + case DBR_TIME_SHORT: + PRN_TIME_VAL_STS(dbr_time_short, DBR_TIME_SHORT); + break; + case DBR_TIME_FLOAT: + PRN_TIME_VAL_STS(dbr_time_float, DBR_TIME_FLOAT); + break; + case DBR_TIME_ENUM: + PRN_TIME_VAL_STS(dbr_time_enum, DBR_TIME_ENUM); + break; + case DBR_TIME_CHAR: + PRN_TIME_VAL_STS(dbr_time_char, DBR_TIME_CHAR); + break; + case DBR_TIME_LONG: + PRN_TIME_VAL_STS(dbr_time_long, DBR_TIME_LONG); + break; + case DBR_TIME_DOUBLE: + PRN_TIME_VAL_STS(dbr_time_double, DBR_TIME_DOUBLE); + break; + default: printf("can't print data type\n"); + } +} + + +/*+************************************************************************** + * + * Function: create_pvs + * + * Description: Creates an arbitrary number of PVs + * + * Arg(s) In: pvs - Pointer to an array of pv structures + * nPvs - Number of elements in the pvs array + * pCB - Connection state change callback + * + * Arg(s) Out: none + * + * Return(s): Error code: + * 0 - All PVs created + * 1 - Some PV(s) not created + * + **************************************************************************-*/ + +int create_pvs (pv* pvs, int nPvs, caCh *pCB) +{ + int n; + int result; + int returncode = 0; + + if (!tsInitC) /* Initialize start timestamp */ + { + epicsTimeGetCurrent(&tsStart); + tsInitC = 1; + } + /* Issue channel connections */ + for (n = 0; n < nPvs; n++) { + result = ca_create_channel (pvs[n].name, + pCB, + &pvs[n], + caPriority, + &pvs[n].chid); + if (result != ECA_NORMAL) { + fprintf(stderr, "CA error %s occurred while trying " + "to create channel '%s'.\n", ca_message(result), pvs[n].name); + pvs[n].status = result; + returncode = 1; + } + } + + return returncode; +} + + +/*+************************************************************************** + * + * Function: connect_pvs + * + * Description: Connects an arbitrary number of PVs + * + * Arg(s) In: pvs - Pointer to an array of pv structures + * nPvs - Number of elements in the pvs array + * + * Arg(s) Out: none + * + * Return(s): Error code: + * 0 - All PVs connected + * 1 - Some PV(s) not connected + * + **************************************************************************-*/ + +int connect_pvs (pv* pvs, int nPvs) +{ + int returncode = create_pvs ( pvs, nPvs, 0); + if ( returncode == 0 ) { + /* Wait for channels to connect */ + int result = ca_pend_io (caTimeout); + if (result == ECA_TIMEOUT) + { + if (nPvs > 1) + { + fprintf(stderr, "Channel connect timed out: some PV(s) not found.\n"); + } else { + fprintf(stderr, "Channel connect timed out: '%s' not found.\n", + pvs[0].name); + } + returncode = 1; + } + } + return returncode; +} diff --git a/src/catools/tool_lib.h b/src/catools/tool_lib.h new file mode 100644 index 000000000..baebf7939 --- /dev/null +++ b/src/catools/tool_lib.h @@ -0,0 +1,99 @@ +/*************************************************************************\ +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* 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. +\*************************************************************************/ + +/* + * Author: Ralph Lange (BESSY) + * + * Modification History + * 2009/03/31 Larry Hoff (BNL) + * Added field separators + * + */ + +#ifndef INCLtool_libh +#define INCLtool_libh + +#include + +/* Convert status and severity to strings */ +#define stat_to_str(stat) \ + ((stat) >= 0 && (stat) <= (signed)lastEpicsAlarmCond) ? \ + epicsAlarmConditionStrings[stat] : "??" + +#define sevr_to_str(stat) \ + ((stat) >= 0 && (stat) <= (signed)lastEpicsAlarmSev) ? \ + epicsAlarmSeverityStrings[stat] : "??" + +#define stat_to_str_unsigned(stat) \ + ((stat) <= lastEpicsAlarmCond) ? \ + epicsAlarmConditionStrings[stat] : "??" + +#define sevr_to_str_unsigned(stat) \ + ((stat) <= lastEpicsAlarmSev) ? \ + epicsAlarmSeverityStrings[stat] : "??" + +/* The different versions are necessary because stat and sevr are + * defined unsigned in CA's DBR_STSACK structure and signed in all the + * others. Some compilers generate warnings if you check an unsigned + * being >=0 */ + + +#define DEFAULT_CA_PRIORITY 0 /* Default CA priority */ +#define DEFAULT_TIMEOUT 1.0 /* Default CA timeout */ + + +/* Type of timestamp */ +typedef enum { absolute, relative, incremental, incrementalByChan } TimeT; + +/* Output formats for integer data types */ +typedef enum { dec, bin, oct, hex } IntFormatT; + +/* Structure representing one PV (= channel) */ +typedef struct +{ + char* name; + chid chid; + long dbfType; + long dbrType; + unsigned long nElems; // True length of data in value + unsigned long reqElems; // Requested length of data + int status; + void* value; + epicsTimeStamp tsPreviousC; + epicsTimeStamp tsPreviousS; + char firstStampPrinted; + char onceConnected; +} pv; + + +extern TimeT tsType; /* Timestamp type flag (-t option) */ +extern int tsSrcServer; /* Timestamp source flag (-t option) */ +extern int tsSrcClient; /* Timestamp source flag (-t option) */ +extern IntFormatT outTypeI; /* Flag used for -0.. output format option */ +extern IntFormatT outTypeF; /* Flag used for -l.. output format option */ +extern int enumAsNr; /* Used for -n option (get DBF_ENUM as number) */ +extern int charArrAsStr; /* used for -S option - treat char array as (long) string */ +extern double caTimeout; /* Wait time default (see -w option) */ +extern char dblFormatStr[]; /* Format string to print doubles (see -e -f option) */ +extern char fieldSeparator; /* Output field separator */ +extern capri caPriority; /* CA priority */ + +extern char *val2str (const void *v, unsigned type, int index); +extern char *dbr2str (const void *value, unsigned type); +extern void print_time_val_sts (pv *pv, unsigned long reqElems); +extern int create_pvs (pv *pvs, int nPvs, caCh *pCB ); +extern int connect_pvs (pv *pvs, int nPvs ); + +/* + * no additions below this endif + */ +#endif /* ifndef INCLtool_libh */ diff --git a/src/db/Makefile b/src/db/Makefile new file mode 100644 index 000000000..0ebdffdb6 --- /dev/null +++ b/src/db/Makefile @@ -0,0 +1,97 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../.. +include $(TOP)/configure/CONFIG + +# includes to install from this sub-project +# +INC += callback.h +INC += dbAccess.h +INC += dbAccessDefs.h +INC += dbCa.h +INC += dbAddr.h +INC += dbBkpt.h +INC += dbConvert.h +INC += dbConvertFast.h +INC += dbEvent.h +INC += dbLock.h +INC += dbNotify.h +INC += dbScan.h +INC += dbTest.h +INC += dbCaTest.h +INC += db_test.h +INC += db_field_log.h +INC += initHooks.h +INC += recGbl.h +INC += dbIocRegister.h +# The following go away what old database access goes away +INC += db_access_routines.h +INC += db_convert.h + +DBDINC += menuAlarmSevr +DBDINC += menuAlarmStat +DBDINC += menuCompress +DBDINC += menuFtype +DBDINC += menuIvoa +DBDINC += menuOmsl +DBDINC += menuPini +DBDINC += menuPriority +DBDINC += menuScan +DBDINC += menuYesNo +DBDINC += menuSimm + +DBDINC += dbCommon +DBD+= menuGlobal.dbd + +LIB_SRCS += dbLock.c +LIB_SRCS += dbAccess.c +LIB_SRCS += dbBkpt.c +LIB_SRCS += dbConvert.c +LIB_SRCS += dbFastLinkConv.c +LIB_SRCS += dbNotify.c +LIB_SRCS += dbScan.c +LIB_SRCS += dbEvent.c +LIB_SRCS += dbTest.c +LIB_SRCS += db_access.c +LIB_SRCS += db_test.c +LIB_SRCS += recGbl.c +LIB_SRCS += callback.c +LIB_SRCS += dbCa.c +LIB_SRCS += dbCaTest.c +LIB_SRCS += initHooks.c +LIB_SRCS += cvtBpt.c +LIB_SRCS += dbContext.cpp +LIB_SRCS += dbChannelIO.cpp +LIB_SRCS += dbSubscriptionIO.cpp +LIB_SRCS += dbPutNotifyBlocker.cpp +LIB_SRCS += dbContextReadNotifyCache.cpp +LIB_SRCS += templateInstances.cpp +LIB_SRCS += dbIocRegister.c + +LIBRARY_IOC = dbIoc +dbIoc_LIBS = dbStaticIoc ca Com + +dbIoc_RCS = dbIoc.rc + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=dbIoc +OBJLIB_SRCS = $(LIB_SRCS) +endif + +include $(TOP)/configure/RULES + +dbCommon.h$(DEP): ../dbCommonRecord.dbd ../dbCommon.dbd + @$(RM) $@ + @-$(MKMF) -m $@ .. $(COMMON_DIR)/dbCommon.h $< + +$(COMMON_DIR)/dbCommon.h: ../dbCommonRecord.dbd ../dbCommon.dbd + $(RM) $@ + $(DBTORECORDTYPEH) -I .. $< $@ + diff --git a/src/db/callback.c b/src/db/callback.c new file mode 100644 index 000000000..c3d4c59fe --- /dev/null +++ b/src/db/callback.c @@ -0,0 +1,233 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* callback.c */ + +/* general purpose callback tasks */ +/* + * Original Author: Marty Kraimer + * Date: 07-18-91 +*/ + +#include +#include +#include + +#include "cantProceed.h" +#include "dbDefs.h" +#include "epicsEvent.h" +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsInterrupt.h" +#include "epicsTimer.h" +#include "epicsRingPointer.h" +#include "errlog.h" +#include "dbStaticLib.h" +#include "dbBase.h" +#include "link.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "taskwd.h" +#include "errMdef.h" +#include "dbCommon.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbAccessDefs.h" +#include "dbLock.h" +#include "callback.h" + + +static epicsThreadOnceId callbackOnceFlag = EPICS_THREAD_ONCE_INIT; +static int callbackQueueSize = 2000; +static epicsEventId callbackSem[NUM_CALLBACK_PRIORITIES]; +static epicsRingPointerId callbackQ[NUM_CALLBACK_PRIORITIES]; +static volatile int ringOverflow[NUM_CALLBACK_PRIORITIES]; + +/* Timer for Delayed Requests */ +static epicsTimerQueueId timerQueue; + +/* Shutdown handling */ +static epicsEventId startStopEvent; +static void *exitCallback; + +/* Static data */ +static char *threadName[NUM_CALLBACK_PRIORITIES] = { + "cbLow", "cbMedium", "cbHigh" +}; +static unsigned int threadPriority[NUM_CALLBACK_PRIORITIES] = { + epicsThreadPriorityScanLow - 1, + epicsThreadPriorityScanLow + 4, + epicsThreadPriorityScanHigh + 1 +}; +static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2}; + + +int callbackSetQueueSize(int size) +{ + if (callbackOnceFlag != EPICS_THREAD_ONCE_INIT) { + errlogPrintf("Callback system already initialized\n"); + return -1; + } + callbackQueueSize = size; + return 0; +} + +static void callbackTask(void *arg) +{ + int priority = *(int *)arg; + + taskwdInsert(0, NULL, NULL); + epicsEventSignal(startStopEvent); + + while(TRUE) { + void *ptr; + epicsEventMustWait(callbackSem[priority]); + while((ptr = epicsRingPointerPop(callbackQ[priority]))) { + CALLBACK *pcallback = (CALLBACK *)ptr; + if (ptr == &exitCallback) goto shutdown; + ringOverflow[priority] = FALSE; + (*pcallback->callback)(pcallback); + } + } + +shutdown: + taskwdRemove(0); + epicsEventSignal(startStopEvent); +} + +static void callbackShutdown(void *arg) +{ + int i; + + for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { + int lockKey = epicsInterruptLock(); + int ok = epicsRingPointerPush(callbackQ[i], &exitCallback); + epicsInterruptUnlock(lockKey); + epicsEventSignal(callbackSem[i]); + if (ok) epicsEventWait(startStopEvent); + } +} + +static void callbackInitOnce(void *arg) +{ + int i; + + startStopEvent = epicsEventMustCreate(epicsEventEmpty); + timerQueue = epicsTimerQueueAllocate(0,epicsThreadPriorityScanHigh); + for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) { + epicsThreadId tid; + + callbackSem[i] = epicsEventMustCreate(epicsEventEmpty); + callbackQ[i] = epicsRingPointerCreate(callbackQueueSize); + if (callbackQ[i] == 0) + cantProceed("epicsRingPointerCreate failed for %s\n", + threadName[i]); + ringOverflow[i] = FALSE; + tid = epicsThreadCreate(threadName[i], threadPriority[i], + epicsThreadGetStackSize(epicsThreadStackBig), + (EPICSTHREADFUNC)callbackTask, &priorityValue[i]); + if (tid == 0) + cantProceed("Failed to spawn callback task %s\n", threadName[i]); + else + epicsEventWait(startStopEvent); + } + epicsAtExit(callbackShutdown, NULL); +} + +void callbackInit(void) +{ + epicsThreadOnce(&callbackOnceFlag, callbackInitOnce, NULL); +} + +/* This routine can be called from interrupt context */ +void callbackRequest(CALLBACK *pcallback) +{ + int priority; + int pushOK; + int lockKey; + + if (!pcallback) { + epicsPrintf("callbackRequest called with NULL pcallback\n"); + return; + } + priority = pcallback->priority; + if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) { + epicsPrintf("callbackRequest called with invalid priority\n"); + return; + } + if (ringOverflow[priority]) return; + + lockKey = epicsInterruptLock(); + pushOK = epicsRingPointerPush(callbackQ[priority], pcallback); + epicsInterruptUnlock(lockKey); + + if (!pushOK) { + errlogPrintf("callbackRequest: %s ring buffer full\n", + threadName[priority]); + ringOverflow[priority] = TRUE; + } + epicsEventSignal(callbackSem[priority]); +} + +static void ProcessCallback(CALLBACK *pcallback) +{ + dbCommon *pRec; + + callbackGetUser(pRec, pcallback); + if (!pRec) return; + dbScanLock(pRec); + (*pRec->rset->process)(pRec); + dbScanUnlock(pRec); +} + +void callbackSetProcess(CALLBACK *pcallback, int Priority, void *pRec) +{ + callbackSetCallback(ProcessCallback, pcallback); + callbackSetPriority(Priority, pcallback); + callbackSetUser(pRec, pcallback); +} + +void callbackRequestProcessCallback(CALLBACK *pcallback, + int Priority, void *pRec) +{ + callbackSetProcess(pcallback, Priority, pRec); + callbackRequest(pcallback); +} + +static void notify(void *pPrivate) +{ + CALLBACK *pcallback = (CALLBACK *)pPrivate; + callbackRequest(pcallback); +} + +void callbackRequestDelayed(CALLBACK *pcallback, double seconds) +{ + epicsTimerId timer = (epicsTimerId)pcallback->timer; + + if (timer == 0) { + timer = epicsTimerQueueCreateTimer(timerQueue, notify, pcallback); + pcallback->timer = timer; + } + epicsTimerStartDelay(timer, seconds); +} + +void callbackCancelDelayed(CALLBACK *pcallback) +{ + epicsTimerId timer = (epicsTimerId)pcallback->timer; + + if (timer != 0) { + epicsTimerCancel(timer); + } +} + +void callbackRequestProcessCallbackDelayed(CALLBACK *pcallback, + int Priority, void *pRec, double seconds) +{ + callbackSetProcess(pcallback, Priority, pRec); + callbackRequestDelayed(pcallback, seconds); +} diff --git a/src/db/callback.h b/src/db/callback.h new file mode 100644 index 000000000..f3513c91b --- /dev/null +++ b/src/db/callback.h @@ -0,0 +1,75 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is 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 */ + +/* includes for general purpose callback tasks */ +/* + * Original Author: Marty Kraimer + * Date: 07-18-91 +*/ + +#ifndef INCcallbackh +#define INCcallbackh 1 + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * WINDOWS also has a "CALLBACK" type def + */ +#ifdef _WIN32 +# ifdef CALLBACK +# undef CALLBACK +# endif /*CALLBACK*/ +#endif /*_WIN32*/ + +#define NUM_CALLBACK_PRIORITIES 3 +#define priorityLow 0 +#define priorityMedium 1 +#define priorityHigh 2 + +typedef struct callbackPvt { + void (*callback)(struct callbackPvt*); + int priority; + void *user; /*for use by callback user*/ + void *timer; /*for use by callback itself*/ +}CALLBACK; + +typedef void (*CALLBACKFUNC)(struct callbackPvt*); + +#define callbackSetCallback(PFUN,PCALLBACK)\ +( (PCALLBACK)->callback = (PFUN) ) +#define callbackSetPriority(PRIORITY,PCALLBACK)\ +( (PCALLBACK)->priority = (PRIORITY) ) +#define callbackSetUser(USER,PCALLBACK)\ +( (PCALLBACK)->user = (void *)(USER) ) +#define callbackGetUser(USER,PCALLBACK)\ +( (USER) = (void *)((CALLBACK *)(PCALLBACK))->user ) + +epicsShareFunc void callbackInit(void); +epicsShareFunc void callbackRequest(CALLBACK *pCallback); +epicsShareFunc void callbackSetProcess( + CALLBACK *pcallback, int Priority, void *pRec); +epicsShareFunc void callbackRequestProcessCallback( + CALLBACK *pCallback,int Priority, void *pRec); +epicsShareFunc void callbackRequestDelayed( + CALLBACK *pCallback,double seconds); +epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback); +epicsShareFunc void callbackRequestProcessCallbackDelayed( + CALLBACK *pCallback, int Priority, void *pRec, double seconds); +epicsShareFunc int callbackSetQueueSize(int size); + +#ifdef __cplusplus +} +#endif + +#endif /*INCcallbackh*/ diff --git a/src/db/cvtBpt.c b/src/db/cvtBpt.c new file mode 100644 index 000000000..d19111a3e --- /dev/null +++ b/src/db/cvtBpt.c @@ -0,0 +1,203 @@ +/*************************************************************************\ +* 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 */ + +/* cvtBpt.c - Convert using breakpoint table + * + * Author: Marty Kraimer + * Date: 04OCT95 + * This is adaptation of old bldCvtTable + */ + +#include "dbBase.h" +#include "dbStaticLib.h" +#include "epicsPrint.h" + +#define epicsExportSharedSymbols +#include "dbAccess.h" +#include "cvtTable.h" + +static brkTable *findBrkTable(short linr) +{ + dbMenu *pdbMenu; + + pdbMenu = dbFindMenu(pdbbase,"menuConvert"); + if (!pdbMenu) { + epicsPrintf("findBrkTable: menuConvert not loaded!\n"); + return NULL; + } + if (linr < 0 || linr >= pdbMenu->nChoice) { + epicsPrintf("findBrkTable: linr=%d but menuConvert only has %d choices\n", + linr,pdbMenu->nChoice); + return NULL; + } + return dbFindBrkTable(pdbbase,pdbMenu->papChoiceValue[linr]); +} + +/* Used by both ao and ai record types */ +long epicsShareAPI cvtRawToEngBpt(double *pval, short linr, short init, + void **ppbrk, short *plbrk) +{ + double val = *pval; + long status = 0; + brkTable *pbrkTable; + brkInt *pInt, *nInt; + short lbrk; + int number; + + if (linr < 2) + return -1; + + if (init || *ppbrk == NULL) { + pbrkTable = findBrkTable(linr); + if (!pbrkTable) + return S_dbLib_badField; + + *ppbrk = (void *)pbrkTable; + *plbrk = 0; + } else + pbrkTable = (brkTable *)*ppbrk; + + number = pbrkTable->number; + lbrk = *plbrk; + + /* Limit index to the size of the table */ + if (lbrk < 0) + lbrk = 0; + else if (lbrk > number-2) + lbrk = number-2; + + pInt = & pbrkTable->paBrkInt[lbrk]; + nInt = pInt + 1; + + if (nInt->raw > pInt->raw) { + /* raw values increase down the table */ + while (val > nInt->raw) { + lbrk++; + pInt = nInt++; + if (lbrk > number-2) { + status = 1; + break; + } + } + while (val < pInt->raw) { + if (lbrk <= 0) { + status = 1; + break; + } + lbrk--; + nInt = pInt--; + } + } else { + /* raw values decrease down the table */ + while (val <= nInt->raw) { + lbrk++; + pInt = nInt++; + if (lbrk > number-2) { + status = 1; + break; + } + } + while(val > pInt->raw) { + if (lbrk <= 0) { + status = 1; + break; + } + lbrk--; + nInt = pInt--; + } + } + + *plbrk = lbrk; + *pval = pInt->eng + (val - pInt->raw) * pInt->slope; + + return status; +} + +/* Used by the ao record type */ +long epicsShareAPI cvtEngToRawBpt(double *pval, short linr, short init, + void **ppbrk, short *plbrk) +{ + double val = *pval; + long status = 0; + brkTable *pbrkTable; + brkInt *pInt, *nInt; + short lbrk; + int number; + + if (linr < 2) + return -1; + + if (init || *ppbrk == NULL) { /*must find breakpoint table*/ + pbrkTable = findBrkTable(linr); + if (!pbrkTable) + return S_dbLib_badField; + + *ppbrk = (void *)pbrkTable; + /* start at the beginning */ + *plbrk = 0; + } else + pbrkTable = (brkTable *)*ppbrk; + + number = pbrkTable->number; + lbrk = *plbrk; + + /* Limit index to the size of the table */ + if (lbrk < 0) + lbrk = 0; + else if (lbrk > number-2) + lbrk = number-2; + + pInt = & pbrkTable->paBrkInt[lbrk]; + nInt = pInt + 1; + + if (nInt->eng > pInt->eng) { + /* eng values increase down the table */ + while (val > nInt->eng) { + lbrk++; + pInt = nInt++; + if (lbrk > number-2) { + status = 1; + break; + } + } + while (val < pInt->eng) { + if (lbrk <= 0) { + status = 1; + break; + } + lbrk--; + nInt = pInt--; + } + } else { + /* eng values decrease down the table */ + while (val <= nInt->eng) { + lbrk++; + pInt = nInt++; + if (lbrk > number-2) { + status = 1; + break; + } + } + while (val > pInt->eng) { + if (lbrk <= 0) { + status = 1; + break; + } + lbrk--; + nInt = pInt--; + } + } + + *plbrk = lbrk; + *pval = pInt->raw + (val - pInt->eng) / pInt->slope; + + return status; +} diff --git a/src/db/dbAccess.c b/src/db/dbAccess.c new file mode 100644 index 000000000..b4baecc8b --- /dev/null +++ b/src/db/dbAccess.c @@ -0,0 +1,1610 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbAccess.c */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Original Author: Bob Dalesio + * Current Author: Marty Kraimer + * Date: 11-7-90 +*/ + + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsThread.h" +#include "errlog.h" +#include "cantProceed.h" +#include "cvtFast.h" +#include "epicsTime.h" +#include "alarm.h" +#include "ellLib.h" +#include "dbStaticLib.h" +#include "dbBase.h" +#include "link.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "devSup.h" +#include "caeventmask.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "dbFldTypes.h" +#include "special.h" +#include "errMdef.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "callback.h" +#include "dbScan.h" +#include "dbLock.h" +#include "dbEvent.h" +#include "dbConvert.h" +#include "dbConvertFast.h" +#include "epicsEvent.h" +#include "dbCa.h" +#include "dbBkpt.h" +#include "dbNotify.h" +#include "dbAccessDefs.h" +#include "recGbl.h" + +epicsShareDef struct dbBase *pdbbase = 0; +epicsShareDef volatile int interruptAccept=FALSE; + +static short mapDBFToDBR[DBF_NTYPES] = { + /* DBF_STRING => */ DBR_STRING, + /* DBF_CHAR => */ DBR_CHAR, + /* DBF_UCHAR => */ DBR_UCHAR, + /* DBF_SHORT => */ DBR_SHORT, + /* DBF_USHORT => */ DBR_USHORT, + /* DBF_LONG => */ DBR_LONG, + /* DBF_ULONG => */ DBR_ULONG, + /* DBF_FLOAT => */ DBR_FLOAT, + /* DBF_DOUBLE => */ DBR_DOUBLE, + /* DBF_ENUM, => */ DBR_ENUM, + /* DBF_MENU, => */ DBR_ENUM, + /* DBF_DEVICE => */ DBR_ENUM, + /* DBF_INLINK => */ DBR_STRING, + /* DBF_OUTLINK => */ DBR_STRING, + /* DBF_FWDLINK => */ DBR_STRING, + /* DBF_NOACCESS => */ DBR_NOACCESS +}; + +/* + * The number of consecutive attempts that can be made to process an + * active record before a SCAN_ALARM is raised. Active records + * (records with the pact flag set) cannot be processed until + * that flag becomes unset. + */ +#define MAX_LOCK 10 + +/* The following is to handle SPC_AS */ +static SPC_ASCALLBACK spcAsCallback = 0; + +static void inherit_severity(const struct pv_link *ppv_link, + dbCommon *pdest, epicsEnum16 stat, epicsEnum16 sevr) +{ + switch(ppv_link->pvlMask&pvlOptMsMode) { + case pvlOptNMS: break; + case pvlOptMSI: if (sevr < INVALID_ALARM) break; + case pvlOptMS: recGblSetSevr(pdest,LINK_ALARM,sevr); break; + case pvlOptMSS: recGblSetSevr(pdest,stat,sevr); break; + } +} + +void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func) +{ + spcAsCallback = func; +} + +long epicsShareAPI dbPutSpecial(DBADDR *paddr,int pass) +{ + long int (*pspecial)()=NULL; + struct rset *prset; + dbCommon *precord = paddr->precord; + long status=0; + long special=paddr->special; + + prset = dbGetRset(paddr); + if(special<100) { /*global processing*/ + if((special==SPC_NOMOD) && (pass==0)) { + status = S_db_noMod; + recGblDbaddrError(status,paddr,"dbPut"); + return(status); + }else if(special==SPC_SCAN){ + if(pass==0) + scanDelete(precord); + else + scanAdd(precord); + }else if((special==SPC_AS) && (pass==1)) { + if(spcAsCallback) (*spcAsCallback)(precord); + } + }else { + if( prset && (pspecial = (prset->special))) { + status=(*pspecial)(paddr,pass); + if(status) return(status); + } else if(pass==0){ + recGblRecSupError(S_db_noSupport,paddr,"dbPut", "special"); + return(S_db_noSupport); + } + } + return(0); +} + +static void get_enum_strs(DBADDR *paddr, char **ppbuffer, + struct rset *prset,long *options) +{ + short field_type=paddr->field_type; + dbFldDes *pdbFldDes = paddr->pfldDes; + dbMenu *pdbMenu; + dbDeviceMenu *pdbDeviceMenu; + char **papChoice; + unsigned long no_str; + char *ptemp; + struct dbr_enumStrs *pdbr_enumStrs=(struct dbr_enumStrs*)(*ppbuffer); + int i; + + memset(pdbr_enumStrs,'\0',dbr_enumStrs_size); + switch(field_type) { + case DBF_ENUM: + if( prset && prset->get_enum_strs ) { + (*prset->get_enum_strs)(paddr,pdbr_enumStrs); + } else { + *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ + } + break; + case DBF_MENU: + pdbMenu = (dbMenu *)pdbFldDes->ftPvt; + no_str = pdbMenu->nChoice; + papChoice= pdbMenu->papChoiceValue; + goto choice_common; + case DBF_DEVICE: + pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; + if(!pdbDeviceMenu) { + *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ + break; + } + no_str = pdbDeviceMenu->nChoice; + papChoice = pdbDeviceMenu->papChoice; + goto choice_common; +choice_common: + i = sizeof(pdbr_enumStrs->strs)/ + sizeof(pdbr_enumStrs->strs[0]); + if(ino_str = no_str; + ptemp = &(pdbr_enumStrs->strs[0][0]); + for (i=0; istrs[0])); + *(ptemp+sizeof(pdbr_enumStrs->strs[0])-1) = 0; + } + ptemp += sizeof(pdbr_enumStrs->strs[0]); + } + break; + default: + *options = (*options)^DBR_ENUM_STRS;/*Turn off option*/ + break; + } + *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size; + return; +} + +static void get_graphics(DBADDR *paddr, char **ppbuffer, + struct rset *prset,long *options) +{ + struct dbr_grDouble grd; + int got_data=FALSE; + + grd.upper_disp_limit = grd.lower_disp_limit = 0.0; + if( prset && prset->get_graphic_double ) { + (*prset->get_graphic_double)(paddr,&grd); + got_data=TRUE; + } + if( (*options) & (DBR_GR_LONG) ) { + char *pbuffer=*ppbuffer; + + if(got_data) { + struct dbr_grLong *pgr=(struct dbr_grLong*)pbuffer; + pgr->upper_disp_limit = (epicsInt32)grd.upper_disp_limit; + pgr->lower_disp_limit = (epicsInt32)grd.lower_disp_limit; + } else { + memset(pbuffer,'\0',dbr_grLong_size); + *options = (*options) ^ DBR_GR_LONG; /*Turn off option*/ + } + *ppbuffer = ((char *)*ppbuffer) + dbr_grLong_size; + } + if( (*options) & (DBR_GR_DOUBLE) ) { + char *pbuffer=*ppbuffer; + + if(got_data) { + struct dbr_grDouble *pgr=(struct dbr_grDouble*)pbuffer; + pgr->upper_disp_limit = grd.upper_disp_limit; + pgr->lower_disp_limit = grd.lower_disp_limit; + } else { + memset(pbuffer,'\0',dbr_grDouble_size); + *options = (*options) ^ DBR_GR_DOUBLE; /*Turn off option*/ + } + *ppbuffer = ((char *)*ppbuffer) + dbr_grDouble_size; + } + return; +} + +static void get_control(DBADDR *paddr, char **ppbuffer, + struct rset *prset,long *options) +{ + struct dbr_ctrlDouble ctrld; + int got_data=FALSE; + + ctrld.upper_ctrl_limit = ctrld.lower_ctrl_limit = 0.0; + if( prset && prset->get_control_double ) { + (*prset->get_control_double)(paddr,&ctrld); + got_data=TRUE; + } + if( (*options) & (DBR_CTRL_LONG) ) { + char *pbuffer=*ppbuffer; + + if(got_data) { + struct dbr_ctrlLong *pctrl=(struct dbr_ctrlLong*)pbuffer; + pctrl->upper_ctrl_limit = (epicsInt32)ctrld.upper_ctrl_limit; + pctrl->lower_ctrl_limit = (epicsInt32)ctrld.lower_ctrl_limit; + } else { + memset(pbuffer,'\0',dbr_ctrlLong_size); + *options = (*options) ^ DBR_CTRL_LONG; /*Turn off option*/ + } + *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlLong_size; + } + if( (*options) & (DBR_CTRL_DOUBLE) ) { + char *pbuffer=*ppbuffer; + + if(got_data) { + struct dbr_ctrlDouble *pctrl=(struct dbr_ctrlDouble*)pbuffer; + pctrl->upper_ctrl_limit = ctrld.upper_ctrl_limit; + pctrl->lower_ctrl_limit = ctrld.lower_ctrl_limit; + } else { + memset(pbuffer,'\0',dbr_ctrlDouble_size); + *options = (*options) ^ DBR_CTRL_DOUBLE; /*Turn off option*/ + } + *ppbuffer = ((char *)*ppbuffer) + dbr_ctrlDouble_size; + } + return; +} + +static void get_alarm(DBADDR *paddr, char **ppbuffer, + struct rset *prset,long *options) +{ + struct dbr_alDouble ald; + int got_data=FALSE; + + ald.upper_alarm_limit = ald.upper_warning_limit = 0.0; + ald.lower_warning_limit = ald.lower_alarm_limit = 0.0; + if( prset && prset->get_alarm_double ) { + (*prset->get_alarm_double)(paddr,&ald); + got_data=TRUE; + } + if( (*options) & (DBR_AL_LONG) ) { + char *pbuffer=*ppbuffer; + + if(got_data) { + struct dbr_alLong *pal=(struct dbr_alLong*)pbuffer; + pal->upper_alarm_limit = (epicsInt32)ald.upper_alarm_limit; + pal->upper_warning_limit = (epicsInt32)ald.upper_warning_limit; + pal->lower_warning_limit = (epicsInt32)ald.lower_warning_limit; + pal->lower_alarm_limit = (epicsInt32)ald.lower_alarm_limit; + } else { + memset(pbuffer,'\0',dbr_alLong_size); + *options = (*options) ^ DBR_AL_LONG; /*Turn off option*/ + } + *ppbuffer = ((char *)*ppbuffer) + dbr_alLong_size; + } + if( (*options) & (DBR_AL_DOUBLE) ) { + char *pbuffer=*ppbuffer; + + if(got_data) { + struct dbr_alDouble *pal=(struct dbr_alDouble*)pbuffer; + pal->upper_alarm_limit = ald.upper_alarm_limit; + pal->upper_warning_limit = ald.upper_warning_limit; + pal->lower_warning_limit = ald.lower_warning_limit; + pal->lower_alarm_limit = ald.lower_alarm_limit; + } else { + memset(pbuffer,'\0',dbr_alDouble_size); + *options = (*options) ^ DBR_AL_DOUBLE; /*Turn off option*/ + } + *ppbuffer = ((char *)*ppbuffer) + dbr_alDouble_size; + } + return; +} + +static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) +{ + db_field_log *pfl= (db_field_log *)pflin; + struct rset *prset; + short field_type=paddr->field_type; + dbCommon *pcommon; + char *pbuffer = *poriginal; + + prset=dbGetRset(paddr); + /* Process options */ + pcommon = paddr->precord; + if( (*options) & DBR_STATUS ) { + unsigned short *pushort = (unsigned short *)pbuffer; + + if(pfl!=NULL) { + *pushort++ = pfl->stat; + *pushort++ = pfl->sevr; + } else { + *pushort++ = pcommon->stat; + *pushort++ = pcommon->sevr; + } + *pushort++ = pcommon->acks; + *pushort++ = pcommon->ackt; + pbuffer = (char *)pushort; + } + if( (*options) & DBR_UNITS ) { + memset(pbuffer,'\0',dbr_units_size); + if( prset && prset->get_units ){ + (*prset->get_units)(paddr, pbuffer); + pbuffer[DB_UNITS_SIZE-1] = '\0'; + } else { + *options ^= DBR_UNITS; /*Turn off DBR_UNITS*/ + } + pbuffer += dbr_units_size; + } + if( (*options) & DBR_PRECISION ) { + memset(pbuffer, '\0', dbr_precision_size); + if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE) + && prset && prset->get_precision ){ + (*prset->get_precision)(paddr,pbuffer); + } else { + *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/ + } + pbuffer += dbr_precision_size; + } + if( (*options) & DBR_TIME ) { + epicsUInt32 *ptime = (epicsUInt32 *)pbuffer; + + if(pfl!=NULL) { + *ptime++ = pfl->time.secPastEpoch; + *ptime++ = pfl->time.nsec; + } else { + *ptime++ = pcommon->time.secPastEpoch; + *ptime++ = pcommon->time.nsec; + } + pbuffer = (char *)ptime; + } + if( (*options) & DBR_ENUM_STRS ) + get_enum_strs(paddr, &pbuffer, prset, options); + if( (*options) & (DBR_GR_LONG|DBR_GR_DOUBLE )) + get_graphics(paddr, &pbuffer, prset, options); + if((*options) & (DBR_CTRL_LONG | DBR_CTRL_DOUBLE )) + get_control(paddr, &pbuffer, prset, options); + if((*options) & (DBR_AL_LONG | DBR_AL_DOUBLE )) + get_alarm(paddr, &pbuffer, prset, options); + *poriginal = pbuffer; +} + +struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr) +{ + struct dbFldDes *pfldDes = paddr->pfldDes; + + if(!pfldDes) return(0); + return(pfldDes->pdbRecordType->prset); +} + +long epicsShareAPI dbPutAttribute( + const char *recordTypename,const char *name,const char*value) +{ + DBENTRY dbEntry; + DBENTRY *pdbEntry = &dbEntry; + long status=0; + + if(!pdbbase) return(S_db_notFound); + dbInitEntry(pdbbase,pdbEntry); + status = dbFindRecordType(pdbEntry,recordTypename); + if(!status) status = dbPutRecordAttribute(pdbEntry,name,value); + dbFinishEntry(pdbEntry); + if(status) errMessage(status,"dbPutAttribute failure"); + return(status); +} + +int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes) +{ + if(pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType) + return(TRUE); + else + return(FALSE); +} + +int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr) +{ + return paddr->pfldDes->indRecordType; +} + +long epicsShareAPI dbGetNelements(const struct link *plink,long *nelements) +{ + switch(plink->type) { + case CONSTANT: + *nelements = 0; + return(0); + case DB_LINK: { + DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt; + *nelements = paddr->no_elements; + return(0); + } + case CA_LINK: + return(dbCaGetNelements(plink,nelements)); + default: + break; + } + return(S_db_badField); +} + +int epicsShareAPI dbIsLinkConnected(const struct link *plink) +{ + switch(plink->type) { + case DB_LINK: return(TRUE); + case CA_LINK: return(dbCaIsLinkConnected(plink)); + default: break; + } + return(FALSE); +} + +int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) +{ + switch(plink->type) { + case DB_LINK: + { + DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt; + + return((int)paddr->field_type); + } + case CA_LINK: return(dbCaGetLinkDBFtype(plink)); + default: break; + } + return(-1); +} + +/* + * Process a record if its scan field is passive. + * Will notify if processing is complete by callback. + * (only if you are interested in completion) + */ +long epicsShareAPI dbScanPassive(dbCommon *pfrom, dbCommon *pto) +{ + long status; + + /* if not passive just return success */ + if(pto->scan != 0) return(0); + + if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto); + status = dbProcess(pto); + return(status); +} + +/*KLUDGE: Following needed so that dbPutLink to PROC field works correctly*/ +long epicsShareAPI dbScanLink(dbCommon *pfrom, dbCommon *pto) +{ + long status; + unsigned char pact; + + if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto); + pact = pfrom->pact; + pfrom->pact = TRUE; + status = dbProcess(pto); + pfrom->pact = pact; + return(status); +} + +void epicsShareAPI dbScanFwdLink(struct link *plink) +{ + dbCommon *precord; + struct pv_link *pvlink; + short fwdLinkValue; + + if(plink->type!=DB_LINK && plink->type!=CA_LINK) return; + pvlink = &plink->value.pv_link; + precord = pvlink->precord; + if(plink->type==DB_LINK) { + dbAddr *paddr = (dbAddr *)plink->value.pv_link.pvt; + dbScanPassive(precord,paddr->precord); + return; + } + if(!(pvlink->pvlMask & pvlOptFWD)) return; + fwdLinkValue = 1; + dbCaPutLink(plink,DBR_SHORT,&fwdLinkValue,1); + return; +} + +/* + * Process the record. + * 1. Check for breakpoints. + * 2. Check the process active flag (PACT). + * 3. Check the disable link. + * 4. Check the RSET (record support entry table) exists. + * 5. Run the process routine specific to the record type. + * 6. Check to see if record contents should be automatically printed. + */ +long epicsShareAPI dbProcess(dbCommon *precord) +{ + struct rset *prset = precord->rset; + dbRecordType *pdbRecordType = precord->rdes; + unsigned char tpro=precord->tpro; + long status = 0; + int *ptrace; + int set_trace=FALSE; + dbFldDes *pdbFldDes; + int callNotifyCompletion = FALSE; + + ptrace = dbLockSetAddrTrace(precord); + /* + * Note that it is likely that if any changes are made + * to dbProcess() corresponding changes will have to + * be made in the breakpoint handler. + */ + + /* see if there are any stopped records or breakpoints */ + if (lset_stack_count != 0) { + /* + * Check to see if the record should be processed + * and activate breakpoint accordingly. If this + * function call returns non-zero, skip record + * support and fall out of dbProcess(). This is + * done so that a dbContTask() can be spawned to + * take over record processing for the lock set + * containing a breakpoint. + */ + if (dbBkpt(precord)) + goto all_done; + } + + /* check for trace processing*/ + if (tpro) { + if(*ptrace==0) { + *ptrace = 1; + set_trace = TRUE; + } + } + + /* If already active dont process */ + if (precord->pact) { + unsigned short monitor_mask; + + if (*ptrace) printf("%s: Active %s\n", + epicsThreadGetNameSelf(), precord->name); + /* raise scan alarm after MAX_LOCK times */ + if (precord->stat==SCAN_ALARM) goto all_done; + if (precord->lcnt++ !=MAX_LOCK) goto all_done; + if (precord->sevr>=INVALID_ALARM) goto all_done; + recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM); + monitor_mask = recGblResetAlarms(precord); + monitor_mask |= DBE_VALUE|DBE_LOG; + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; + db_post_events(precord, + (void *)(((char *)precord) + pdbFldDes->offset), + monitor_mask); + goto all_done; + } + else precord->lcnt = 0; + + /* + * Check the record disable link. A record will not be + * processed if the value retrieved through this link + * is equal to constant set in the record's disv field. + */ + status = dbGetLink(&(precord->sdis),DBR_SHORT,&(precord->disa),0,0); + + /* if disabled check disable alarm severity and return success */ + if (precord->disa == precord->disv) { + if(*ptrace) printf("%s: Disabled %s\n", + epicsThreadGetNameSelf(), precord->name); + /*take care of caching and notifyCompletion*/ + precord->rpro = FALSE; + precord->putf = FALSE; + callNotifyCompletion = TRUE; + /* raise disable alarm */ + if (precord->stat==DISABLE_ALARM) goto all_done; + precord->sevr = precord->diss; + precord->stat = DISABLE_ALARM; + precord->nsev = 0; + precord->nsta = 0; + db_post_events(precord, &precord->stat, DBE_VALUE); + db_post_events(precord, &precord->sevr, DBE_VALUE); + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; + db_post_events(precord, + (void *)(((char *)precord) + pdbFldDes->offset), + DBE_VALUE|DBE_ALARM); + goto all_done; + } + + /* locate record processing routine */ + /* put this in iocInit() !!! */ + if (!(prset=precord->rset) || !(prset->process)) { + callNotifyCompletion = TRUE; + precord->pact=1;/*set pact TRUE so error is issued only once*/ + recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess"); + status = S_db_noRSET; + if (*ptrace) printf("%s: No RSET for %s\n", + epicsThreadGetNameSelf(), precord->name); + goto all_done; + } + if(*ptrace) printf("%s: Process %s\n", + epicsThreadGetNameSelf(), precord->name); + /* process record */ + status = (*prset->process)(precord); + /* Print record's fields if PRINT_MASK set in breakpoint field */ + if (lset_stack_count != 0) { + dbPrint(precord); + } +all_done: + if (set_trace) *ptrace = 0; + if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord); + return(status); +} + +/* + * Fill out a database structure (*paddr) for + * a record given by the name "pname." + * + * Returns error codes from StaticLib module, not + * from dbAccess. + */ +long epicsShareAPI dbNameToAddr(const char *pname, DBADDR *paddr) +{ + DBENTRY dbEntry; + dbFldDes *pflddes; + struct rset *prset; + long status = 0; + long no_elements = 1; + short dbfType, dbrType, field_size; + + if (!pname || !*pname || !pdbbase) + return S_db_notFound; + + dbInitEntry(pdbbase, &dbEntry); + status = dbFindRecordPart(&dbEntry, &pname); + if (status) goto finish; + + if (*pname == '.') ++pname; + status = dbFindFieldPart(&dbEntry, &pname); + if (status == S_dbLib_fieldNotFound) + status = dbGetAttributePart(&dbEntry, &pname); + if (status) goto finish; + + paddr->precord = dbEntry.precnode->precord; + paddr->pfield = dbEntry.pfield; + pflddes = dbEntry.pflddes; + + dbfType = pflddes->field_type; + dbrType = mapDBFToDBR[dbfType]; + field_size = pflddes->size; + + if (*pname++ == '$') { + /* Some field types can be accessed as char arrays */ + if (dbfType == DBF_STRING) { + dbfType = DBF_CHAR; + dbrType = DBR_CHAR; + no_elements = field_size; + field_size = 1; + } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { + /* Clients see a char array, but keep original dbfType */ + dbrType = DBR_CHAR; + no_elements = PVNAME_STRINGSZ + 12; + field_size = 1; + } else { + status = S_dbLib_fieldNotFound; + goto finish; + } + } + + paddr->pfldDes = pflddes; + paddr->field_type = dbfType; + paddr->dbr_field_type = dbrType; + paddr->field_size = field_size; + paddr->special = pflddes->special; + paddr->no_elements = no_elements; + + if ((paddr->special == SPC_DBADDR) && + (prset = dbGetRset(paddr)) && + prset->cvt_dbaddr) + /* cvt_dbaddr routine may change any of these elements of paddr: + * pfield, no_elements, element_offset, field_type, + * dbr_field_type, field_size, and/or special. + */ + status = prset->cvt_dbaddr(paddr); + +finish: + dbFinishEntry(&dbEntry); + return status; +} + +/* JOH 10-19-04 */ +static char * dbCopyInNameComponentOfPV ( + char * pBuf, unsigned bufLen, const char * pComponent ) +{ + unsigned compLen = strlen ( pComponent ); + if ( compLen < bufLen ) { + strcpy ( pBuf, pComponent ); + return pBuf + compLen; + } + else { + unsigned reducedSize = bufLen - 1u; + strncpy ( pBuf, pComponent, reducedSize ); + pBuf[reducedSize] = '\0'; + return pBuf + reducedSize; + } +} +/* + * Copies PV name into pBuf of length bufLen + * + * Returns the number of bytes written to pBuf + * not including null termination. + * JOH 10-19-04 + */ +unsigned dbNameOfPV ( + const dbAddr * paddr, char * pBuf, unsigned bufLen ) +{ + dbFldDes * pfldDes = paddr->pfldDes; + char * pBufTmp = pBuf; + if ( bufLen == 0u ) { + return 0u; + } + pBufTmp = dbCopyInNameComponentOfPV ( + pBufTmp, bufLen, paddr->precord->name ); + pBufTmp = dbCopyInNameComponentOfPV ( + pBufTmp, bufLen - ( pBufTmp - pBuf ), "." ); + pBufTmp = dbCopyInNameComponentOfPV ( + pBufTmp, bufLen - ( pBufTmp - pBuf ), pfldDes->name ); + return pBufTmp - pBuf; +} +/* + * Returns the number of bytes in the PV name + * not including null termination. + * JOH 10-19-04 + */ +unsigned dbNameSizeOfPV ( const dbAddr * paddr ) +{ + unsigned recNameLen = strlen ( paddr->precord->name ); + dbFldDes * pfldDes = paddr->pfldDes; + unsigned fieldNameLen = strlen ( pfldDes->name ); + return recNameLen + fieldNameLen + 1; +} + +long epicsShareAPI dbValueSize(short dbr_type) +{ + /* sizes for value associated with each DBR request type */ + static long size[] = { + MAX_STRING_SIZE, /* STRING */ + sizeof(epicsInt8), /* CHAR */ + sizeof(epicsUInt8), /* UCHAR */ + sizeof(epicsInt16), /* SHORT */ + sizeof(epicsUInt16), /* USHORT */ + sizeof(epicsInt32), /* LONG */ + sizeof(epicsUInt32), /* ULONG */ + sizeof(epicsFloat32), /* FLOAT */ + sizeof(epicsFloat64), /* DOUBLE */ + sizeof(epicsEnum16)}; /* ENUM */ + + return(size[dbr_type]); +} + + +long epicsShareAPI dbBufferSize(short dbr_type, long options, long no_elements) +{ + long nbytes=0; + + nbytes += dbValueSize(dbr_type) * no_elements; + if (options & DBR_STATUS) nbytes += dbr_status_size; + if (options & DBR_UNITS) nbytes += dbr_units_size; + if (options & DBR_PRECISION) nbytes += dbr_precision_size; + if (options & DBR_TIME) nbytes += dbr_time_size; + if (options & DBR_ENUM_STRS) nbytes += dbr_enumStrs_size; + if (options & DBR_GR_LONG) nbytes += dbr_grLong_size; + if (options & DBR_GR_DOUBLE) nbytes += dbr_grDouble_size; + if (options & DBR_CTRL_LONG) nbytes += dbr_ctrlLong_size; + if (options & DBR_CTRL_DOUBLE) nbytes += dbr_ctrlDouble_size; + if (options & DBR_AL_LONG) nbytes += dbr_alLong_size; + if (options & DBR_AL_DOUBLE) nbytes += dbr_alDouble_size; + return(nbytes); +} +int epicsShareAPI dbLoadDatabase(const char *file, const char *path, const char *subs) +{ + return dbReadDatabase(&pdbbase, file, path, subs); +} + +int epicsShareAPI dbLoadRecords(const char* file, const char* subs) +{ + return dbReadDatabase(&pdbbase, file, 0, subs); +} + + +long epicsShareAPI dbGetLinkValue(struct link *plink, short dbrType, + void *pbuffer, long *poptions, long *pnRequest) +{ + long status = 0; + + if (plink->type == CONSTANT) { + if (poptions) *poptions = 0; + if (pnRequest) *pnRequest = 0; + } else if (plink->type == DB_LINK) { + struct pv_link *ppv_link = &(plink->value.pv_link); + DBADDR *paddr = ppv_link->pvt; + dbCommon *precord = plink->value.pv_link.precord; + + /* scan passive records with links that are process passive */ + if (ppv_link->pvlMask&pvlOptPP) { + dbCommon *pfrom = paddr->precord; + unsigned char pact; + + pact = precord->pact; + precord->pact = TRUE; + status = dbScanPassive(precord,pfrom); + precord->pact = pact; + if (status) return status; + } + if(precord!= paddr->precord) { + inherit_severity(ppv_link,precord,paddr->precord->stat,paddr->precord->sevr); + } + + if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { + status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + } else { + unsigned short dbfType = paddr->field_type; + long no_elements = paddr->no_elements; + + if (dbrType < 0 || dbrType > DBR_ENUM || + dbfType > DBF_DEVICE) { + status = S_db_badDbrtype; + recGblRecordError(status, precord, "GetLinkValue Failed"); + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + return status; + } + /* attempt to make a fast link */ + if ((!poptions || *poptions == 0) && + no_elements == 1 && + (!pnRequest || *pnRequest == 1) && + paddr->special != SPC_ATTRIBUTE) { + ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; + status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + } else { + ppv_link->getCvt = 0; + status = dbGet(paddr, dbrType, pbuffer, poptions, pnRequest, NULL); + } + } + ppv_link->lastGetdbrType = dbrType; + if (status) { + recGblRecordError(status, precord, "dbGetLinkValue"); + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + } + } else if (plink->type == CA_LINK) { + struct dbCommon *precord = plink->value.pv_link.precord; + const struct pv_link *pcalink = &plink->value.pv_link; + epicsEnum16 sevr, stat; + + status=dbCaGetLink(plink,dbrType,pbuffer,&stat,&sevr,pnRequest); + if (status) { + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + } else { + inherit_severity(pcalink,precord,stat,sevr); + } + if (poptions) *poptions = 0; + } else { + cantProceed("dbGetLinkValue: Illegal link type"); + } + return status; +} + +long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + long status = 0; + + if (plink->type == DB_LINK) { + struct dbCommon *psource = plink->value.pv_link.precord; + struct pv_link *ppv_link = &plink->value.pv_link; + DBADDR *paddr = (DBADDR *)ppv_link->pvt; + dbCommon *pdest = paddr->precord; + + status = dbPut(paddr, dbrType, pbuffer, nRequest); + inherit_severity(ppv_link,pdest,psource->nsta,psource->nsev); + if (status) return status; + + if (paddr->pfield == (void *)&pdest->proc || + (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { + /*if dbPutField caused asyn record to process */ + /* ask for reprocessing*/ + if (pdest->putf) { + pdest->rpro = TRUE; + } else { /* otherwise ask for the record to be processed*/ + status = dbScanLink(psource, pdest); + } + } + if (status) + recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM); + } else if (plink->type == CA_LINK) { + struct dbCommon *psource = plink->value.pv_link.precord; + + status = dbCaPutLink(plink, dbrType, pbuffer, nRequest); + if (status < 0) + recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM); + } else { + cantProceed("dbPutLinkValue: Illegal link type"); + } + return status; +} + +long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType, + void *pbuffer, long *options, long *nRequest, void *pflin) +{ + short dbfType = paddr->field_type; + dbCommon *precord = paddr->precord; + long status = 0; + + dbScanLock(precord); + if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { + DBENTRY dbEntry; + dbFldDes *pfldDes = paddr->pfldDes; + char *rtnString; + char *pbuf = (char *)pbuffer; + int maxlen; + + if (options && (*options)) + getOptions(paddr, &pbuf, options, pflin); + if (nRequest && *nRequest == 0) goto done; + + switch (dbrType) { + case DBR_STRING: + maxlen = MAX_STRING_SIZE - 1; + if (nRequest && *nRequest > 1) *nRequest = 1; + break; + + case DBR_CHAR: + case DBR_UCHAR: + if (nRequest && *nRequest > 1) { + maxlen = *nRequest - 1; + break; + } + /* else fall through ... */ + default: + status = S_db_badDbrtype; + goto done; + } + + dbInitEntry(pdbbase, &dbEntry); + status = dbFindRecord(&dbEntry, precord->name); + if (!status) status = dbFindField(&dbEntry, pfldDes->name); + if (!status) { + rtnString = dbGetString(&dbEntry); + strncpy(pbuf, rtnString, maxlen - 1); + pbuf[maxlen - 1] = 0; + } + dbFinishEntry(&dbEntry); + } else { + status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin); + } +done: + dbScanUnlock(precord); + return status; +} + +long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, + void *pbuffer, long *options, long *nRequest, void *pflin) +{ + db_field_log *pfl = (db_field_log *)pflin; + short field_type = paddr->field_type; + long no_elements = paddr->no_elements; + long offset; + struct rset *prset; + long status = 0; + + if (options && *options) { + char *pbuf = pbuffer; + + getOptions(paddr, &pbuf, options, pflin); + pbuffer = pbuf; + } + if (nRequest && *nRequest == 0) return 0; + + if (paddr->special == SPC_ATTRIBUTE) { + char *pbuf = pbuffer; + int maxlen; + + if (!paddr->pfield) return S_db_badField; + + switch (dbrType) { + case DBR_STRING: + maxlen = MAX_STRING_SIZE - 1; + if (nRequest && *nRequest > 1) *nRequest = 1; + break; + + case DBR_CHAR: + case DBR_UCHAR: + if (nRequest && *nRequest > 1) { + maxlen = *nRequest - 1; + break; + } + /* else fall through ... */ + default: + return S_db_badDbrtype; + } + + strncpy(pbuf, (char *)paddr->pfield, maxlen - 1); + pbuf[maxlen - 1] = 0; + return 0; + } + + /* Check for valid request */ + if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { + char message[80]; + + sprintf(message, "dbGet: Request type is %d\n", dbrType); + recGblDbaddrError(S_db_badDbrtype, paddr, message); + return S_db_badDbrtype; + } + + /* check for array */ + if (paddr->special == SPC_DBADDR && + no_elements > 1 && + (prset = dbGetRset(paddr)) && + prset->get_array_info) { + status = prset->get_array_info(paddr, &no_elements, &offset); + } else + offset = 0; + + if (offset == 0 && (!nRequest || no_elements == 1)) { + if (nRequest) *nRequest = 1; + if (pfl != NULL) { + DBADDR localAddr = *paddr; /* Structure copy */ + + localAddr.pfield = (char *)&pfl->field; + status = dbFastGetConvertRoutine[field_type][dbrType] + (localAddr.pfield, pbuffer, &localAddr); + } else { + status = dbFastGetConvertRoutine[field_type][dbrType] + (paddr->pfield, pbuffer, paddr); + } + } else { + long n; + long (*convert)(); + + if (nRequest) { + if (no_elements<(*nRequest)) *nRequest = no_elements; + n = *nRequest; + } else { + n = 1; + } + convert = dbGetConvertRoutine[field_type][dbrType]; + if (!convert) { + char message[80]; + + sprintf(message, "dbGet: Missing conversion for [%d][%d]\n", + field_type, dbrType); + recGblDbaddrError(S_db_badDbrtype, paddr, message); + return S_db_badDbrtype; + } + /* convert database field and place it in the buffer */ + if (n <= 0) { + ;/*do nothing*/ + } else if (pfl) { + DBADDR localAddr = *paddr; /* Structure copy */ + + localAddr.pfield = (char *)&pfl->field; + status = convert(&localAddr, pbuffer, n, no_elements, offset); + } else { + status = convert(paddr, pbuffer, n, no_elements, offset); + } + } + return status; +} + +devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp) { + return (devSup *)ellNth(&prdes->devList, dtyp+1); +} + +devSup* epicsShareAPI dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset) { + devSup *pdevSup = (devSup *)ellFirst(&prdes->devList); + while (pdevSup) { + if (pdset == pdevSup->pdset) return pdevSup; + pdevSup = (devSup *)ellNext(&pdevSup->node); + } + return NULL; +} + +static long dbPutFieldLink(DBADDR *paddr, + short dbrType, const void *pbuffer, long nRequest) +{ + dbCommon *precord = paddr->precord; + dbFldDes *pfldDes = paddr->pfldDes; + long special = paddr->special; + DBLINK *plink = (DBLINK *)paddr->pfield; + const char *pstring = (const char *)pbuffer; + DBENTRY dbEntry; + DBADDR dbaddr; + struct dsxt *old_dsxt = NULL; + struct dset *new_dset = NULL; + struct dsxt *new_dsxt = NULL; + long status; + int isDevLink; + short scan; + + switch (dbrType) { + case DBR_CHAR: + case DBR_UCHAR: + if (pstring[nRequest - 1] != '\0') + return S_db_badDbrtype; + break; + + case DBR_STRING: + break; + + default: + return S_db_badDbrtype; + } + + dbInitEntry(pdbbase, &dbEntry); + status = dbFindRecord(&dbEntry, precord->name); + if (!status) status = dbFindField(&dbEntry, pfldDes->name); + if (status) goto finish; + + isDevLink = ellCount(&precord->rdes->devList) > 0 && + (strcmp(pfldDes->name, "INP") == 0 || + strcmp(pfldDes->name, "OUT") == 0); + + dbLockSetGblLock(); + dbLockSetRecordLock(precord); + + scan = precord->scan; + + if (isDevLink) { + devSup *pdevSup = dbDTYPtoDevSup(precord->rdes, precord->dtyp); + if (pdevSup) { + new_dset = pdevSup->pdset; + new_dsxt = pdevSup->pdsxt; + } + + if (precord->dset) { + pdevSup = dbDSETtoDevSup(precord->rdes, precord->dset); + if (pdevSup) + old_dsxt = pdevSup->pdsxt; + } + + if (new_dsxt == NULL || + new_dsxt->add_record == NULL || + (precord->dset && old_dsxt == NULL) || + (old_dsxt && old_dsxt->del_record == NULL)) { + status = S_db_noSupport; + goto unlock; + } + + if (scan == menuScanI_O_Intr) { + scanDelete(precord); + precord->scan = menuScanPassive; + } + + if (old_dsxt) { + status = old_dsxt->del_record(precord); + if (status) + goto restoreScan; + } + } + + switch (plink->type) { /* Old link type */ + case DB_LINK: + free(plink->value.pv_link.pvt); + plink->value.pv_link.pvt = 0; + plink->type = PV_LINK; + plink->value.pv_link.getCvt = 0; + plink->value.pv_link.pvlMask = 0; + plink->value.pv_link.lastGetdbrType = 0; + dbLockSetSplit(precord); + break; + + case CA_LINK: + dbCaRemoveLink(plink); + plink->type = PV_LINK; + plink->value.pv_link.getCvt = 0; + plink->value.pv_link.pvlMask = 0; + plink->value.pv_link.lastGetdbrType = 0; + break; + + case CONSTANT: + break; /* do nothing */ + + case PV_LINK: + case MACRO_LINK: + break; /* should never get here */ + + default: /* Hardware address */ + if (!isDevLink) { + status = S_db_badHWaddr; + goto restoreScan; + } + break; + } + + if (special) status = dbPutSpecial(paddr, 0); + + if (!status) status = dbPutString(&dbEntry, pstring); + + if (!status && special) status = dbPutSpecial(paddr, 1); + + if (status) { + if (isDevLink) { + precord->dset = NULL; + precord->pact = TRUE; + } + goto postScanEvent; + } + + if (isDevLink) { + precord->dpvt = NULL; + precord->dset = new_dset; + precord->pact = FALSE; + + status = new_dsxt->add_record(precord); + if (status) { + precord->dset = NULL; + precord->pact = TRUE; + goto postScanEvent; + } + } + + switch (plink->type) { /* New link type */ + case PV_LINK: + if (plink == &precord->tsel) + recGblTSELwasModified(plink); + plink->value.pv_link.precord = precord; + + if (!(plink->value.pv_link.pvlMask & (pvlOptCA|pvlOptCP|pvlOptCPP)) && + (dbNameToAddr(plink->value.pv_link.pvname, &dbaddr) == 0)) { + /* It's a DB link */ + DBADDR *pdbAddr; + + plink->type = DB_LINK; + pdbAddr = dbMalloc(sizeof(struct dbAddr)); + *pdbAddr = dbaddr; /* NB: structure copy */; + plink->value.pv_link.pvt = pdbAddr; + dbLockSetRecordLock(pdbAddr->precord); + dbLockSetMerge(precord, pdbAddr->precord); + } else { /* Make it a CA link */ + char *pperiod; + + plink->type = CA_LINK; + if (pfldDes->field_type == DBF_INLINK) { + plink->value.pv_link.pvlMask |= pvlOptInpNative; + } + dbCaAddLink(plink); + if (pfldDes->field_type == DBF_FWDLINK) { + pperiod = strrchr(plink->value.pv_link.pvname, '.'); + if (pperiod && strstr(pperiod, "PROC")) + plink->value.pv_link.pvlMask |= pvlOptFWD; + } + } + break; + + case CONSTANT: + break; + + case DB_LINK: + case CA_LINK: + case MACRO_LINK: + break; /* should never get here */ + + default: /* Hardware address */ + if (!isDevLink) { + status = S_db_badHWaddr; + goto postScanEvent; + } + break; + } + db_post_events(precord, plink, DBE_VALUE | DBE_LOG); + +restoreScan: + if (isDevLink && + scan == menuScanI_O_Intr) { /* undo scanDelete() */ + precord->scan = scan; + scanAdd(precord); + } +postScanEvent: + if (scan != precord->scan) + db_post_events(precord, &precord->scan, DBE_VALUE|DBE_LOG); +unlock: + dbLockSetGblUnlock(); +finish: + dbFinishEntry(&dbEntry); + return status; +} + +long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, + const void *pbuffer, long nRequest) +{ + long status = 0; + long special = paddr->special; + dbFldDes *pfldDes = paddr->pfldDes; + dbCommon *precord = paddr->precord; + short dbfType = paddr->field_type; + + if (special == SPC_ATTRIBUTE) + return S_db_noMod; + + /*check for putField disabled*/ + if (precord->disp && + (void *)(&precord->disp) != paddr->pfield) + return S_db_putDisabled; + + if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) + return dbPutFieldLink(paddr, dbrType, pbuffer, nRequest); + + dbScanLock(precord); + status = dbPut(paddr, dbrType, pbuffer, nRequest); + if (status == 0) { + if (paddr->pfield == (void *)&precord->proc || + (pfldDes->process_passive && + precord->scan == 0 && + dbrType < DBR_PUT_ACKT)) { + if (precord->pact) { + if (precord->tpro) + printf("%s: Active %s\n", + epicsThreadGetNameSelf(), precord->name); + precord->rpro = TRUE; + } else { + /* indicate that dbPutField called dbProcess */ + precord->putf = TRUE; + status = dbProcess(precord); + } + } + } + dbScanUnlock(precord); + return status; +} + +static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, + long no_elements, long offset) +{ + dbCommon *precord = paddr->precord; + + if (*pbuffer == precord->ackt) return 0; + precord->ackt = *pbuffer; + db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM); + if (!precord->ackt && precord->acks > precord->sevr) { + precord->acks = precord->sevr; + db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); + } + db_post_events(precord, NULL, DBE_ALARM); + return 0; +} + +static long putAcks(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, + long no_elements, long offset) +{ + dbCommon *precord = paddr->precord; + + if (*pbuffer >= precord->acks) { + precord->acks = 0; + db_post_events(precord, NULL, DBE_ALARM); + db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); + } + return 0; +} + +long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, + const void *pbuffer, long nRequest) +{ + dbCommon *precord = paddr->precord; + short field_type = paddr->field_type; + long no_elements = paddr->no_elements; + long special = paddr->special; + long offset; + long status = 0; + dbFldDes *pfldDes; + int isValueField; + + if (special == SPC_ATTRIBUTE) return S_db_noMod; + + if (dbrType == DBR_PUT_ACKT && field_type <= DBF_DEVICE) { + return putAckt(paddr, (unsigned short *)pbuffer, 1, 1, 0); + } else if (dbrType == DBR_PUT_ACKS && field_type <= DBF_DEVICE) { + return putAcks(paddr, (unsigned short *)pbuffer, 1, 1, 0); + } else if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { + char message[80]; + + sprintf(message, "dbPut: Request type is %d", dbrType); + recGblDbaddrError(S_db_badDbrtype, paddr, message); + return S_db_badDbrtype; + } + + if (special) { + status = dbPutSpecial(paddr, 0); + if (status) return status; + } + + if (no_elements <= 1) { + status = dbFastPutConvertRoutine[dbrType][field_type](pbuffer, + paddr->pfield, paddr); + } else { + struct rset *prset = dbGetRset(paddr); + + if (paddr->special == SPC_DBADDR && + prset && prset->get_array_info) { + long dummy; + + status = prset->get_array_info(paddr, &dummy, &offset); + } + else + offset = 0; + if (no_elements < nRequest) nRequest = no_elements; + status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, + nRequest, no_elements, offset); + + /* update array info */ + if (!status && + paddr->special == SPC_DBADDR && + prset && prset->put_array_info) { + status = prset->put_array_info(paddr, nRequest); + } + } + if (status) return status; + + /* check if special processing is required */ + if (special) { + status = dbPutSpecial(paddr,1); + if (status) return status; + } + + /* Propagate monitor events for this field, */ + /* unless the field field is VAL and PP is true. */ + pfldDes = paddr->pfldDes; + isValueField = dbIsValueField(pfldDes); + if (isValueField) precord->udf = FALSE; + if (precord->mlis.count && + !(isValueField && pfldDes->process_passive)) + db_post_events(precord, paddr->pfield, DBE_VALUE | DBE_LOG); + + return status; +} + +/* various utility routines */ +long epicsShareAPI dbGetControlLimits( + const struct link *plink,double *low, double *high) +{ + struct buffer { + DBRctrlDouble + double value; + } buffer; + DBADDR *paddr; + long options = DBR_CTRL_DOUBLE; + long number_elements = 0; + long status; + + if(plink->type == CA_LINK) return(dbCaGetControlLimits(plink,low,high)); + if(plink->type !=DB_LINK) return(S_db_notFound); + paddr = (DBADDR *)plink->value.pv_link.pvt; + status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); + if(status) return(status); + *low = buffer.lower_ctrl_limit; + *high = buffer.upper_ctrl_limit; + return(0); +} + +long epicsShareAPI dbGetGraphicLimits( + const struct link *plink,double *low, double *high) +{ + struct buffer { + DBRgrDouble + double value; + } buffer; + DBADDR *paddr; + long options = DBR_GR_DOUBLE; + long number_elements = 0; + long status; + + if(plink->type == CA_LINK) return(dbCaGetGraphicLimits(plink,low,high)); + if(plink->type !=DB_LINK) return(S_db_notFound); + paddr = (DBADDR *)plink->value.pv_link.pvt; + status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); + if(status) return(status); + *low = buffer.lower_disp_limit; + *high = buffer.upper_disp_limit; + return(0); +} + +long epicsShareAPI dbGetAlarmLimits(const struct link *plink, + double *lolo, double *low, double *high, double *hihi) +{ + struct buffer { + DBRalDouble + double value; + } buffer; + DBADDR *paddr; + long options = DBR_AL_DOUBLE; + long number_elements = 0; + long status; + + if(plink->type == CA_LINK) + return(dbCaGetAlarmLimits(plink,lolo,low,high,hihi)); + if(plink->type !=DB_LINK) return(S_db_notFound); + paddr = (DBADDR *)plink->value.pv_link.pvt; + status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); + if(status) return(status); + *lolo = buffer.lower_alarm_limit; + *low = buffer.lower_warning_limit; + *high = buffer.upper_warning_limit; + *hihi = buffer.upper_alarm_limit; + return(0); +} + +long epicsShareAPI dbGetPrecision(const struct link *plink,short *precision) +{ + struct buffer { + DBRprecision + double value; + } buffer; + DBADDR *paddr; + long options = DBR_PRECISION; + long number_elements = 0; + long status; + + if(plink->type == CA_LINK) return(dbCaGetPrecision(plink,precision)); + if(plink->type !=DB_LINK) return(S_db_notFound); + paddr = (DBADDR *)plink->value.pv_link.pvt; + status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); + if(status) return(status); + *precision = buffer.precision.dp; + return(0); +} + +long epicsShareAPI dbGetUnits( + const struct link *plink,char *units,int unitsSize) +{ + struct buffer { + DBRunits + double value; + } buffer; + DBADDR *paddr; + long options = DBR_UNITS; + long number_elements = 0; + long status; + + if(plink->type == CA_LINK) return(dbCaGetUnits(plink,units,unitsSize)); + if(plink->type !=DB_LINK) return(S_db_notFound); + paddr = (DBADDR *)plink->value.pv_link.pvt; + status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); + if(status) return(status); + strncpy(units,buffer.units,unitsSize); + return(0); +} + +long epicsShareAPI dbGetAlarm(const struct link *plink, + epicsEnum16 *status,epicsEnum16 *severity) +{ + DBADDR *paddr; + + if(plink->type == CA_LINK) return(dbCaGetAlarm(plink,status,severity)); + if(plink->type !=DB_LINK) return(S_db_notFound); + paddr = (DBADDR *)plink->value.pv_link.pvt; + if (status) *status = paddr->precord->stat; + if (severity) *severity = paddr->precord->sevr; + return(0); +} + +long epicsShareAPI dbGetTimeStamp(const struct link *plink,epicsTimeStamp *pstamp) +{ + DBADDR *paddr; + + if (plink->type == CA_LINK) + return dbCaGetTimeStamp(plink,pstamp); + if (plink->type != DB_LINK) + return S_db_notFound; + paddr = (DBADDR *)plink->value.pv_link.pvt; + *pstamp = paddr->precord->time; + return 0; +} diff --git a/src/db/dbAccess.h b/src/db/dbAccess.h new file mode 100644 index 000000000..909774323 --- /dev/null +++ b/src/db/dbAccess.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbAccess.h */ + +#ifndef INCdbAccessh +#define INCdbAccessh + +#include "dbDefs.h" +#include "epicsTime.h" +#include "caeventmask.h" +#include "dbFldTypes.h" +#include "link.h" +#include "dbBase.h" +#include "shareLib.h" +#include "dbAddr.h" +#include "dbLock.h" +#include "dbAccessDefs.h" +#include "dbCa.h" +#include "dbCommon.h" +#include "db_field_log.h" + +#endif /*INCdbAccessh*/ diff --git a/src/db/dbAccessDefs.h b/src/db/dbAccessDefs.h new file mode 100644 index 000000000..7ac36a7c2 --- /dev/null +++ b/src/db/dbAccessDefs.h @@ -0,0 +1,289 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbAccessDefs.h */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +#ifndef INCdbAccessDefsh +#define INCdbAccessDefsh + +#ifdef epicsExportSharedSymbols +# define INCLdb_accessh_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "epicsTypes.h" +#include "epicsTime.h" + +#ifdef INCLdb_accessh_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +epicsShareExtern struct dbBase *pdbbase; +epicsShareExtern volatile int interruptAccept; + +/* The database field and request types are defined in dbFldTypes.h*/ +/* Data Base Request Options */ +#define DBR_STATUS 0x00000001 +#define DBR_UNITS 0x00000002 +#define DBR_PRECISION 0x00000004 +#define DBR_TIME 0x00000008 +#define DBR_ENUM_STRS 0x00000010 +#define DBR_GR_LONG 0x00000020 +#define DBR_GR_DOUBLE 0x00000040 +#define DBR_CTRL_LONG 0x00000080 +#define DBR_CTRL_DOUBLE 0x00000100 +#define DBR_AL_LONG 0x00000200 +#define DBR_AL_DOUBLE 0x00000400 + +/********************************************************************** + * The next page contains macros for defining requests. + * As an example the following defines a buffer to accept an array + * of 10 float values + DBR_STATUS and DBR_TIME options + * + * struct { + * DBRstatus + * DBRtime + * epicsFloat32 value[10] + * } buffer; + * + * IMPORTANT!! The DBRoptions must be given in the order that they + * appear in the Data Base Request Options #defines + * + * The associated dbGetField call is: + * + * long options,number_elements; + * ... + * options = DBR_STATUS|DBR_TIME; + * number_elements = 10; + * rtnval=dbGetField(paddr,DBR_FLOAT,&buffer,&options,&number_elements); + * + * When dbGetField returns: + * rtnval is error status (0 means success) + * options has a bit set for each option that was accepted + * number_elements is actual number of elements obtained + * + * The individual items can be refered to by the expressions:: + * + * buffer.status + * buffer.severity + * buffer.err_status + * buffer.epoch_seconds + * buffer.nano_seconds + * buffer.value[i] + * + * The following is also a valid declaration: + * + * typedef struct { + * DBRstatus + * DBRtime + * epicsFloat32 value[10] + * } MYBUFFER; + * + * With this definition you can give definitions such as the following: + * + * MYBUFFER *pbuf1; + * MYBUFFER buf; + *************************************************************************/ + +/* Macros for defining each option */ +#define DBRstatus \ + epicsUInt16 status; /* alarm status */\ + epicsUInt16 severity; /* alarm severity*/\ + epicsUInt16 acks; /* alarm ack severity*/\ + epicsUInt16 ackt; /* Acknowledge transient alarms?*/ +#define DB_UNITS_SIZE 16 +#define DBRunits \ + char units[DB_UNITS_SIZE]; /* units */ +#define DBRprecision union { \ + long dp; /* number of decimal places*/\ + double unused; /* for alignment */\ + } precision; + /* precision.dp must be long to match the pointer arguments to + * RSET->get_precision() and recGblGetPrec(), which it's + * too late to change now. DBRprecision must be padded to + * maintain 8-byte alignment. */ +#define DBRtime \ + epicsTimeStamp time; /* time stamp*/ +#define DBRenumStrs \ + epicsUInt32 no_str; /* number of strings*/\ + epicsInt32 padenumStrs; /*padding to force 8 byte align*/\ + char strs[DB_MAX_CHOICES][MAX_STRING_SIZE]; /* string values */ +#define DBRgrLong \ + epicsInt32 upper_disp_limit; /*upper limit of graph*/\ + epicsInt32 lower_disp_limit; /*lower limit of graph*/ +#define DBRgrDouble \ + epicsFloat64 upper_disp_limit; /*upper limit of graph*/\ + epicsFloat64 lower_disp_limit; /*lower limit of graph*/ +#define DBRctrlLong \ + epicsInt32 upper_ctrl_limit; /*upper limit of graph*/\ + epicsInt32 lower_ctrl_limit; /*lower limit of graph*/ +#define DBRctrlDouble \ + epicsFloat64 upper_ctrl_limit; /*upper limit of graph*/\ + epicsFloat64 lower_ctrl_limit; /*lower limit of graph*/ +#define DBRalLong \ + epicsInt32 upper_alarm_limit;\ + epicsInt32 upper_warning_limit;\ + epicsInt32 lower_warning_limit;\ + epicsInt32 lower_alarm_limit; +#define DBRalDouble \ + epicsFloat64 upper_alarm_limit;\ + epicsFloat64 upper_warning_limit;\ + epicsFloat64 lower_warning_limit;\ + epicsFloat64 lower_alarm_limit; + +/* structures for each option type */ +struct dbr_status {DBRstatus}; +struct dbr_units {DBRunits}; +struct dbr_precision {DBRprecision}; +struct dbr_time {DBRtime}; +struct dbr_enumStrs {DBRenumStrs}; +struct dbr_grLong {DBRgrLong}; +struct dbr_grDouble {DBRgrDouble}; +struct dbr_ctrlLong {DBRctrlLong}; +struct dbr_ctrlDouble {DBRctrlDouble}; +struct dbr_alLong {DBRalLong}; +struct dbr_alDouble {DBRalDouble}; +/* sizes for each option structure */ +#define dbr_status_size sizeof(struct dbr_status) +#define dbr_units_size sizeof(struct dbr_units) +#define dbr_precision_size sizeof(struct dbr_precision) +#define dbr_time_size sizeof(struct dbr_time) +#define dbr_enumStrs_size sizeof(struct dbr_enumStrs) +#define dbr_grLong_size sizeof(struct dbr_grLong) +#define dbr_grDouble_size sizeof(struct dbr_grDouble) +#define dbr_ctrlLong_size sizeof(struct dbr_ctrlLong) +#define dbr_ctrlDouble_size sizeof(struct dbr_ctrlDouble) +#define dbr_alLong_size sizeof(struct dbr_alLong) +#define dbr_alDouble_size sizeof(struct dbr_alDouble) + +#ifndef INCerrMdefh +#include "errMdef.h" +#endif +#define S_db_notFound (M_dbAccess| 1) /*Process Variable Not Found*/ +#define S_db_badDbrtype (M_dbAccess| 3) /*Illegal Database Request Type*/ +#define S_db_noMod (M_dbAccess| 5) /*Attempt to modify noMod field*/ +#define S_db_badLset (M_dbAccess| 7) /*Illegal Lock Set*/ +#define S_db_precision (M_dbAccess| 9) /*get precision failed */ +#define S_db_onlyOne (M_dbAccess|11) /*Only one element allowed*/ +#define S_db_badChoice (M_dbAccess|13) /*Illegal choice*/ +#define S_db_badField (M_dbAccess|15) /*Illegal field value*/ +#define S_db_lsetLogic (M_dbAccess|17) /*Logic error generating lock sets*/ +#define S_db_noRSET (M_dbAccess|31) /*missing record support entry table*/ +#define S_db_noSupport (M_dbAccess|33) /*RSET or DSXT routine not defined*/ +#define S_db_BadSub (M_dbAccess|35) /*Subroutine not found*/ +/*!!!! Do not change next line without changing src/rsrv/server.h!!!!!!!!*/ +#define S_db_Pending (M_dbAccess|37) /*Request is pending*/ + +#define S_db_Blocked (M_dbAccess|39) /*Request is Blocked*/ +#define S_db_putDisabled (M_dbAccess|41) /*putFields are disabled*/ +#define S_db_badHWaddr (M_dbAccess|43) /*Hardware link type not on INP/OUT*/ +#define S_db_bkptSet (M_dbAccess|53) /*Breakpoint already set*/ +#define S_db_bkptNotSet (M_dbAccess|55) /*No breakpoint set in record*/ +#define S_db_notStopped (M_dbAccess|57) /*Record not stopped*/ +#define S_db_errArg (M_dbAccess|59) /*Error in argument*/ +#define S_db_bkptLogic (M_dbAccess|61) /*Logic error in breakpoint routine*/ +#define S_db_cntSpwn (M_dbAccess|63) /*Cannot spawn dbContTask*/ +#define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/ +#define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ + +/* Global Database Access Routines*/ +#define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \ + ( ( ( (PLNK)->type == CONSTANT ) && \ + ( (NREQUEST) == 0) &&\ + ( (OPTIONS) == 0) ) \ + ? 0 \ + : dbGetLinkValue((PLNK),(DBRTYPE), \ + (void *)(PBUFFER), (OPTIONS), (NREQUEST) ) ) +#define dbPutLink(PLNK, DBRTYPE, PBUFFER, NREQUEST) \ + ( ( (PLNK)->type == CONSTANT) \ + ? 0 \ + : dbPutLinkValue( (PLNK), (DBRTYPE), (void *)(PBUFFER), (NREQUEST) ) ) +#define dbGetPdbAddrFromLink(PLNK) \ + ( ( (PLNK)->type != DB_LINK ) \ + ? 0 \ + : ( ( (struct dbAddr *)( (PLNK)->value.pv_link.pvt) ) ) ) +#define dbGetSevr(PLINK,PSEVERITY) \ + dbGetAlarm((PLINK),NULL,(PSEVERITY)) + +epicsShareFunc long epicsShareAPI dbPutSpecial(struct dbAddr *paddr,int pass); +epicsShareFunc struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr); +epicsShareFunc long epicsShareAPI dbPutAttribute( + const char *recordTypename,const char *name,const char*value); +epicsShareFunc int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes); +epicsShareFunc int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr); +epicsShareFunc long epicsShareAPI dbGetNelements( + const struct link *plink,long *nelements); +epicsShareFunc int epicsShareAPI dbIsLinkConnected(const struct link *plink); +epicsShareFunc int epicsShareAPI dbGetLinkDBFtype(const struct link *plink); +epicsShareFunc long epicsShareAPI dbScanLink( + struct dbCommon *pfrom, struct dbCommon *pto); +epicsShareFunc long epicsShareAPI dbScanPassive( + struct dbCommon *pfrom,struct dbCommon *pto); +epicsShareFunc void epicsShareAPI dbScanFwdLink(struct link *plink); +epicsShareFunc long epicsShareAPI dbProcess(struct dbCommon *precord); +epicsShareFunc long epicsShareAPI dbNameToAddr( + const char *pname,struct dbAddr *); +epicsShareFunc devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp); +epicsShareFunc devSup* epicsShareAPI dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset); +epicsShareFunc long epicsShareAPI dbGetLinkValue( + struct link *,short dbrType,void *pbuffer,long *options,long *nRequest); +epicsShareFunc long epicsShareAPI dbGetField( + struct dbAddr *,short dbrType,void *pbuffer,long *options, + long *nRequest,void *pfl); +epicsShareFunc long epicsShareAPI dbGet( + struct dbAddr *,short dbrType,void *pbuffer,long *options, + long *nRequest,void *pfl); +epicsShareFunc long epicsShareAPI dbPutLinkValue( + struct link *,short dbrType,const void *pbuffer,long nRequest); +epicsShareFunc long epicsShareAPI dbPutField( + struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); +epicsShareFunc long epicsShareAPI dbPut( + struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); + +/* various utility routines */ +epicsShareFunc long epicsShareAPI dbGetControlLimits( + const struct link *plink,double *low, double *high); +epicsShareFunc long epicsShareAPI dbGetGraphicLimits( + const struct link *plink,double *low, double *high); +epicsShareFunc long epicsShareAPI dbGetAlarmLimits( + const struct link *plink,double *lolo, double *low, double *high, double *hihi); +epicsShareFunc long epicsShareAPI dbGetPrecision( + const struct link *plink,short *precision); +epicsShareFunc long epicsShareAPI dbGetUnits( + const struct link *plink,char *units,int unitsSize); +epicsShareFunc long epicsShareAPI dbGetAlarm( + const struct link *plink, epicsEnum16 *status,epicsEnum16 *severity); +epicsShareFunc long epicsShareAPI dbGetTimeStamp( + const struct link *plink,epicsTimeStamp *pstamp); + +typedef void(*SPC_ASCALLBACK)(struct dbCommon *); +/*dbSpcAsRegisterCallback called by access security */ +epicsShareFunc void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func); +epicsShareFunc long epicsShareAPI dbBufferSize( + short dbrType,long options,long nRequest); +epicsShareFunc long epicsShareAPI dbValueSize(short dbrType); + +epicsShareFunc int epicsShareAPI dbLoadDatabase( + const char *filename, const char *path, const char *substitutions); + +epicsShareFunc int epicsShareAPI dbLoadRecords( + const char* filename, const char* substitutions); + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbAccessDefsh*/ diff --git a/src/db/dbAddr.h b/src/db/dbAddr.h new file mode 100644 index 000000000..d7fdd94e9 --- /dev/null +++ b/src/db/dbAddr.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef dbAddrh +#define dbAddrh + +struct dbCommon; +struct dbFldDes; + +typedef struct dbAddr { + struct dbCommon *precord; /* address of record */ + void *pfield; /* address of field */ + struct dbFldDes *pfldDes; /* address of struct fldDes */ + long no_elements; /* number of elements (arrays) */ + short field_type; /* type of database field */ + short field_size; /* size of the field being accessed */ + short special; /* special processing */ + short dbr_field_type; /* field type as seen by database request*/ + /* DBR_STRING,...,DBR_ENUM,DBR_NOACCESS */ +} dbAddr; + +typedef dbAddr DBADDR; + +unsigned dbNameOfPV (const dbAddr * paddr, char * pBuf, unsigned bufLen); +unsigned dbNameSizeOfPV (const dbAddr * paddr); + +#endif /* dbAddrh */ diff --git a/src/db/dbBkpt.c b/src/db/dbBkpt.c new file mode 100644 index 000000000..c79d4ed74 --- /dev/null +++ b/src/db/dbBkpt.c @@ -0,0 +1,970 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbBkpt.c */ +/* base/src/db Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Matthew Needes + * Date: 8-30-93 +*/ + +/* + * Database Breakpoint Manipulation and User Interface + * + * USER COMMANDS + * dbb(record_name) Set a breakpoint in a record + * dbd(record_name) Delete a record's breakpoint + * dbc(record_name) Resume record processing + * dbs(record_name) Step through record processing through + * IO links, forward process links, etc. + * dbstat() Display status of stopped records in lock sets. + * dbap(record_name) Toggle automatic print after processing. + * dbp(record_name) Print out fields from record currently stopped. + * dbprc(record_name) Processes a record once without printing it. + * (Unless autoprint is on) + * + * INTERNAL FUNCTIONS + * dbBkpt() Process breakpoints, called by dbProcess(). + * dbPrint() Prints record if autoprint enabled. + * dbBkptCont() The task that continues and steps through + * records that are stopped at a breakpoint. + */ + +/* #define BKPT_DIAG */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsTime.h" +#include "ellLib.h" +#include "errlog.h" +#include "alarm.h" +#include "dbBase.h" +#include "dbFldTypes.h" +#include "link.h" +#include "dbCommon.h" +#include "dbFldTypes.h" +#include "db_field_log.h" +#include "errMdef.h" +#include "recSup.h" +#include "special.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbAccessDefs.h" +#include "dbScan.h" +#include "dbLock.h" +#include "recGbl.h" +#include "dbTest.h" +#include "dbBkpt.h" + +/* private routines */ +static void dbBkptCont(dbCommon *precord); +static long FIND_CONT_NODE( + const char *record_name, + struct LS_LIST **ppnode, + struct dbCommon **pprecord); + +/* + * Breakpoints are used as a debugging instrument to suspend the + * processing of database records. Once suspended, record + * processing may continue if either a continue (dbc()) or a + * step (dbs()) command is then issued. The current record's + * contents may be printed either with dbp(), or immediately + * after processing (use dbap() to toggle the BKPT_PRINT bit). + * + * dbb() and dbd() add a breakpoint to a record or delete one + * from a record. dbstat() prints out comprehensive breakpoint + * status information. + * + * Breakpoints may be set on a per lockset basis. When a + * breakpoint is set in a lockset, a new task is created. A + * separate task gets created for _every_ lockset containing + * a breakpoint. Thus multiple locksets may be debugged + * simultaneously. The breakpoint handler then schedules future + * processing in that lockset to this task. The separate task is + * used so that locksets that do not have breakpoints are isolated + * from locksets that do. This allows the processing of other + * locksets to continue uninterupted, even if they exist on the same + * scan list as a lockset containing a breakpoint. + * + * An entrypoint is the first record that gets processed in a lockset. + * This type of record is the basis for subsequent recursive executions + * of dbProcess(). The breakpoint handler monitors and schedules + * these entrypoints to the breakpoint tasks. + * + * Two hooks have been inserted in dbProcess() to manage breakpoints, + * dbBkpt() and dbPrint(). The former does two things: + * + * 1. Schedule entrypoints with the breakpoint task. + * 2. Suspend record processing when a breakpoint is detected. + * + * 1 occurs only if dbProcess() is called outside of the breakpoint + * task. Number 2 only occurs when dbProcess() is called from + * _within_ the breakpoint task's context. Number 1 is used for + * detection and scheduling, while 2 is used for suspending the task. + * + * The dbPrint() hook is used to print out a record's contents immediately + * _after_ a record has been processed. + * + * The dbBkptCont, or breakpoint task, pends on a semaphore that gets + * released whenever new entrypoints are scheduled for it. When + * released, this task then runs down its entrypoint queue and + * processes each entrypoint in turn. In this context, dbProcess + * will execute the dbBkpt() hook in mode 2, allowing this task to + * be suspended whenever a breakpoint is detected. + * + * NOTE: This is not a very "real-time" implementation (even for those + * locksets not containing a breakpoint). I may fix this later. + * + * Final comment: The scary thing is, I don't think this can be done + * more simply... + * + */ + +/* + * Flag used by dbProcess() to determine if there are + * any breakpoints. This is so that there is only + * a single comparison in the critical path during + * normal record execution, i.e. when there aren't + * any breakpoints set. + */ +long lset_stack_count = 0; + +/* + * Stack--in which each entry represents a different + * lock set with either breakpoints and/or stopped + * execution. (Breakpoints may be disabled even + * though execution is stopped). The order of the + * list is maintained so that the entry on the top + * of stack is used as a default for dbc() and dbs(). + * The semaphore is used to prevent conflicts while + * operating with this stack. + */ +static ELLLIST lset_stack = ELLLIST_INIT; +static epicsMutexId bkpt_stack_sem = 0; + +/* + * Stores the last lockset continued or stepped from. + * dbs() and dbc() will print a message if the current + * lockset to be continued from differs from this + * variable. + */ +static unsigned long last_lset = 0; + +/* + * FIND_LOCKSET() finds the stack entry + * whose l_num field matches precord's + * lset field. The node that is found + * is returned in "pnode." + */ +#define FIND_LOCKSET(precord, pnode) \ + pnode = (struct LS_LIST *) ellFirst(&lset_stack); \ + while ((pnode) != NULL) { \ + if (pnode->l_num == dbLockGetLockId(precord)) break; \ + pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode); \ + } \ + +/* + * FIND_QUEUE_ENTRY() matches entries in an + * entry point queue. pep_queue is the queue + * being searched, pqe is the pointer to the + * queue entry found, and precord is the record + * being searched for in *pep_queue. + */ +#define FIND_QUEUE_ENTRY(pep_queue, pqe, precord) \ + pqe = (struct EP_LIST *) ellFirst(pep_queue); \ + while ((pqe) != NULL) { \ + if ((pqe)->entrypoint == (precord)) break; \ + pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe); \ + } \ + +/* + * Fills out pnode and precord structures for dbc() and dbs() + * MUST LOCK OUT STACK BEFORE ENTRY + */ +static long FIND_CONT_NODE( + const char *record_name, + struct LS_LIST **ppnode, + struct dbCommon **pprecord) +{ + struct dbAddr addr; + struct LS_LIST *pnode; + struct dbCommon *precord = NULL; + long status = 0; + + if (record_name == NULL) { + /* + * Search through stack, taking the first entry that + * is currently stopped at a breakpoint. + */ + pnode = (struct LS_LIST *) ellFirst(&lset_stack); + while (pnode != NULL) { + if (pnode->precord != NULL) { + precord = pnode->precord; + break; + } + pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode); + } + + if (pnode == NULL) { + printf(" BKPT> No records are currently stopped\n"); + return(S_db_notStopped); + } + } + else { + /* + * Convert name to address + */ + status = dbNameToAddr(record_name, &addr); + if (status == S_db_notFound) + printf(" BKPT> Record %s not found\n", record_name); + if (status != 0) + return(status); + + precord = addr.precord; + + FIND_LOCKSET(precord, pnode); + + if (pnode == NULL || pnode->precord == NULL) { + printf(" BKPT> Currently not stopped in this lockset\n"); + return(S_db_notStopped); + } + } + + *pprecord = precord; + *ppnode = pnode; + return(0); +} + + +/* + * Initialise the breakpoint stack + */ +void epicsShareAPI dbBkptInit(void) +{ + if (! bkpt_stack_sem) { + bkpt_stack_sem = epicsMutexMustCreate(); + lset_stack_count = 0; + } +} + +/* + * Add breakpoint to a lock set + * 1. Convert name to address and check breakpoint mask. + * 2. Lock database. + * 3. If empty, initialize lock set stack and its semaphore. + * 4. Take that semaphore. + * 5. Find lockset in the list. If it doesn't exist, create it. + * 6. Turn on breakpoint field in record. + * 7. Add breakpoint to list of breakpoints in structure. + * 8. Spawn continuation task if it isn't already running. + */ +long epicsShareAPI dbb(const char *record_name) +{ + struct dbAddr addr; + struct LS_LIST *pnode; + struct BP_LIST *pbl; + struct dbCommon *precord; + long status; + + /* + * Convert name to address + */ + status = dbNameToAddr(record_name, &addr); + if (status == S_db_notFound) + printf(" BKPT> Record %s not found\n", record_name); + if (status != 0) return(status); + + precord = addr.precord; + + if (precord->bkpt & BKPT_ON_MASK) { + printf(" BKPT> Breakpoint already set in this record\n"); + return(S_db_bkptSet); + } + + dbScanLock(precord); + + /* + * Add lock set to the stack of lock sets that + * contain breakpoints and/or stopped records. + */ + + epicsMutexMustLock(bkpt_stack_sem); + + FIND_LOCKSET(precord, pnode); + + if (pnode == NULL) { + /* lockset not found, create node, add to end of list */ + pnode = (struct LS_LIST *) malloc(sizeof(struct LS_LIST)); + if (pnode == NULL) { + printf(" BKPT> Out of memory\n"); + dbScanUnlock(precord); + epicsMutexUnlock(bkpt_stack_sem); + return(1); + } + pnode->precord = NULL; + + /* initialize breakpoint list */ + ellInit(&pnode->bp_list); + + /* initialize entry point queue */ + ellInit(&pnode->ep_queue); + + /* create execution semaphore */ + pnode->ex_sem = epicsEventCreate(epicsEventEmpty); + if (pnode->ex_sem == NULL) { + printf(" BKPT> Out of memory\n"); + dbScanUnlock(precord); + epicsMutexUnlock(bkpt_stack_sem); + return(1); + } + + pnode->taskid = 0; + pnode->step = 0; + pnode->l_num = dbLockGetLockId(precord); + + ellAdd(&lset_stack, (ELLNODE *)pnode); + ++lset_stack_count; + } + + /* + * Add record to breakpoint list + */ + pbl = (struct BP_LIST *) malloc(sizeof(struct BP_LIST)); + if (pbl == NULL) { + printf(" BKPT> Out of memory\n"); + dbScanUnlock(precord); + epicsMutexUnlock(bkpt_stack_sem); + return(1); + } + pbl->precord = precord; + ellAdd(&pnode->bp_list, (ELLNODE *)pbl); + + /* + * Turn on breakpoint field in record + */ + precord->bkpt |= BKPT_ON_MASK; + + if (! pnode->taskid) { + +#ifdef BKPT_DIAG + printf(" BKPT> Spawning task: %s\n", precord->name); +#endif + /* + * Spawn continuation task + */ + pnode->taskid = epicsThreadCreate("bkptCont",epicsThreadPriorityScanLow-1, + epicsThreadGetStackSize(epicsThreadStackBig), + (EPICSTHREADFUNC)dbBkptCont,precord); + if (pnode->taskid == 0) { + printf(" BKPT> Cannot spawn task to process record\n"); + pnode->taskid = 0; + dbScanUnlock(precord); + epicsMutexUnlock(bkpt_stack_sem); + return(1); + } + } + + epicsMutexUnlock(bkpt_stack_sem); + dbScanUnlock(precord); + return(0); +} + +/* + * Remove breakpoint from a record + * 1. Convert name to address and check breakpoint mask. + * 2. Lock database and take stack semaphore. + * 3. Find structure for record's lockset (in stack). + * 4. Find and delete record from breakpoint list. + * 5. Turn off break point field. + * 6. Give up semaphore to "signal" bkptCont task to quit. + */ +long epicsShareAPI dbd(const char *record_name) +{ + struct dbAddr addr; + struct LS_LIST *pnode; + struct BP_LIST *pbl; + struct dbCommon *precord; + long status; + + /* + * Convert name to address + */ + status = dbNameToAddr(record_name, &addr); + if (status == S_db_notFound) + printf(" BKPT> Record %s not found\n", record_name); + if (status != 0) return(status); + + precord = addr.precord; + + if (! precord->bkpt & BKPT_ON_MASK) { + printf(" BKPT> No breakpoint set in this record\n"); + return(S_db_bkptNotSet); + } + + dbScanLock(precord); + + epicsMutexMustLock(bkpt_stack_sem); + + FIND_LOCKSET(precord, pnode); + + if (pnode == NULL) { + /* not found, error ! */ + printf(" BKPT> Logic Error in dbd()\n"); + precord->bkpt &= BKPT_OFF_MASK; + + epicsMutexUnlock(bkpt_stack_sem); + dbScanUnlock(precord); + return(S_db_bkptLogic); + } + + /* + * Remove record from breakpoint list + */ + + /* find record in list */ + pbl = (struct BP_LIST *) ellFirst(&pnode->bp_list); + while (pbl != NULL) { + if (pbl->precord == precord) { + ellDelete(&pnode->bp_list, (ELLNODE *)pbl); + free(pbl); + break; + } + pbl = (struct BP_LIST *) ellNext((ELLNODE *)pbl); + } + + if (pbl == NULL) { + printf(" BKPT> Logic Error in dbd()\n"); + precord->bkpt &= BKPT_OFF_MASK; + epicsMutexUnlock(bkpt_stack_sem); + dbScanUnlock(precord); + return(S_db_bkptLogic); + } + + /* + * Turn off breakpoint field in record + */ + precord->bkpt &= BKPT_OFF_MASK; + + /* + * If there are no more breakpoints, give up semaphore + * to cause the bkptCont task to quit. + */ + if (ellCount(&pnode->bp_list) == 0) + epicsEventSignal(pnode->ex_sem); + + epicsMutexUnlock(bkpt_stack_sem); + + dbScanUnlock(precord); + return(0); +} + +/* + * Continue processing in a lock set + * 1. Find top node in the lockset stack. + * 2. Turn off stepping mode. + * 2. Resume dbBkptCont. + */ +long epicsShareAPI dbc(const char *record_name) +{ + struct LS_LIST *pnode; + struct dbCommon *precord = NULL; + long status = 0; + + epicsMutexMustLock(bkpt_stack_sem); + + status = FIND_CONT_NODE(record_name, &pnode, &precord); + if (status) { + epicsMutexUnlock(bkpt_stack_sem); + return(status); + } + + if (record_name == NULL && last_lset != pnode->l_num) + printf(" BKPT> Continuing: %s\n", pnode->precord->name); + + last_lset = pnode->l_num; + + /* + * Turn off stepping mode + */ + pnode->step = 0; + + /* + * Resume dbBkptCont() until dbProcess() is executed + * for a record with a breakpoint. This occurs + * because stepping mode has been switched off. + */ + epicsThreadResume(pnode->taskid); + epicsMutexUnlock(bkpt_stack_sem); + return(0); +} + +/* + * Step through record processing + * 1. Find top node in lockset stack. + * 2. Resume dbBkptCont. + */ +long epicsShareAPI dbs(const char *record_name) +{ + struct LS_LIST *pnode; + struct dbCommon *precord = NULL; + long status = 0; + + epicsMutexMustLock(bkpt_stack_sem); + + status = FIND_CONT_NODE(record_name, &pnode, &precord); + if (status) { + epicsMutexUnlock(bkpt_stack_sem); + return(status); + } + + if (last_lset != pnode->l_num && record_name == NULL) + printf(" BKPT> Stepping: %s\n", pnode->precord->name); + + last_lset = pnode->l_num; + + epicsThreadResume(pnode->taskid); + epicsMutexUnlock(bkpt_stack_sem); + return(0); +} + +/* + * Task for continuing record processing + * 1. Find lockset in stack for precord. + * DO 2-3 while breakpoints exist in the lockset. + * 2. Wait on execution semaphore ... + * 3. Run through every entrypoint in queue, processing + * those that are scheduled. + * 4. Free resources for lockset, and exit task. + */ +static void dbBkptCont(dbCommon *precord) +{ + struct LS_LIST *pnode; + struct EP_LIST *pqe = NULL; + + /* + * Reset breakpoint, process record, and + * reset bkpt field in record + */ + epicsMutexMustLock(bkpt_stack_sem); + + FIND_LOCKSET(precord, pnode); + + if (pnode == NULL) { + printf(" BKPT> Logic error in dbBkptCont()\n"); + return; + } + + /* + * For every entrypoint scheduled, process. Run process + * until there are no more breakpoints remaining in a + * lock set. + */ + do { + /* Give up semaphore before waiting to run ... */ + epicsMutexUnlock(bkpt_stack_sem); + + /* Wait to run */ + epicsEventMustWait(pnode->ex_sem); + + /* Bkpt stack must still be stable ! */ + epicsMutexMustLock(bkpt_stack_sem); + + pqe = (struct EP_LIST *) ellFirst(&pnode->ep_queue); + + /* Run through entrypoint queue */ + while (pqe != NULL) { + /* check if entrypoint is currently scheduled */ + if (pqe->sched) { + /* save current entrypoint */ + pnode->current_ep = pqe->entrypoint; + + /* lock the lockset, process record, unlock */ + dbScanLock(precord); + dbProcess(pqe->entrypoint); + dbScanUnlock(precord); + + /* reset schedule and stepping flag - Do this AFTER processing */ + pqe->sched = 0; + pnode->step = 0; + } + pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe); + } + + /* Reset precord. (Since no records are at a breakpoint) */ + pnode->precord = NULL; + } while (ellCount(&pnode->bp_list) != 0); + + /* remove node from lockset stack */ + ellDelete(&lset_stack, (ELLNODE *)pnode); + --lset_stack_count; + + /* free entrypoint queue */ + ellFree(&pnode->ep_queue); + + /* remove execution semaphore */ + epicsEventDestroy(pnode->ex_sem); + + printf("\n BKPT> End debug of lockset %lu\n-> ", pnode->l_num); + + /* free list node */ + free(pnode); + + epicsMutexUnlock(bkpt_stack_sem); +} + +/* + * Process breakpoint + * Returns a zero if dbProcess() is to execute + * record support, a one if dbProcess() is to + * skip over record support. See dbProcess(). + * + * 1. See if there is at least a breakpoint set somewhere + * in precord's lockset. If not, return immediately. + * 2. Check the disable flag. + * 3. Add entry points to the queue for future stepping and + * schedule new entrypoints for the continuation task. + * 4. Check the pact flag. + * 5. Check to see if there is a breakpoint set in a record, and + * if so, turn on stepping mode. + * 6. If stepping mode is set, stop and report the breakpoint. + */ +int epicsShareAPI dbBkpt(dbCommon *precord) +{ + struct LS_LIST *pnode; + struct EP_LIST *pqe; + + /* + * It is crucial that operations in dbBkpt() execute + * in the correct order or certain features in the + * breakpoint handler will not work as expected. + */ + + /* + * Take and give a semaphore to check for breakpoints + * every time a record is processed. Slow. Thank + * goodness breakpoint checking is turned off during + * normal operation. + */ + epicsMutexMustLock(bkpt_stack_sem); + FIND_LOCKSET(precord, pnode); + epicsMutexUnlock(bkpt_stack_sem); + + if (pnode == NULL) { + /* no breakpoints in precord's lockset */ + return(0); + } + + /* Check disable flag */ + dbGetLink(&(precord->sdis),DBR_SHORT,&(precord->disa),0,0); + if (precord->disa == precord->disv) { + /* + * Do not process breakpoints if the record is disabled, + * but allow disable alarms. Alarms will be raised + * in dbProcess() because returning 0 allows dbProcess() + * to continue. However processing will be prevented + * because disa and disv will be examined again in + * dbProcess(). Note that checking for pact will occur + * before checking for disa and disv in dbProcess(). + */ + return(0); + } + + /* + * Queue entry points for future stepping. The taskid comparison + * is used to determine if the source of processing is the + * continuation task or an external source. If it is an external + * source, queue its execution, but dump out of dbProcess without + * calling record support. + */ + if (pnode->taskid && (epicsThreadGetIdSelf() != pnode->taskid)) { + /* CONTINUE TASK CANNOT ENTER HERE */ + + /* + * Add an entry point to queue, if it does + * not already exist. + */ + FIND_QUEUE_ENTRY(&pnode->ep_queue, pqe, precord); + + if (pqe == NULL) { + + pqe = (struct EP_LIST *) malloc(sizeof(struct EP_LIST)); + if (pqe == NULL) + return(1); + + + pqe->entrypoint = precord; + pqe->count = 1; + epicsTimeGetCurrent(&pqe->time); + pqe->sched = 0; + +#ifdef BKPT_DIAG + printf(" BKPT> Adding entrypoint %s to queue\n", precord->name); +#endif + + /* + * Take semaphore, wait on continuation task + */ + epicsMutexMustLock(bkpt_stack_sem); + + /* Add entry to queue */ + ellAdd(&pnode->ep_queue, (ELLNODE *)pqe); + + epicsMutexUnlock(bkpt_stack_sem); + } + else { + if (pqe->count < MAX_EP_COUNT) + pqe->count++; + } + + /* check pact */ + if (! precord->pact) { + /* schedule if pact not set */ + pqe->sched = 1; + + /* + * Release the semaphore, letting the continuation + * task begin execution of the new entrypoint. + */ + epicsEventSignal(pnode->ex_sem); + } + return(1); + } + + /* + * Don't mess with breakpoints if pact set! Skip + * over rest of dbProcess() since we don't want + * alarms going off. The pact flag is checked + * AFTER entry point queuing so that the record + * timing feature will work properly. + */ + if (precord->pact) + return(1); + + /* Turn on stepping mode if a breakpoint is found */ + if (precord->bkpt & BKPT_ON_MASK) { + pnode->step = 1; + +#ifdef BKPT_DIAG + printf(" BKPT> Bkpt detected: %s\n", precord->name); +#endif + } + + /* + * If we are currently stepping through the lockset, + * suspend task. + */ + if (pnode->step) { + printf("\n BKPT> Stopped at: %s within Entrypoint: %s\n-> ", + precord->name, pnode->current_ep->name); + + pnode->precord = precord; + + /* Move current lockset to top of stack */ + ellDelete(&lset_stack, (ELLNODE *)pnode); + ellInsert(&lset_stack, NULL, (ELLNODE *)pnode); + /* + * Unlock database while the task suspends itself. This + * is done so that dbb() dbd() dbc() dbs() may be used + * when the task is suspended. Scan tasks that also + * use the scan lock feature will not be hung during + * a breakpoint, so that records in other locksets will + * continue to be processed. Cross your fingers, this + * might actually work ! + */ + epicsMutexUnlock(bkpt_stack_sem); + dbScanUnlock(precord); + epicsThreadSuspendSelf(); + dbScanLock(precord); + epicsMutexMustLock(bkpt_stack_sem); + } + return(0); +} + +/* print record after processing */ +void epicsShareAPI dbPrint(dbCommon *precord ) +{ + struct LS_LIST *pnode; + + if (! (precord->bkpt & BKPT_PRINT_MASK)) + return; + + FIND_LOCKSET(precord, pnode); + + /* do not print if lockset does not currently contain breakpoints */ + if (pnode == NULL) + return; + + printf("\n"); + dbpr(precord->name, 2); + printf("-> "); +} + +/* print stopped record */ +long epicsShareAPI dbp(const char *record_name, int interest_level) +{ + struct LS_LIST *pnode; + struct dbCommon *precord; + int status; + + epicsMutexMustLock(bkpt_stack_sem); + + /* find pnode and precord pointers */ + status = FIND_CONT_NODE(record_name, &pnode, &precord); + if (status) { + epicsMutexUnlock(bkpt_stack_sem); + return(status); + } + + /* print out record's fields */ + dbpr(precord->name, (interest_level == 0) ? 2 : interest_level); + + epicsMutexUnlock(bkpt_stack_sem); + return(0); +} + +/* toggle printing after processing a certain record */ +long epicsShareAPI dbap(const char *record_name) +{ + struct dbAddr addr; + struct dbCommon *precord; + long status; + + /* + * Convert name to address + */ + status = dbNameToAddr(record_name, &addr); + if (status == S_db_notFound) + printf(" BKPT> Record %s not found\n", record_name); + if (status != 0) return(status); + + precord = addr.precord; + + /* + * Toggle print after process field in record + */ + if (precord->bkpt & BKPT_PRINT_MASK) { + printf(" BKPT> Auto print off for record %s\n", precord->name); + precord->bkpt &= BKPT_PRINT_OFF_MASK; + } + else { + printf(" BKPT> Auto print on for record %s\n", precord->name); + precord->bkpt |= BKPT_PRINT_MASK; + } + + return(0); +} + +/* print list of stopped records, and breakpoints set in locksets */ +long epicsShareAPI dbstat(void) +{ + struct LS_LIST *pnode; + struct BP_LIST *pbl; + struct EP_LIST *pqe; + epicsTimeStamp time; + + epicsMutexMustLock(bkpt_stack_sem); + + epicsTimeGetCurrent(&time); + + /* + * Traverse list, reporting stopped records + */ + pnode = (struct LS_LIST *) ellFirst(&lset_stack); + while (pnode != NULL) { + if (pnode->precord != NULL) { + + printf("LSet: %lu Stopped at: %-28.28s #B: %5.5d T: %p\n", + pnode->l_num, pnode->precord->name, ellCount(&pnode->bp_list), pnode->taskid); + + /* for each entrypoint detected, print out entrypoint statistics */ + pqe = (struct EP_LIST *) ellFirst(&pnode->ep_queue); + while (pqe != NULL) { + double diff = epicsTimeDiffInSeconds(&time,&pqe->time); + if (diff) { + printf(" Entrypoint: %-28.28s #C: %5.5lu C/S: %7.1f\n", + pqe->entrypoint->name, pqe->count,diff); + } + pqe = (struct EP_LIST *) ellNext((ELLNODE *)pqe); + } + } + else { + printf("LSet: %lu #B: %5.5d T: %p\n", + pnode->l_num, ellCount(&pnode->bp_list), pnode->taskid); + } + + /* + * Print out breakpoints set in the lock set + */ + pbl = (struct BP_LIST *) ellFirst(&pnode->bp_list); + while (pbl != NULL) { + printf(" Breakpoint: %-28.28s", pbl->precord->name); + + /* display auto print flag */ + if (pbl->precord->bkpt & BKPT_PRINT_MASK) + printf(" (ap)\n"); + else + printf("\n"); + + pbl = (struct BP_LIST *) ellNext((ELLNODE *)pbl); + } + + pnode = (struct LS_LIST *) ellNext((ELLNODE *)pnode); + } + + epicsMutexUnlock(bkpt_stack_sem); + return(0); +} + +/* + * Process a record without printing it. + */ +long epicsShareAPI dbprc(char *record_name) +{ + struct dbAddr addr; + struct dbCommon *precord; + long status; + + /* + * Convert name to address + */ + status = dbNameToAddr(record_name, &addr); + if (status == S_db_notFound) + printf(" BKPT> Record %s not found\n", record_name); + if (status != 0) return(status); + + precord = addr.precord; + + /* lock lockset, process record, unlock lockset */ + dbScanLock(precord); + status = dbProcess(precord); + dbScanUnlock(precord); + + return(status); +} + +#ifdef BKPT_DIAG + +/* Reset breakpoints */ +int dbreset() +{ + epicsMutexUnlock(bkpt_stack_sem); + + return(0); +} + +#endif + diff --git a/src/db/dbBkpt.h b/src/db/dbBkpt.h new file mode 100644 index 000000000..e607089d9 --- /dev/null +++ b/src/db/dbBkpt.h @@ -0,0 +1,104 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbBkpt.h */ +/* base/include Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Matthew Needes + * Date: 8-30-93 + */ + +#ifndef INCdbBkptsh +#define INCdbBkptsh 1 + +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "shareLib.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Structure containing a list of set breakpoints + * in a lockset + */ + +struct BP_LIST { + ELLNODE *next_list; + ELLNODE *prev_list; + struct dbCommon *precord; +}; + +/* + * Structure containing queue of entrypoints + * detected for a lockset. + */ +struct EP_LIST { + ELLNODE *next_list; + ELLNODE *prev_list; + struct dbCommon *entrypoint; /* pointer to entry point in lockset */ + unsigned long count; /* number of times record processed */ + epicsTimeStamp time; /* time record first logged */ + char sched; /* schedule record for next dbContTask() pass */ +}; + +/* + * Structure for stack of lock sets that + * currently contain breakpoints. (uses ellLib) + */ +struct LS_LIST { + ELLNODE *next_list; + ELLNODE *prev_list; + struct dbCommon *precord;/* points to where execution is currently stopped */ + struct dbCommon *current_ep; /* current entrypoint */ + ELLLIST bp_list; /* list of records containing breakpoints in a lockset */ + ELLLIST ep_queue; /* queue of entrypoints found so far */ + epicsEventId ex_sem; /* semaphore for execution queue */ + epicsThreadId taskid; /* saved taskid for the task in stepping mode */ + int step; /* one if currently "stepping," else zero */ + unsigned long l_num; /* lockset number */ +}; + +/* Values for BKPT (breakpoint) field in record */ + +/* 1st bit = 0 if breakpoint is not set, */ +/* 1 if breakpoint set */ +/* 2nd bit = 0 if no printing after processing */ +/* 1 if print after processing set */ + +/* Breakpoint Masks */ +#define BKPT_ON_MASK 0x001 +#define BKPT_OFF_MASK 0x0FE +#define BKPT_PRINT_MASK 0x002 +#define BKPT_PRINT_OFF_MASK 0x0FD + +#define MAX_EP_COUNT 99999 + +epicsShareFunc void epicsShareAPI dbBkptInit(void); +epicsShareFunc long epicsShareAPI dbb(const char *recordname); +epicsShareFunc long epicsShareAPI dbd(const char *recordname); +epicsShareFunc long epicsShareAPI dbc(const char *recordname); +epicsShareFunc long epicsShareAPI dbs(const char *recordname); +epicsShareFunc long epicsShareAPI dbstat(void); +epicsShareFunc long epicsShareAPI dbp( + const char *record_name, int interest_level); +epicsShareFunc long epicsShareAPI dbap(const char *record_name); +epicsShareFunc int epicsShareAPI dbBkpt(struct dbCommon *precord); +epicsShareFunc void epicsShareAPI dbPrint(struct dbCommon *precord); +epicsShareFunc long epicsShareAPI dbprc(char *record_name); + +extern long lset_stack_count; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/db/dbCAC.h b/src/db/dbCAC.h new file mode 100644 index 000000000..363e82a2b --- /dev/null +++ b/src/db/dbCAC.h @@ -0,0 +1,243 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + * NOTES: + * 1) This interface is preliminary and will change in the future + */ + +#ifndef dbCACh +#define dbCACh + +#ifdef epicsExportSharedSymbols +# define dbCACh_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "tsDLList.h" +#include "tsFreeList.h" +#include "resourceLib.h" +#include "cacIO.h" +#include "compilerDependencies.h" +#include "epicsMemory.h" + +#ifdef dbCACh_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +# include "shareLib.h" +#endif + +#include "db_access.h" +#include "dbNotify.h" +#include "dbEvent.h" +#include "dbAddr.h" +#include "dbLock.h" +#include "dbCommon.h" +#include "db_convert.h" +#include "resourceLib.h" + +extern "C" void putNotifyCompletion ( putNotify *ppn ); + +class dbContext; +class dbChannelIO; +class dbPutNotifyBlocker; +class dbSubscriptionIO; + +class dbBaseIO // X aCC 655 + : public chronIntIdRes < dbBaseIO > { +public: + virtual dbSubscriptionIO * isSubscription () = 0; + virtual void show ( epicsGuard < epicsMutex > &, unsigned level ) const = 0; + virtual void show ( unsigned level ) const = 0; + dbBaseIO (); + dbBaseIO ( const dbBaseIO & ); + dbBaseIO & operator = ( const dbBaseIO & ); +protected: + virtual ~dbBaseIO() {} +}; + +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, + int eventsRemaining, struct db_field_log *pfl ); + +class dbSubscriptionIO : + public tsDLNode < dbSubscriptionIO >, + public dbBaseIO { +public: + dbSubscriptionIO ( + epicsGuard < epicsMutex > &, epicsMutex &, + dbContext &, dbChannelIO &, struct dbAddr &, cacStateNotify &, + unsigned type, unsigned long count, unsigned mask, dbEventCtx ); + void destructor ( epicsGuard < epicsMutex > & ); + void unsubscribe ( epicsGuard < epicsMutex > & ); + void channelDeleteException ( epicsGuard < epicsMutex > & ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; + void show ( unsigned level ) const; + void * operator new ( size_t size, + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & )) +private: + epicsMutex & mutex; + unsigned long count; + cacStateNotify & notify; + dbChannelIO & chan; + dbEventSubscription es; + unsigned type; + unsigned id; + dbSubscriptionIO * isSubscription (); + friend void dbSubscriptionEventCallback ( + void * pPrivate, struct dbAddr * paddr, + int eventsRemaining, struct db_field_log * pfl ); + dbSubscriptionIO ( const dbSubscriptionIO & ); + dbSubscriptionIO & operator = ( const dbSubscriptionIO & ); + virtual ~dbSubscriptionIO (); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +class dbContext; + +class dbContextPrivateListOfIO { +public: + dbContextPrivateListOfIO (); + ~dbContextPrivateListOfIO (); +private: + tsDLList < dbSubscriptionIO > eventq; + dbPutNotifyBlocker * pBlocker; + friend class dbContext; + dbContextPrivateListOfIO ( const dbContextPrivateListOfIO & ); + dbContextPrivateListOfIO & operator = ( const dbContextPrivateListOfIO & ); +}; + +class dbContextReadNotifyCacheAllocator { +public: + dbContextReadNotifyCacheAllocator (); + ~dbContextReadNotifyCacheAllocator (); + char * alloc ( unsigned long size ); + void free ( char * pFree ); + void show ( unsigned level ) const; +private: + struct cacheElem_t { + struct cacheElem_t * pNext; + }; + unsigned long _readNotifyCacheSize; + cacheElem_t * _pReadNotifyCache; + void reclaimAllCacheEntries (); + dbContextReadNotifyCacheAllocator ( const dbContextReadNotifyCacheAllocator & ); + dbContextReadNotifyCacheAllocator & operator = ( const dbContextReadNotifyCacheAllocator & ); +}; + +class dbContextReadNotifyCache { +public: + dbContextReadNotifyCache ( epicsMutex & ); + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbAddr & addr, unsigned type, unsigned long count, + cacReadNotify & notify ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; +private: + dbContextReadNotifyCacheAllocator _allocator; + epicsMutex & _mutex; + dbContextReadNotifyCache ( const dbContextReadNotifyCache & ); + dbContextReadNotifyCache & operator = ( const dbContextReadNotifyCache & ); +}; + +class dbContext : public cacContext { +public: + dbContext ( epicsMutex & cbMutex, epicsMutex & mutex, + cacContextNotify & notify ); + virtual ~dbContext (); + void destroyChannel ( epicsGuard < epicsMutex > &, dbChannelIO & ); + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbAddr & addr, unsigned type, unsigned long count, + cacReadNotify & notify ); + void callStateNotify ( struct dbAddr &addr, unsigned type, unsigned long count, + const struct db_field_log * pfl, cacStateNotify & notify ); + void subscribe ( + epicsGuard < epicsMutex > &, + struct dbAddr & addr, dbChannelIO & chan, + unsigned type, unsigned long count, unsigned mask, + cacStateNotify & notify, cacChannel::ioid * pId ); + void initiatePutNotify ( + epicsGuard < epicsMutex > &, dbChannelIO &, struct dbAddr &, + unsigned type, unsigned long count, const void * pValue, + cacWriteNotify & notify, cacChannel::ioid * pId ); + void show ( unsigned level ) const; + void showAllIO ( const dbChannelIO & chan, unsigned level ) const; + void destroyAllIO ( + epicsGuard < epicsMutex > &, dbChannelIO & chan ); + void ioCancel ( epicsGuard < epicsMutex > &, + dbChannelIO & chan, const cacChannel::ioid &id ); + void ioShow ( epicsGuard < epicsMutex > &, + const cacChannel::ioid & id, unsigned level ) const; +private: + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > dbPutNotifyBlockerFreeList; + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > dbSubscriptionIOFreeList; + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > dbChannelIOFreeList; + chronIntIdResTable < dbBaseIO > ioTable; + dbContextReadNotifyCache readNotifyCache; + dbEventCtx ctx; + unsigned long stateNotifyCacheSize; + mutable epicsMutex & mutex; + mutable epicsMutex & cbMutex; + cacContextNotify & notify; + epics_auto_ptr < cacContext > pNetContext; + char * pStateNotifyCache; + + cacChannel & createChannel ( + epicsGuard < epicsMutex > &, + const char * pChannelName, cacChannelNotify &, + cacChannel::priLev ); + void flush ( + epicsGuard < epicsMutex > & ); + unsigned circuitCount ( + epicsGuard < epicsMutex > & ) const; + void selfTest ( + epicsGuard < epicsMutex > & ) const; + unsigned beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & ) const; + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; + + dbContext ( const dbContext & ); + dbContext & operator = ( const dbContext & ); +}; + +inline dbContextPrivateListOfIO::dbContextPrivateListOfIO () : + pBlocker ( 0 ) +{ +} + +inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO () +{ + assert ( ! this->pBlocker ); +} + +inline void dbContext::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbAddr &addr, + unsigned type, unsigned long count, cacReadNotify & notifyIn ) +{ + guard.assertIdenticalMutex ( this-> mutex ); + this->readNotifyCache.callReadNotify ( guard, addr, type, count, notifyIn ); +} + +#endif // dbCACh + diff --git a/src/db/dbCa.c b/src/db/dbCa.c new file mode 100644 index 000000000..876503153 --- /dev/null +++ b/src/db/dbCa.c @@ -0,0 +1,1004 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 26MAR96 + * + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "epicsString.h" +#include "errlog.h" +#include "taskwd.h" +#include "alarm.h" +#include "link.h" +#include "errMdef.h" +#include "epicsPrint.h" +#include "dbCommon.h" +#include "cadef.h" +#include "epicsAssert.h" +#include "epicsExit.h" +#include "cantProceed.h" + +/* We can't include dbStaticLib.h here */ +#define dbCalloc(nobj,size) callocMustSucceed(nobj,size,"dbCalloc") + +#define epicsExportSharedSymbols +#include "db_access_routines.h" +#include "db_convert.h" +#include "dbScan.h" +#include "dbLock.h" +#include "dbCa.h" +#include "dbCaPvt.h" +#include "recSup.h" + +extern void dbServiceIOInit(); + + +static ELLLIST workList = ELLLIST_INIT; /* Work list for dbCaTask */ +static epicsMutexId workListLock; /*Mutual exclusions semaphores for workList*/ +static epicsEventId workListEvent; /*wakeup event for dbCaTask*/ +static int removesOutstanding = 0; +#define removesOutstandingWarning 10000 + +static enum { + ctlRun, ctlPause, ctlExit +} dbCaCtl; +static epicsEventId startStopEvent; + +struct ca_client_context * dbCaClientContext; + +/* Forward declarations */ +static void dbCaTask(void *); + +#define printLinks(pcaLink) \ + errlogPrintf("%s has DB CA link to %s\n",\ + pcaLink->plink->value.pv_link.precord->name, pcaLink->pvname) + + +/* caLink locking + * + * workListLock + * This is only used to put request into and take them out of workList. + * While this is locked no other locks are taken + * + * dbScanLock + * dbCaAddLink and dbCaRemoveLink are only called by dbAccess or iocInit + * They are only called by dbAccess when it has a global lock on lock set. + * It is assumed that ALL other dbCaxxx calls are made only if dbScanLock + * is already active. These routines are intended for use by record/device + * support. + * + * caLink.lock + * Any code that use a caLink takes this lock and releases it when done + * + * dbCaTask and the channel access callbacks NEVER access anything in the + * records except after locking caLink.lock and checking that caLink.plink + * is not null. They NEVER call dbScanLock. + * + * The above is necessary to prevent deadlocks and attempts to use a caLink + * that has been deleted. + * + * Just a few words about handling dbCaRemoveLink because this is when + * it is essential that nothing trys to use a caLink that has been freed. + * + * dbCaRemoveLink is called when links are being modified. This is only + * done with the dbScan mechanism guranteeing that nothing from + * database access trys to access the record containing the caLink. + * + * Thus the problem is to make sure that nothing from channel access + * accesses a caLink that is deleted. This is done as follows. + * + * dbCaRemoveLink does the following: + * epicsMutexMustLock(pca->lock); + * pca->plink = 0; + * plink->value.pv_link.pvt = 0; + * epicsMutexUnlock(pca->lock); + * addAction(pca,CA_CLEAR_CHANNEL); + * + * dbCaTask issues a ca_clear_channel and then frees the caLink. + * + * If any channel access callback gets called before the ca_clear_channel + * it finds pca->plink=0 and does nothing. Once ca_clear_channel + * is called no other callback for this caLink will be called. + * + * dbCaPutLinkCallback causes an additional complication because + * when dbCaRemoveLink is called the callback may not have occured. + * What is done is the following: + * If callback has not occured dbCaRemoveLink sets plinkPutCallback=plink + * If putCallback is called before dbCaTask calls ca_clear_channel + * it does NOT call the users callback. + * dbCaTask calls the users callback passing plinkPutCallback AFTER + * it has called ca_clear_channel + * Thus the users callback will get called exactly once. +*/ + +static void addAction(caLink *pca, short link_action) +{ + int callAdd; + + epicsMutexMustLock(workListLock); + callAdd = (pca->link_action == 0); + if (pca->link_action & CA_CLEAR_CHANNEL) { + errlogPrintf("dbCa::addAction %d with CA_CLEAR_CHANNEL set\n", + link_action); + printLinks(pca); + link_action = 0; + } + if (link_action & CA_CLEAR_CHANNEL) { + if (++removesOutstanding >= removesOutstandingWarning) { + errlogPrintf("dbCa::addAction pausing, %d channels to clear\n", + removesOutstanding); + printLinks(pca); + } + while (removesOutstanding >= removesOutstandingWarning) { + epicsMutexUnlock(workListLock); + epicsThreadSleep(1.0); + epicsMutexMustLock(workListLock); + } + } + pca->link_action |= link_action; + if (callAdd) + ellAdd(&workList, &pca->node); + epicsMutexUnlock(workListLock); + if (callAdd) + epicsEventSignal(workListEvent); +} + +void dbCaCallbackProcess(void *usrPvt) +{ + struct link *plink = (struct link *)usrPvt; + dbCommon *pdbCommon = plink->value.pv_link.precord; + + dbScanLock(pdbCommon); + pdbCommon->rset->process(pdbCommon); + dbScanUnlock(pdbCommon); +} + +static void dbCaShutdown(void *arg) +{ + if (dbCaCtl == ctlRun) { + dbCaCtl = ctlExit; + epicsEventSignal(workListEvent); + epicsEventMustWait(startStopEvent); + } +} + +void dbCaLinkInit(void) +{ + dbServiceIOInit(); + workListLock = epicsMutexMustCreate(); + workListEvent = epicsEventMustCreate(epicsEventEmpty); + startStopEvent = epicsEventMustCreate(epicsEventEmpty); + dbCaCtl = ctlPause; + + epicsThreadCreate("dbCaLink", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackBig), + dbCaTask, NULL); + epicsEventMustWait(startStopEvent); + epicsAtExit(dbCaShutdown, NULL); +} + +void dbCaRun(void) +{ + dbCaCtl = ctlRun; + epicsEventSignal(workListEvent); +} + +void dbCaPause(void) +{ + dbCaCtl = ctlPause; + epicsEventSignal(workListEvent); +} + +void dbCaAddLinkCallback(struct link *plink, + dbCaCallback connect, dbCaCallback monitor, void *userPvt) +{ + caLink *pca; + + assert(!plink->value.pv_link.pvt); + + pca = (caLink *)dbCalloc(1, sizeof(caLink)); + pca->lock = epicsMutexMustCreate(); + pca->plink = plink; + pca->pvname = epicsStrDup(plink->value.pv_link.pvname); + pca->connect = connect; + pca->monitor = monitor; + pca->userPvt = userPvt; + + epicsMutexMustLock(pca->lock); + plink->type = CA_LINK; + plink->value.pv_link.pvt = pca; + addAction(pca, CA_CONNECT); + epicsMutexUnlock(pca->lock); +} + +void dbCaRemoveLink(struct link *plink) +{ + caLink *pca = (caLink *)plink->value.pv_link.pvt; + + if (!pca) return; + epicsMutexMustLock(pca->lock); + pca->plink = 0; + plink->value.pv_link.pvt = 0; + if (pca->putCallback) + pca->plinkPutCallback = plink; + /* Unlock before addAction or dbCaTask might free first */ + epicsMutexUnlock(pca->lock); + addAction(pca, CA_CLEAR_CHANNEL); +} + +long dbCaGetLink(struct link *plink,short dbrType, void *pdest, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *nelements) +{ + caLink *pca = (caLink *)plink->value.pv_link.pvt; + long status = 0; + short link_action = 0; + int newType; + + assert(pca); + epicsMutexMustLock(pca->lock); + assert(pca->plink); + if (!pca->isConnected || !pca->hasReadAccess) { + pca->sevr = INVALID_ALARM; + pca->stat = LINK_ALARM; + status = -1; + goto done; + } + if (pca->dbrType == DBR_ENUM && dbDBRnewToDBRold[dbrType] == DBR_STRING){ + long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); + + /* Subscribe as DBR_STRING */ + if (!pca->pgetString) { + plink->value.pv_link.pvlMask |= pvlOptInpString; + link_action |= CA_MONITOR_STRING; + } + if (!pca->gotInString) { + pca->sevr = INVALID_ALARM; + pca->stat = LINK_ALARM; + status = -1; + goto done; + } + if (nelements) *nelements = 1; + fConvert = dbFastGetConvertRoutine[dbDBRoldToDBFnew[DBR_STRING]][dbrType]; + status = fConvert(pca->pgetString, pdest, 0); + goto done; + } + if (!pca->pgetNative) { + plink->value.pv_link.pvlMask |= pvlOptInpNative; + link_action |= CA_MONITOR_NATIVE; + } + if (!pca->gotInNative){ + pca->sevr = INVALID_ALARM; + pca->stat = LINK_ALARM; + status = -1; + goto done; + } + newType = dbDBRoldToDBFnew[pca->dbrType]; + if (!nelements || *nelements == 1) { + long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); + + fConvert = dbFastGetConvertRoutine[newType][dbrType]; + assert(pca->pgetNative); + status = fConvert(pca->pgetNative, pdest, 0); + } else { + unsigned long ntoget = *nelements; + struct dbAddr dbAddr; + long (*aConvert)(struct dbAddr *paddr, void *to, long nreq, long nto, long off); + + aConvert = dbGetConvertRoutine[newType][dbrType]; + assert(pca->pgetNative); + + if (ntoget > pca->nelements) + ntoget = pca->nelements; + *nelements = ntoget; + + memset((void *)&dbAddr, 0, sizeof(dbAddr)); + dbAddr.pfield = pca->pgetNative; + /*Following will only be used for pca->dbrType == DBR_STRING*/ + dbAddr.field_size = MAX_STRING_SIZE; + /*Ignore error return*/ + aConvert(&dbAddr, pdest, ntoget, ntoget, 0); + } +done: + if (pstat) *pstat = pca->stat; + if (psevr) *psevr = pca->sevr; + if (link_action) addAction(pca, link_action); + epicsMutexUnlock(pca->lock); + return status; +} + +long dbCaPutLinkCallback(struct link *plink,short dbrType, + const void *pbuffer,long nRequest,dbCaCallback callback,void *userPvt) +{ + caLink *pca = (caLink *)plink->value.pv_link.pvt; + long status = 0; + short link_action = 0; + + assert(pca); + /* put the new value in */ + epicsMutexMustLock(pca->lock); + assert(pca->plink); + if (!pca->isConnected || !pca->hasWriteAccess) { + epicsMutexUnlock(pca->lock); + return -1; + } + if (pca->dbrType == DBR_ENUM && dbDBRnewToDBRold[dbrType] == DBR_STRING) { + long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); + + /* Send as DBR_STRING */ + if (!pca->pputString) { + pca->pputString = dbCalloc(1, MAX_STRING_SIZE); +/* Disabled by ANJ, needs a link flag to allow user to control this. + * Setting these makes the reconnect callback re-do the last CA put. + plink->value.pv_link.pvlMask |= pvlOptOutString; + */ + } + fConvert = dbFastPutConvertRoutine[dbrType][dbDBRoldToDBFnew[DBR_STRING]]; + status = fConvert(pbuffer, pca->pputString, 0); + link_action |= CA_WRITE_STRING; + pca->gotOutString = TRUE; + if (pca->newOutString) pca->nNoWrite++; + pca->newOutString = TRUE; + } else { + int newType = dbDBRoldToDBFnew[pca->dbrType]; + if (!pca->pputNative) { + pca->pputNative = dbCalloc(pca->nelements, + dbr_value_size[ca_field_type(pca->chid)]); +/* Fixed and disabled by ANJ, see comment above. + plink->value.pv_link.pvlMask |= pvlOptOutNative; + */ + } + if (nRequest == 1){ + long (*fConvert)(const void *from, void *to, struct dbAddr *paddr); + + fConvert = dbFastPutConvertRoutine[dbrType][newType]; + status = fConvert(pbuffer, pca->pputNative, 0); + } else { + struct dbAddr dbAddr; + long (*aConvert)(struct dbAddr *paddr, const void *from, long nreq, long nfrom, long off); + + aConvert = dbPutConvertRoutine[dbrType][newType]; + memset((void *)&dbAddr, 0, sizeof(dbAddr)); + dbAddr.pfield = pca->pputNative; + /*Following only used for DBF_STRING*/ + dbAddr.field_size = MAX_STRING_SIZE; + status = aConvert(&dbAddr, pbuffer, nRequest, pca->nelements, 0); + } + link_action |= CA_WRITE_NATIVE; + pca->gotOutNative = TRUE; + if (pca->newOutNative) pca->nNoWrite++; + pca->newOutNative = TRUE; + } + if (callback) { + pca->putType = CA_PUT_CALLBACK; + pca->putCallback = callback; + pca->putUserPvt = userPvt; + } else { + pca->putType = CA_PUT; + pca->putCallback = 0; + } + addAction(pca, link_action); + epicsMutexUnlock(pca->lock); + return status; +} + +int dbCaIsLinkConnected(const struct link *plink) +{ + caLink *pca; + + if (!plink || plink->type != CA_LINK) return FALSE; + pca = (caLink *)plink->value.pv_link.pvt; + if (!pca || !pca->chid) return FALSE; + return pca->isConnected; +} + +#define pcaGetCheck \ + assert(plink); \ + if (plink->type != CA_LINK) return -1; \ + pca = (caLink *)plink->value.pv_link.pvt; \ + assert(pca); \ + epicsMutexMustLock(pca->lock); \ + assert(pca->plink); \ + if (!pca->isConnected) { \ + epicsMutexUnlock(pca->lock); \ + return -1; \ + } + +long dbCaGetNelements(const struct link *plink, long *nelements) +{ + caLink *pca; + + pcaGetCheck + *nelements = pca->nelements; + epicsMutexUnlock(pca->lock); + return 0; +} + +long dbCaGetAlarm(const struct link *plink, + epicsEnum16 *pstat, epicsEnum16 *psevr) +{ + caLink *pca; + + pcaGetCheck + if (pstat) *pstat = pca->stat; + if (psevr) *psevr = pca->sevr; + epicsMutexUnlock(pca->lock); + return 0; +} + +long dbCaGetTimeStamp(const struct link *plink, + epicsTimeStamp *pstamp) +{ + caLink *pca; + + pcaGetCheck + memcpy(pstamp, &pca->timeStamp, sizeof(epicsTimeStamp)); + epicsMutexUnlock(pca->lock); + return 0; +} + +int dbCaGetLinkDBFtype(const struct link *plink) +{ + caLink *pca; + int type; + + pcaGetCheck + type = dbDBRoldToDBFnew[pca->dbrType]; + epicsMutexUnlock(pca->lock); + return type; +} + +long dbCaGetAttributes(const struct link *plink, + dbCaCallback callback,void *userPvt) +{ + caLink *pca; + int gotAttributes; + + assert(plink); + if (plink->type != CA_LINK) return -1; + pca = (caLink *)plink->value.pv_link.pvt; + assert(pca); + epicsMutexMustLock(pca->lock); + assert(pca->plink); + pca->getAttributes = callback; + pca->getAttributesPvt = userPvt; + gotAttributes = pca->gotAttributes; + epicsMutexUnlock(pca->lock); + if (gotAttributes && callback) callback(userPvt); + return 0; +} + +long dbCaGetControlLimits(const struct link *plink, + double *low, double *high) +{ + caLink *pca; + int gotAttributes; + + pcaGetCheck + gotAttributes = pca->gotAttributes; + if (gotAttributes) { + *low = pca->controlLimits[0]; + *high = pca->controlLimits[1]; + } + epicsMutexUnlock(pca->lock); + return gotAttributes ? 0 : -1; +} + +long dbCaGetGraphicLimits(const struct link *plink, + double *low, double *high) +{ + caLink *pca; + int gotAttributes; + + pcaGetCheck + gotAttributes = pca->gotAttributes; + if (gotAttributes) { + *low = pca->displayLimits[0]; + *high = pca->displayLimits[1]; + } + epicsMutexUnlock(pca->lock); + return gotAttributes ? 0 : -1; +} + +long dbCaGetAlarmLimits(const struct link *plink, + double *lolo, double *low, double *high, double *hihi) +{ + caLink *pca; + int gotAttributes; + + pcaGetCheck + gotAttributes = pca->gotAttributes; + if (gotAttributes) { + *lolo = pca->alarmLimits[0]; + *low = pca->alarmLimits[1]; + *high = pca->alarmLimits[2]; + *hihi = pca->alarmLimits[3]; + } + epicsMutexUnlock(pca->lock); + return gotAttributes ? 0 : -1; +} + +long dbCaGetPrecision(const struct link *plink, short *precision) +{ + caLink *pca; + int gotAttributes; + + pcaGetCheck + gotAttributes = pca->gotAttributes; + if (gotAttributes) *precision = pca->precision; + epicsMutexUnlock(pca->lock); + return gotAttributes ? 0 : -1; +} + +long dbCaGetUnits(const struct link *plink, + char *units, int unitsSize) +{ + caLink *pca; + int gotAttributes; + + pcaGetCheck + gotAttributes = pca->gotAttributes; + if (unitsSize > sizeof(pca->units)) unitsSize = sizeof(pca->units); + if (gotAttributes) strncpy(units, pca->units, unitsSize); + units[unitsSize-1] = 0; + epicsMutexUnlock(pca->lock); + return gotAttributes ? 0 : -1; +} + +static void connectionCallback(struct connection_handler_args arg) +{ + caLink *pca; + short link_action = 0; + struct link *plink; + + pca = ca_puser(arg.chid); + assert(pca); + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if (!plink) goto done; + pca->isConnected = (ca_state(arg.chid) == cs_conn); + if (!pca->isConnected) { + struct pv_link *ppv_link = &plink->value.pv_link; + dbCommon *precord = ppv_link->precord; + + pca->nDisconnect++; + if (precord && + ((ppv_link->pvlMask & pvlOptCP) || + ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) + scanOnce(precord); + goto done; + } + pca->hasReadAccess = ca_read_access(arg.chid); + pca->hasWriteAccess = ca_write_access(arg.chid); + + if (pca->gotFirstConnection) { + if (pca->nelements != ca_element_count(arg.chid) || + pca->dbrType != ca_field_type(arg.chid)) { + /* BUG: We have no way to clear any old subscription with the + * originally chosen data type/size. That will continue + * to send us data and will result in an assert() fail. + */ + /* Let next dbCaGetLink and/or dbCaPutLink determine options */ + plink->value.pv_link.pvlMask &= + ~(pvlOptInpNative | pvlOptInpString | + pvlOptOutNative | pvlOptOutString); + + pca->gotInNative = 0; + pca->gotOutNative = 0; + pca->gotInString = 0; + pca->gotOutString = 0; + free(pca->pgetNative); pca->pgetNative = 0; + free(pca->pgetString); pca->pgetString = 0; + free(pca->pputNative); pca->pputNative = 0; + free(pca->pputString); pca->pputString = 0; + } + } + pca->gotFirstConnection = TRUE; + pca->nelements = ca_element_count(arg.chid); + pca->dbrType = ca_field_type(arg.chid); + if ((plink->value.pv_link.pvlMask & pvlOptInpNative) && !pca->pgetNative) { + link_action |= CA_MONITOR_NATIVE; + } + if ((plink->value.pv_link.pvlMask & pvlOptInpString) && !pca->pgetString) { + link_action |= CA_MONITOR_STRING; + } + if ((plink->value.pv_link.pvlMask & pvlOptOutNative) && pca->gotOutNative) { + link_action |= CA_WRITE_NATIVE; + } + if ((plink->value.pv_link.pvlMask & pvlOptOutString) && pca->gotOutString) { + link_action |= CA_WRITE_STRING; + } + pca->gotAttributes = 0; + if (pca->dbrType != DBR_STRING) { + link_action |= CA_GET_ATTRIBUTES; + } +done: + if (link_action) addAction(pca, link_action); + epicsMutexUnlock(pca->lock); +} + +static void eventCallback(struct event_handler_args arg) +{ + caLink *pca = (caLink *)arg.usr; + DBLINK *plink; + size_t size; + dbCommon *precord = 0; + struct dbr_time_double *pdbr_time_double; + dbCaCallback monitor = 0; + void *userPvt = 0; + + assert(pca); + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if (!plink) goto done; + monitor = pca->monitor; + userPvt = pca->userPvt; + precord = plink->value.pv_link.precord; + if (arg.status != ECA_NORMAL) { + if (precord) { + if (arg.status != ECA_NORDACCESS && + arg.status != ECA_GETFAIL) + errlogPrintf("dbCa: eventCallback record %s error %s\n", + precord->name, ca_message(arg.status)); + } else { + errlogPrintf("dbCa: eventCallback error %s\n", + ca_message(arg.status)); + } + goto done; + } + assert(arg.dbr); + size = arg.count * dbr_value_size[arg.type]; + if (arg.type == DBR_TIME_STRING && + ca_field_type(pca->chid) == DBR_ENUM) { + assert(pca->pgetString); + memcpy(pca->pgetString, dbr_value_ptr(arg.dbr, arg.type), size); + pca->gotInString = TRUE; + } else switch (arg.type){ + case DBR_TIME_STRING: + case DBR_TIME_SHORT: + case DBR_TIME_FLOAT: + case DBR_TIME_ENUM: + case DBR_TIME_CHAR: + case DBR_TIME_LONG: + case DBR_TIME_DOUBLE: + assert(pca->pgetNative); + memcpy(pca->pgetNative, dbr_value_ptr(arg.dbr, arg.type), size); + pca->gotInNative = TRUE; + break; + default: + errMessage(-1, "dbCa: eventCallback Logic Error\n"); + break; + } + pdbr_time_double = (struct dbr_time_double *)arg.dbr; + pca->sevr = pdbr_time_double->severity; + pca->stat = pdbr_time_double->status; + memcpy(&pca->timeStamp, &pdbr_time_double->stamp, sizeof(epicsTimeStamp)); + if (precord) { + struct pv_link *ppv_link = &plink->value.pv_link; + + if ((ppv_link->pvlMask & pvlOptCP) || + ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)) + scanOnce(precord); + } +done: + epicsMutexUnlock(pca->lock); + if (monitor) monitor(userPvt); +} + +static void exceptionCallback(struct exception_handler_args args) +{ + const char *context = (args.ctx ? args.ctx : "unknown"); + + errlogPrintf("DB CA Link Exception: \"%s\", context \"%s\"\n", + ca_message(args.stat), context); + if (args.chid) { + errlogPrintf( + "DB CA Link Exception: channel \"%s\"\n", + ca_name(args.chid)); + if (ca_state(args.chid) == cs_conn) { + errlogPrintf( + "DB CA Link Exception: native T=%s, request T=%s," + " native N=%ld, request N=%ld, " + " access rights {%s%s}\n", + dbr_type_to_text(ca_field_type(args.chid)), + dbr_type_to_text(args.type), + ca_element_count(args.chid), + args.count, + ca_read_access(args.chid) ? "R" : "", + ca_write_access(args.chid) ? "W" : ""); + } + } +} + +static void putCallback(struct event_handler_args arg) +{ + caLink *pca = (caLink *)arg.usr; + struct link *plink; + dbCaCallback callback = 0; + void *userPvt = 0; + + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if (!plink) goto done; + callback = pca->putCallback; + userPvt = pca->putUserPvt; + pca->putCallback = 0; + pca->putType = 0; + pca->putUserPvt = 0; +done: + epicsMutexUnlock(pca->lock); + if (callback) callback(userPvt); +} + +static void accessRightsCallback(struct access_rights_handler_args arg) +{ + caLink *pca = (caLink *)ca_puser(arg.chid); + struct link *plink; + struct pv_link *ppv_link; + dbCommon *precord; + + assert(pca); + if (ca_state(pca->chid) != cs_conn) + return; /* connectionCallback will handle */ + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if (!plink) goto done; + pca->hasReadAccess = ca_read_access(arg.chid); + pca->hasWriteAccess = ca_write_access(arg.chid); + if (pca->hasReadAccess && pca->hasWriteAccess) goto done; + ppv_link = &plink->value.pv_link; + precord = ppv_link->precord; + if (precord && + ((ppv_link->pvlMask & pvlOptCP) || + ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))) + scanOnce(precord); +done: + epicsMutexUnlock(pca->lock); +} + +static void getAttribEventCallback(struct event_handler_args arg) +{ + caLink *pca = (caLink *)arg.usr; + struct link *plink; + struct dbr_ctrl_double *pdbr; + dbCaCallback connect = 0; + void *userPvt = 0; + dbCaCallback getAttributes = 0; + void *getAttributesPvt; + + assert(pca); + epicsMutexMustLock(pca->lock); + plink = pca->plink; + if (!plink) { + epicsMutexUnlock(pca->lock); + return; + } + connect = pca->connect; + userPvt = pca->userPvt; + getAttributes = pca->getAttributes; + getAttributesPvt = pca->getAttributesPvt; + if (arg.status != ECA_NORMAL) { + dbCommon *precord = plink->value.pv_link.precord; + if (precord) { + errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n", + precord->name, ca_message(arg.status)); + } else { + errlogPrintf("dbCa: getAttribEventCallback error %s\n", + ca_message(arg.status)); + } + epicsMutexUnlock(pca->lock); + return; + } + assert(arg.dbr); + pdbr = (struct dbr_ctrl_double *)arg.dbr; + pca->gotAttributes = TRUE; + pca->controlLimits[0] = pdbr->lower_ctrl_limit; + pca->controlLimits[1] = pdbr->upper_ctrl_limit; + pca->displayLimits[0] = pdbr->lower_disp_limit; + pca->displayLimits[1] = pdbr->upper_disp_limit; + pca->alarmLimits[0] = pdbr->lower_alarm_limit; + pca->alarmLimits[1] = pdbr->lower_warning_limit; + pca->alarmLimits[2] = pdbr->upper_warning_limit; + pca->alarmLimits[3] = pdbr->upper_alarm_limit; + pca->precision = pdbr->precision; + memcpy(pca->units, pdbr->units, MAX_UNITS_SIZE); + epicsMutexUnlock(pca->lock); + if (getAttributes) getAttributes(getAttributesPvt); + if (connect) connect(userPvt); +} + +static void dbCaTask(void *arg) +{ + int chan_count = 0; + + taskwdInsert(0, NULL, NULL); + SEVCHK(ca_context_create(ca_enable_preemptive_callback), + "dbCaTask calling ca_context_create"); + dbCaClientContext = ca_current_context (); + SEVCHK(ca_add_exception_event(exceptionCallback,NULL), + "ca_add_exception_event"); + epicsEventSignal(startStopEvent); + + /* channel access event loop */ + while (TRUE){ + do { + epicsEventMustWait(workListEvent); + } while (dbCaCtl == ctlPause); + while (TRUE) { /* process all requests in workList*/ + caLink *pca; + short link_action; + int status; + + epicsMutexMustLock(workListLock); + if (!(pca = (caLink *)ellGet(&workList))){ /* Take off list head */ + epicsMutexUnlock(workListLock); + if (dbCaCtl == ctlExit) goto shutdown; + break; /* workList is empty */ + } + link_action = pca->link_action; + pca->link_action = 0; + if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding; + epicsMutexUnlock(workListLock); /* Give back immediately */ + if (link_action & CA_CLEAR_CHANNEL) { /* This must be first */ + dbCaCallback callback; + struct link *plinkPutCallback = 0; + + if (pca->chid) { + ca_clear_channel(pca->chid); + --chan_count; + } + callback = pca->putCallback; + if (callback) { + plinkPutCallback = pca->plinkPutCallback; + pca->plinkPutCallback = 0; + pca->putCallback = 0; + pca->putType = 0; + } + free(pca->pgetNative); + free(pca->pputNative); + free(pca->pgetString); + free(pca->pputString); + free(pca->pvname); + epicsMutexDestroy(pca->lock); + free(pca); + /* No alarm is raised. Since link is changing so what? */ + if (callback) callback(plinkPutCallback); + continue; /* No other link_action makes sense */ + } + if (link_action & CA_CONNECT) { + status = ca_create_channel( + pca->pvname,connectionCallback,(void *)pca, + CA_PRIORITY_DB_LINKS, &(pca->chid)); + if (status != ECA_NORMAL) { + errlogPrintf("dbCaTask ca_create_channel %s\n", + ca_message(status)); + printLinks(pca); + continue; + } + chan_count++; + status = ca_replace_access_rights_event(pca->chid, + accessRightsCallback); + if (status != ECA_NORMAL) { + errlogPrintf("dbCaTask replace_access_rights_event %s\n", + ca_message(status)); + printLinks(pca); + } + continue; /*Other options must wait until connect*/ + } + if (ca_state(pca->chid) != cs_conn) continue; + if (link_action & CA_WRITE_NATIVE) { + assert(pca->pputNative); + if (pca->putType == CA_PUT) { + status = ca_array_put( + pca->dbrType, pca->nelements, + pca->chid, pca->pputNative); + } else if (pca->putType==CA_PUT_CALLBACK) { + status = ca_array_put_callback( + pca->dbrType, pca->nelements, + pca->chid, pca->pputNative, + putCallback, pca); + } else { + status = ECA_PUTFAIL; + } + if (status != ECA_NORMAL) { + errlogPrintf("dbCaTask ca_array_put %s\n", + ca_message(status)); + printLinks(pca); + } + epicsMutexMustLock(pca->lock); + if (status == ECA_NORMAL) pca->newOutNative = FALSE; + epicsMutexUnlock(pca->lock); + } + if (link_action & CA_WRITE_STRING) { + assert(pca->pputString); + if (pca->putType == CA_PUT) { + status = ca_array_put( + DBR_STRING, 1, + pca->chid, pca->pputString); + } else if (pca->putType==CA_PUT_CALLBACK) { + status = ca_array_put_callback( + DBR_STRING, 1, + pca->chid, pca->pputString, + putCallback, pca); + } else { + status = ECA_PUTFAIL; + } + if (status != ECA_NORMAL) { + errlogPrintf("dbCaTask ca_array_put %s\n", + ca_message(status)); + printLinks(pca); + } + epicsMutexMustLock(pca->lock); + if (status == ECA_NORMAL) pca->newOutString = FALSE; + epicsMutexUnlock(pca->lock); + } + /*CA_GET_ATTRIBUTES before CA_MONITOR so that attributes available + * before the first monitor callback */ + if (link_action & CA_GET_ATTRIBUTES) { + status = ca_get_callback(DBR_CTRL_DOUBLE, + pca->chid, getAttribEventCallback, pca); + if (status != ECA_NORMAL) { + errlogPrintf("dbCaTask ca_get_callback %s\n", + ca_message(status)); + printLinks(pca); + } + } + if (link_action & CA_MONITOR_NATIVE) { + size_t element_size; + + element_size = dbr_value_size[ca_field_type(pca->chid)]; + epicsMutexMustLock(pca->lock); + pca->pgetNative = dbCalloc(pca->nelements, element_size); + epicsMutexUnlock(pca->lock); + status = ca_add_array_event( + ca_field_type(pca->chid)+DBR_TIME_STRING, + ca_element_count(pca->chid), + pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0); + if (status != ECA_NORMAL) { + errlogPrintf("dbCaTask ca_add_array_event %s\n", + ca_message(status)); + printLinks(pca); + } + } + if (link_action & CA_MONITOR_STRING) { + epicsMutexMustLock(pca->lock); + pca->pgetString = dbCalloc(1, MAX_STRING_SIZE); + epicsMutexUnlock(pca->lock); + status = ca_add_array_event(DBR_TIME_STRING, 1, + pca->chid, eventCallback, pca, 0.0, 0.0, 0.0, 0); + if (status != ECA_NORMAL) { + errlogPrintf("dbCaTask ca_add_array_event %s\n", + ca_message(status)); + printLinks(pca); + } + } + } + SEVCHK(ca_flush_io(), "dbCaTask"); + } +shutdown: + taskwdRemove(0); + if (chan_count == 0) + ca_context_destroy(); + else + fprintf(stderr, "dbCa: chan_count = %d at shutdown\n", chan_count); + epicsEventSignal(startStopEvent); +} diff --git a/src/db/dbCa.h b/src/db/dbCa.h new file mode 100644 index 000000000..ee0fee1ca --- /dev/null +++ b/src/db/dbCa.h @@ -0,0 +1,75 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbCa.h */ + +#ifndef INCdbCah +#define INCdbCah + +#include "shareLib.h" +#include "epicsTime.h" +#include "link.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*dbCaCallback)(void *userPvt); +epicsShareFunc void dbCaCallbackProcess(void *usrPvt); + +epicsShareFunc void dbCaLinkInit(void); +epicsShareFunc void dbCaRun(void); +epicsShareFunc void dbCaPause(void); + +epicsShareFunc void dbCaAddLinkCallback(struct link *plink, + dbCaCallback connect, dbCaCallback monitor, void *userPvt); +#define dbCaAddLink(plink) dbCaAddLinkCallback((plink), 0, 0, 0) +epicsShareFunc void dbCaRemoveLink(struct link *plink); +epicsShareFunc long dbCaGetLink(struct link *plink, + short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr, + long *nRequest); +epicsShareFunc long dbCaPutLinkCallback(struct link *plink, + short dbrType, const void *pbuffer,long nRequest, + dbCaCallback callback, void *userPvt); +#define dbCaPutLink(plink, dbrType, pbuffer, nRequest) \ + dbCaPutLinkCallback((plink), (dbrType), (pbuffer), (nRequest), 0, 0) +epicsShareFunc int dbCaIsLinkConnected(const struct link *plink); + +/* The following are available after the link is connected*/ +epicsShareFunc long dbCaGetNelements(const struct link *plink, + long *nelements); +#define dbCaGetSevr(plink, severity) \ + dbCaGetAlarm((plink), NULL, (severity)) +epicsShareFunc long dbCaGetAlarm(const struct link *plink, + epicsEnum16 *status, epicsEnum16 *severity); +epicsShareFunc long dbCaGetTimeStamp(const struct link *plink, + epicsTimeStamp *pstamp); +epicsShareFunc int dbCaGetLinkDBFtype(const struct link *plink); + +/*The following are available after attribute request is complete*/ +epicsShareFunc long dbCaGetAttributes(const struct link *plink, + dbCaCallback callback, void *userPvt); +epicsShareFunc long dbCaGetControlLimits(const struct link *plink, + double *low, double *high); +epicsShareFunc long dbCaGetGraphicLimits(const struct link *plink, + double *low, double *high); +epicsShareFunc long dbCaGetAlarmLimits(const struct link *plink, + double *lolo, double *low, double *high, double *hihi); +epicsShareFunc long dbCaGetPrecision(const struct link *plink, + short *precision); +epicsShareFunc long dbCaGetUnits(const struct link *plink, + char *units, int unitsSize); + +extern struct ca_client_context * dbCaClientContext; + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbCah*/ diff --git a/src/db/dbCaPvt.h b/src/db/dbCaPvt.h new file mode 100644 index 000000000..9625bfbd6 --- /dev/null +++ b/src/db/dbCaPvt.h @@ -0,0 +1,88 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbCaPvt.h */ +/**************************************************************** +* +* Current Author: Bob Dalesio +* Contributing Author: Marty Kraimer +* Date: 08APR96 +* +****************************************************************/ + +#ifndef INCdbCaPvth +#define INCdbCaPvth 1 + +/* link_action mask */ +#define CA_CLEAR_CHANNEL 0x1 +#define CA_CONNECT 0x2 +#define CA_WRITE_NATIVE 0x4 +#define CA_WRITE_STRING 0x8 +#define CA_MONITOR_NATIVE 0x10 +#define CA_MONITOR_STRING 0x20 +#define CA_GET_ATTRIBUTES 0x40 +/* write type */ +#define CA_PUT 0x1 +#define CA_PUT_CALLBACK 0x2 + +typedef struct caLink +{ + ELLNODE node; + epicsMutexId lock; + struct link *plink; + char *pvname; + chid chid; + short link_action; + /* The following have new values after each data event*/ + epicsEnum16 sevr; + epicsEnum16 stat; + epicsTimeStamp timeStamp; + /* The following have values after connection*/ + short dbrType; + long nelements; + char hasReadAccess; + char hasWriteAccess; + char isConnected; + char gotFirstConnection; + /* The following are for dbCaAddLinkCallback */ + dbCaCallback connect; + dbCaCallback monitor; + void *userPvt; + /* The following are for write request */ + short putType; + dbCaCallback putCallback; + void *putUserPvt; + struct link *plinkPutCallback; + /* The following are for access to additional attributes*/ + char gotAttributes; + dbCaCallback getAttributes; + void *getAttributesPvt; + /* The following have values after getAttribEventCallback*/ + double controlLimits[2]; + double displayLimits[2]; + double alarmLimits[4]; + short precision; + char units[MAX_UNITS_SIZE]; /* units of value */ + /* The following are for handling data*/ + void *pgetNative; + char *pgetString; + void *pputNative; + char *pputString; + char gotInNative; + char gotInString; + char gotOutNative; + char gotOutString; + char newOutNative; + char newOutString; + /* The following are for dbcar*/ + unsigned long nDisconnect; + unsigned long nNoWrite; /*only modified by dbCaPutLink*/ +}caLink; + +#endif /*INCdbCaPvth*/ diff --git a/src/db/dbCaTest.c b/src/db/dbCaTest.c new file mode 100644 index 000000000..e4103068a --- /dev/null +++ b/src/db/dbCaTest.c @@ -0,0 +1,204 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* dbCaTest.c */ + +/**************************************************************** +* +* Author: Marty Kraimer +* Date: 10APR96 +* +****************************************************************/ + +#include +#include +#include +#include + +#include "dbStaticLib.h" +#include "epicsStdioRedirect.h" +#include "link.h" +/*definitions needed because of old vs new database access*/ +#undef DBR_SHORT +#undef DBR_PUT_ACKT +#undef DBR_PUT_ACKS +#undef VALID_DB_REQ +#undef INVALID_DB_REQ +/*end of conflicting definitions*/ +#include "cadef.h" +#include "db_access.h" +#include "dbDefs.h" +#include "epicsPrint.h" +#include "dbCommon.h" +#include "epicsEvent.h" + +/*define DB_CONVERT_GBLSOURCE because db_access.c does not include db_access.h*/ +#define DB_CONVERT_GBLSOURCE + +#define epicsExportSharedSymbols +#include "dbLock.h" +#include "db_access_routines.h" +#include "db_convert.h" +#include "dbCa.h" +#include "dbCaPvt.h" +#include "dbCaTest.h" + + +long dbcar(char *precordname, int level) +{ + DBENTRY dbentry; + DBENTRY *pdbentry=&dbentry; + long status; + dbCommon *precord; + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + DBLINK *plink; + int ncalinks=0; + int nconnected=0; + int noReadAccess=0; + int noWriteAccess=0; + unsigned long nDisconnect=0; + unsigned long nNoWrite=0; + caLink *pca; + int j; + + if (!precordname || precordname[0] == '\0' || !strcmp(precordname, "*")) { + precordname = NULL; + printf("CA links in all records\n\n"); + } else { + printf("CA links in record named '%s'\n\n", precordname); + } + dbInitEntry(pdbbase,pdbentry); + status = dbFirstRecordType(pdbentry); + while (!status) { + status = dbFirstRecord(pdbentry); + while (!status) { + if (precordname ? + !strcmp(precordname, dbGetRecordName(pdbentry)) : + !dbIsAlias(pdbentry)) { + pdbRecordType = pdbentry->precordType; + precord = (dbCommon *)pdbentry->precnode->precord; + for (j=0; jno_links; j++) { + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + dbLockSetGblLock(); + if (plink->type == CA_LINK) { + ncalinks++; + pca = (caLink *)plink->value.pv_link.pvt; + if (pca + && pca->chid + && (ca_field_type(pca->chid) != TYPENOTCONN)) { + nconnected++; + nDisconnect += pca->nDisconnect; + nNoWrite += pca->nNoWrite; + if (!ca_read_access(pca->chid)) noReadAccess++; + if (!ca_write_access(pca->chid)) noWriteAccess++; + if (level>1) { + int rw = ca_read_access(pca->chid) | + ca_write_access(pca->chid) << 1; + static const char *rights[4] = { + "No Access", "Read Only", + "Write Only", "Read/Write" + }; + int mask = plink->value.pv_link.pvlMask; + printf("%28s.%-4s ==> %-28s (%lu, %lu)\n", + precord->name, + pdbFldDes->name, + plink->value.pv_link.pvname, + pca->nDisconnect, + pca->nNoWrite); + printf("%21s [%s%s%s%s] host %s, %s\n", "", + mask & pvlOptInpNative ? "IN" : " ", + mask & pvlOptInpString ? "IS" : " ", + mask & pvlOptOutNative ? "ON" : " ", + mask & pvlOptOutString ? "OS" : " ", + ca_host_name(pca->chid), + rights[rw]); + } + } else { + if (level>0) { + printf("%28s.%-4s --> %-28s (%lu, %lu)\n", + precord->name, + pdbFldDes->name, + plink->value.pv_link.pvname, + pca->nDisconnect, + pca->nNoWrite); + } + } + } + dbLockSetGblUnlock(); + } + if (precordname) goto done; + } + status = dbNextRecord(pdbentry); + } + status = dbNextRecordType(pdbentry); + } +done: + if ((level > 1 && nconnected > 0) || + (level > 0 && ncalinks != nconnected)) printf("\n"); + printf("Total %d CA link%s; ", + ncalinks, (ncalinks != 1) ? "s" : ""); + printf("%d connected, %d not connected.\n", + nconnected, (ncalinks - nconnected)); + printf(" %d can't read, %d can't write.", + noReadAccess, noWriteAccess); + printf(" (%lu disconnects, %lu writes prohibited)\n\n", + nDisconnect, nNoWrite); + dbFinishEntry(pdbentry); + + if ( level > 2 && dbCaClientContext != 0 ) { + ca_context_status ( dbCaClientContext, level - 2 ); + } + + return(0); +} + +void dbcaStats(int *pchans, int *pdiscon) +{ + DBENTRY dbentry; + DBENTRY *pdbentry = &dbentry; + long status; + DBLINK *plink; + long ncalinks = 0; + long nconnected = 0; + + dbInitEntry(pdbbase,pdbentry); + status = dbFirstRecordType(pdbentry); + while (!status) { + dbRecordType *pdbRecordType = pdbentry->precordType; + + status = dbFirstRecord(pdbentry); + while (!status) { + dbCommon *precord = (dbCommon *)pdbentry->precnode->precord; + int j; + + if (!dbIsAlias(pdbentry)) { + for (j=0; jno_links; j++) { + int i = pdbRecordType->link_ind[j]; + + dbFldDes *pdbFldDes = pdbRecordType->papFldDes[i]; + plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + if (plink->type == CA_LINK) { + caLink *pca = (caLink *)plink->value.pv_link.pvt; + + ncalinks++; + if (pca && ca_state(pca->chid) == cs_conn) { + nconnected++; + } + } + } + } + status = dbNextRecord(pdbentry); + } + status = dbNextRecordType(pdbentry); + } + dbFinishEntry(pdbentry); + if (pchans) *pchans = ncalinks; + if (pdiscon) *pdiscon = ncalinks - nconnected; +} diff --git a/src/db/dbCaTest.h b/src/db/dbCaTest.h new file mode 100644 index 000000000..ed501df2d --- /dev/null +++ b/src/db/dbCaTest.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_dbCaTest_H +#define INC_dbCaTest_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc long dbcar(char *recordname,int level); +epicsShareFunc void dbcaStats(int *pchans, int *pdiscon); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_dbCaTest_H */ diff --git a/src/db/dbChannelIO.cpp b/src/db/dbChannelIO.cpp new file mode 100644 index 000000000..e3c971746 --- /dev/null +++ b/src/db/dbChannelIO.cpp @@ -0,0 +1,243 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include + +#include "tsFreeList.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "db_access.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "db_access_routines.h" +#include "dbCAC.h" +#include "dbChannelIO.h" +#include "dbPutNotifyBlocker.h" + +dbChannelIO::dbChannelIO ( + epicsMutex & mutexIn, cacChannelNotify & notify, + const dbAddr & addrIn, dbContext & serviceIO ) : + cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ), + addr ( addrIn ) +{ + unsigned bufLen = dbNameSizeOfPV ( & this->addr ) + 1; + this->pNameStr.reset ( new char [ bufLen ] ); + dbNameOfPV ( & this->addr, this->pNameStr.get (), bufLen ); +} + +void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->notify().connectNotify ( guard ); +} + +dbChannelIO::~dbChannelIO () +{ +} + +void dbChannelIO::destructor ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.destroyAllIO ( guard, *this ); + this->~dbChannelIO (); +} + +void dbChannelIO::destroy ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.destroyChannel ( guard, *this ); + // dont access this pointer after above call because + // object nolonger exists +} + +cacChannel::ioStatus dbChannelIO::read ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, cacReadNotify & notify, ioid * ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.callReadNotify ( guard, this->addr, + type, count, notify ); + return iosSynch; +} + +void dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, const void *pValue ) +{ + epicsGuardRelease < epicsMutex > unguard ( guard ); + if ( count > LONG_MAX ) { + throw outOfBounds(); + } + int status = db_put_field ( &this->addr, type, pValue, + static_cast (count) ); + if ( status ) { + throw std::logic_error ( + "db_put_field() completed unsuccessfully" ); + } +} + +cacChannel::ioStatus dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, const void * pValue, + cacWriteNotify & notify, ioid * pId ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( count > LONG_MAX ) { + throw outOfBounds(); + } + + this->serviceIO.initiatePutNotify ( + guard, *this, this->addr, + type, count, pValue, notify, pId ); + + return iosAsynch; +} + +void dbChannelIO::subscribe ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, + unsigned mask, cacStateNotify & notify, ioid * pId ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.subscribe ( + guard, this->addr, *this, + type, count, mask, notify, pId ); +} + +void dbChannelIO::ioCancel ( + epicsGuard < epicsMutex > & mutualExclusionGuard, + const ioid & id ) +{ + mutualExclusionGuard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.ioCancel ( mutualExclusionGuard, *this, id ); +} + +void dbChannelIO::ioShow ( + epicsGuard < epicsMutex > & guard, + const ioid & id, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.ioShow ( guard, id, level ); +} + +void dbChannelIO::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + + printf ("channel at %p attached to local database record %s\n", + static_cast ( this ), this->addr.precord->name ); + + if ( level > 0u ) { + printf ( "\ttype %s, element count %li, field at %p\n", + dbf_type_to_text ( this->addr.dbr_field_type ), this->addr.no_elements, + this->addr.pfield ); + } + if ( level > 1u ) { + this->serviceIO.show ( level - 2u ); + this->serviceIO.showAllIO ( *this, level - 2u ); + } +} + +unsigned long dbChannelIO::nativeElementCount ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->addr.no_elements >= 0u ) { + return static_cast < unsigned long > ( this->addr.no_elements ); + } + return 0u; +} + +// hopefully to be eventually phased out +const char * dbChannelIO::pName ( + epicsGuard < epicsMutex > & guard ) const throw () +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->pNameStr.get (); +} + +unsigned dbChannelIO::getName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw () +{ + return dbNameOfPV ( & this->addr, pBuf, bufLen ); +} + +short dbChannelIO::nativeType ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->addr.dbr_field_type; +} + +void * dbChannelIO::operator new ( size_t size, + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +void * dbChannelIO::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +#ifdef CXX_PLACEMENT_DELETE +void dbChannelIO::operator delete ( void *pCadaver, + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +void dbChannelIO::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void dbChannelIO::flush ( + epicsGuard < epicsMutex > & ) +{ +} + +unsigned dbChannelIO::requestMessageBytesPending ( + epicsGuard < epicsMutex > & ) +{ + return 0u; +} + diff --git a/src/db/dbChannelIO.h b/src/db/dbChannelIO.h new file mode 100644 index 000000000..367d8fcd0 --- /dev/null +++ b/src/db/dbChannelIO.h @@ -0,0 +1,134 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + * NOTES: + * 1) This interface is preliminary and will change in the future + */ + +#ifndef dbChannelIOh +#define dbChannelIOh + +#ifdef epicsExportSharedSymbols +# define dbChannelIOh_restore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include "compilerDependencies.h" +#include "epicsMemory.h" + +#ifdef dbChannelIOh_restore_epicsExportSharedSymbols +# define epicsExportSharedSymbols +#endif + +class dbChannelIO : public cacChannel, public dbContextPrivateListOfIO { +public: + dbChannelIO ( + epicsMutex &, cacChannelNotify &, + const dbAddr &, dbContext & ); + void destructor ( + epicsGuard < epicsMutex > & ); + void destroy ( + epicsGuard < epicsMutex > & mutualExclusionGuard ); + void callReadNotify ( + epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, + cacReadNotify & notify ); + void callStateNotify ( + unsigned type, unsigned long count, + const struct db_field_log * pfl, cacStateNotify & notify ); + void show ( + epicsGuard < epicsMutex > &, unsigned level ) const; + unsigned getName ( + epicsGuard < epicsMutex > &, + char * pBuf, unsigned bufLen ) const throw (); + const char * pName ( + epicsGuard < epicsMutex > & ) const throw (); + void * operator new ( size_t size, + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & )) +protected: + ~dbChannelIO (); +private: + epicsMutex & mutex; + dbContext & serviceIO; + dbAddr addr; + epics_auto_ptr < char, eapt_array > pNameStr; + + void initiateConnect ( + epicsGuard < epicsMutex > & ); + unsigned requestMessageBytesPending ( + epicsGuard < epicsMutex > & ); + void flush ( + epicsGuard < epicsMutex > & ); + ioStatus read ( + epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, + cacReadNotify &, ioid * ); + void write ( + epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, + const void * pvalue ); + ioStatus write ( + epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, + const void * pvalue, cacWriteNotify &, ioid * ); + void subscribe ( + epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, + unsigned mask, cacStateNotify ¬ify, ioid * ); + void ioCancel ( + epicsGuard < epicsMutex > & mutualExclusionGuard, + const ioid & ); + void ioShow ( + epicsGuard < epicsMutex > &, + const ioid &, unsigned level ) const; + short nativeType ( + epicsGuard < epicsMutex > & ) const; + unsigned long nativeElementCount ( + epicsGuard < epicsMutex > & ) const; + dbChannelIO ( const dbChannelIO & ); + dbChannelIO & operator = ( const dbChannelIO & ); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +inline void dbChannelIO::callReadNotify ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, + cacReadNotify & notify ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->serviceIO.callReadNotify ( guard, this->addr, type, count, notify ); +} + +inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, + const struct db_field_log *pfl, cacStateNotify ¬ify ) +{ + this->serviceIO.callStateNotify ( this->addr, type, count, pfl, notify ); +} + + +#endif // dbChannelIOh + diff --git a/src/db/dbCommon.dbd b/src/db/dbCommon.dbd new file mode 100644 index 000000000..eb8e7bf35 --- /dev/null +++ b/src/db/dbCommon.dbd @@ -0,0 +1,254 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + %#include "epicsTypes.h" + %#include "link.h" + field(NAME,DBF_STRING) { + prompt("Record Name") + special(SPC_NOMOD) + size(61) + } + field(DESC,DBF_STRING) { + prompt("Descriptor") + promptgroup(GUI_COMMON) + size(41) + } + field(ASG,DBF_STRING) { + prompt("Access Security Group") + promptgroup(GUI_COMMON) + special(SPC_AS) + size(29) + } + field(SCAN,DBF_MENU) { + prompt("Scan Mechanism") + promptgroup(GUI_SCAN) + special(SPC_SCAN) + interest(1) + menu(menuScan) + } + field(PINI,DBF_MENU) { + prompt("Process at iocInit") + promptgroup(GUI_SCAN) + interest(1) + menu(menuPini) + } + field(PHAS,DBF_SHORT) { + prompt("Scan Phase") + promptgroup(GUI_SCAN) + special(SPC_SCAN) + interest(1) + } + field(EVNT,DBF_SHORT) { + prompt("Event Number") + promptgroup(GUI_SCAN) + special(SPC_SCAN) + interest(1) + } + field(TSE,DBF_SHORT) { + prompt("Time Stamp Event") + promptgroup(GUI_SCAN) + interest(1) + } + field(TSEL,DBF_INLINK) { + prompt("Time Stamp Link") + promptgroup(GUI_SCAN) + interest(1) + } + field(DTYP,DBF_DEVICE) { + prompt("Device Type") + promptgroup(GUI_LINKS) + interest(1) + } + field(DISV,DBF_SHORT) { + prompt("Disable Value") + promptgroup(GUI_SCAN) + initial("1") + } + field(DISA,DBF_SHORT) { + prompt("Disable") + } + field(SDIS,DBF_INLINK) { + prompt("Scanning Disable") + promptgroup(GUI_SCAN) + interest(1) + } + %#include "epicsMutex.h" + field(MLOK,DBF_NOACCESS) { + prompt("Monitor lock") + special(SPC_NOMOD) + interest(4) + extra("epicsMutexId mlok") + } + %#include "ellLib.h" + field(MLIS,DBF_NOACCESS) { + prompt("Monitor List") + special(SPC_NOMOD) + interest(4) + extra("ELLLIST mlis") + } + field(DISP,DBF_UCHAR) { + prompt("Disable putField") + } + field(PROC,DBF_UCHAR) { + prompt("Force Processing") + pp(TRUE) + interest(3) + } + field(STAT,DBF_MENU) { + prompt("Alarm Status") + special(SPC_NOMOD) + menu(menuAlarmStat) + initial("UDF") + } + field(SEVR,DBF_MENU) { + prompt("Alarm Severity") + special(SPC_NOMOD) + menu(menuAlarmSevr) + initial("INVALID") + } + field(NSTA,DBF_MENU) { + prompt("New Alarm Status") + special(SPC_NOMOD) + interest(2) + menu(menuAlarmStat) + } + field(NSEV,DBF_MENU) { + prompt("New Alarm Severity") + special(SPC_NOMOD) + interest(2) + menu(menuAlarmSevr) + } + field(ACKS,DBF_MENU) { + prompt("Alarm Ack Severity") + special(SPC_NOMOD) + interest(2) + menu(menuAlarmSevr) + } + field(ACKT,DBF_MENU) { + prompt("Alarm Ack Transient") + promptgroup(GUI_ALARMS) + special(SPC_NOMOD) + interest(2) + menu(menuYesNo) + initial("YES") + } + field(DISS,DBF_MENU) { + prompt("Disable Alarm Sevrty") + promptgroup(GUI_SCAN) + interest(1) + menu(menuAlarmSevr) + } + field(LCNT,DBF_UCHAR) { + prompt("Lock Count") + special(SPC_NOMOD) + interest(2) + } + field(PACT,DBF_UCHAR) { + prompt("Record active") + special(SPC_NOMOD) + interest(1) + } + field(PUTF,DBF_UCHAR) { + prompt("dbPutField process") + special(SPC_NOMOD) + interest(1) + } + field(RPRO,DBF_UCHAR) { + prompt("Reprocess ") + special(SPC_NOMOD) + interest(1) + } + field(ASP,DBF_NOACCESS) { + prompt("Access Security Pvt") + special(SPC_NOMOD) + interest(4) + extra("struct asgMember *asp") + } + field(PPN,DBF_NOACCESS) { + prompt("addr of PUTNOTIFY") + special(SPC_NOMOD) + interest(4) + extra("struct putNotify *ppn") + } + field(PPNR,DBF_NOACCESS) { + prompt("pputNotifyRecord") + special(SPC_NOMOD) + interest(4) + extra("struct putNotifyRecord *ppnr") + } + field(SPVT,DBF_NOACCESS) { + prompt("Scan Private") + special(SPC_NOMOD) + interest(4) + extra("struct scan_element *spvt") + } + field(RSET,DBF_NOACCESS) { + prompt("Address of RSET") + special(SPC_NOMOD) + interest(4) + extra("struct rset *rset") + } + field(DSET,DBF_NOACCESS) { + prompt("DSET address") + special(SPC_NOMOD) + interest(4) + extra("struct dset *dset") + } + field(DPVT,DBF_NOACCESS) { + prompt("Device Private") + special(SPC_NOMOD) + interest(4) + extra("void *dpvt") + } + field(RDES,DBF_NOACCESS) { + prompt("Address of dbRecordType") + special(SPC_NOMOD) + interest(4) + extra("struct dbRecordType *rdes") + } + field(LSET,DBF_NOACCESS) { + prompt("Lock Set") + special(SPC_NOMOD) + interest(4) + extra("struct lockRecord *lset") + } + field(PRIO,DBF_MENU) { + prompt("Scheduling Priority") + promptgroup(GUI_SCAN) + special(SPC_SCAN) + interest(1) + menu(menuPriority) + } + field(TPRO,DBF_UCHAR) { + prompt("Trace Processing") + } + field(BKPT,DBF_NOACCESS) { + prompt("Break Point") + special(SPC_NOMOD) + interest(1) + extra("char bkpt") + } + field(UDF,DBF_UCHAR) { + prompt("Undefined") + promptgroup(GUI_COMMON) + pp(TRUE) + interest(1) + initial("1") + } + %#include "epicsTime.h" + field(TIME,DBF_NOACCESS) { + prompt("Time") + special(SPC_NOMOD) + interest(2) + extra("epicsTimeStamp time") + } + field(FLNK,DBF_FWDLINK) { + prompt("Forward Process Link") + promptgroup(GUI_LINKS) + interest(1) + } diff --git a/src/db/dbCommonRecord.dbd b/src/db/dbCommonRecord.dbd new file mode 100644 index 000000000..a988619ae --- /dev/null +++ b/src/db/dbCommonRecord.dbd @@ -0,0 +1,12 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(dbCommon) { + include "dbCommon.dbd" +} diff --git a/src/db/dbContext.cpp b/src/db/dbContext.cpp new file mode 100644 index 000000000..d49a8639e --- /dev/null +++ b/src/db/dbContext.cpp @@ -0,0 +1,422 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#include "epicsMutex.h" +#include "tsFreeList.h" + +#include "cadef.h" // this can be eliminated when the callbacks use the new interface +#include "db_access.h" // should be eliminated here in the future +#include "caerr.h" // should be eliminated here in the future +#include "epicsEvent.h" +#include "epicsThread.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "db_access_routines.h" +#include "dbCAC.h" +#include "dbChannelIO.h" +#include "dbPutNotifyBlocker.h" + +class dbService : public cacService { +public: + ~dbService () {} + cacContext & contextCreate ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, + cacContextNotify & ); +}; + +static dbService dbs; + +cacContext & dbService::contextCreate ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, + cacContextNotify & notify ) +{ + return * new dbContext ( callbackControl, + mutualExclusion, notify ); +} + +extern "C" void dbServiceIOInit () +{ + caInstallDefaultService ( dbs ); +} + +dbBaseIO::dbBaseIO () {} + +dbContext::dbContext ( epicsMutex & cbMutexIn, + epicsMutex & mutexIn, cacContextNotify & notifyIn ) : + readNotifyCache ( mutexIn ), ctx ( 0 ), + stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ), + notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ) +{ +} + +dbContext::~dbContext () +{ + delete [] this->pStateNotifyCache; + if ( this->ctx ) { + db_close_events ( this->ctx ); + } +} + +cacChannel & dbContext::createChannel ( // X aCC 361 + epicsGuard < epicsMutex > & guard, const char * pName, + cacChannelNotify & notifyIn, cacChannel::priLev priority ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + struct dbAddr addr; + int status; + { + // dont know if the database might call a put callback + // while holding its lock ... + epicsGuardRelease < epicsMutex > unguard ( guard ); + status = db_name_to_addr ( pName, & addr ); + } + if ( status ) { + if ( ! this->pNetContext.get() ) { + this->pNetContext.reset ( + & this->notify.createNetworkContext ( + this->mutex, this->cbMutex ) ); + } + return this->pNetContext->createChannel ( + guard, pName, notifyIn, priority ); + } + else if ( ca_preemtive_callback_is_enabled () ) { + return * new ( this->dbChannelIOFreeList ) + dbChannelIO ( this->mutex, notifyIn, addr, *this ); + } + else { + errlogPrintf ( + "dbContext: preemptive callback required for direct in\n" + "memory interfacing of CA channels to the DB.\n" ); + throw cacChannel::unsupportedByService (); + } +} + +void dbContext::destroyChannel ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + if ( chan.dbContextPrivateListOfIO::pBlocker ) { + this->ioTable.remove ( *chan.dbContextPrivateListOfIO::pBlocker ); + chan.dbContextPrivateListOfIO::pBlocker->destructor ( guard ); + this->dbPutNotifyBlockerFreeList.release ( chan.dbContextPrivateListOfIO::pBlocker ); + chan.dbContextPrivateListOfIO::pBlocker = 0; + } + + chan.destructor ( guard ); + this->dbChannelIOFreeList.release ( & chan ); +} + +void dbContext::callStateNotify ( struct dbAddr & addr, + unsigned type, unsigned long count, + const struct db_field_log * pfl, + cacStateNotify & notifyIn ) +{ + unsigned long size = dbr_size_n ( type, count ); + + if ( type > INT_MAX ) { + epicsGuard < epicsMutex > guard ( this->mutex ); + notifyIn.exception ( guard, ECA_BADTYPE, + "type code out of range (high side)", + type, count ); + return; + } + + if ( count > INT_MAX ) { + epicsGuard < epicsMutex > guard ( this->mutex ); + notifyIn.exception ( guard, ECA_BADCOUNT, + "element count out of range (high side)", + type, count); + return; + } + + // no need to lock this because state notify is + // called from only one event queue consumer thread + if ( this->stateNotifyCacheSize < size) { + char * pTmp = new char [size]; + delete [] this->pStateNotifyCache; + this->pStateNotifyCache = pTmp; + this->stateNotifyCacheSize = size; + } + void *pvfl = (void *) pfl; + int status = db_get_field ( &addr, static_cast ( type ), + this->pStateNotifyCache, static_cast ( count ), pvfl ); + if ( status ) { + epicsGuard < epicsMutex > guard ( this->mutex ); + notifyIn.exception ( guard, ECA_GETFAIL, + "db_get_field() completed unsuccessfuly", type, count ); + } + else { + epicsGuard < epicsMutex > guard ( this->mutex ); + notifyIn.current ( guard, type, count, this->pStateNotifyCache ); + } +} + +extern "C" void cacAttachClientCtx ( void * pPrivate ) +{ + int status = ca_attach_context ( (ca_client_context *) pPrivate ); + assert ( status == ECA_NORMAL ); +} + +void dbContext::subscribe ( + epicsGuard < epicsMutex > & guard, + struct dbAddr & addr, dbChannelIO & chan, + unsigned type, unsigned long count, unsigned mask, + cacStateNotify & notifyIn, cacChannel::ioid * pId ) +{ + guard.assertIdenticalMutex ( this->mutex ); + + /* + * the database uses type "int" to store these parameters + */ + if ( type > INT_MAX ) { + throw cacChannel::badType(); + } + if ( count > INT_MAX ) { + throw cacChannel::outOfBounds(); + } + + if ( ! this->ctx ) { + dbEventCtx tmpctx = 0; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + tmpctx = db_init_events (); + if ( ! tmpctx ) { + throw std::bad_alloc (); + } + + unsigned selfPriority = epicsThreadGetPrioritySelf (); + unsigned above; + epicsThreadBooleanStatus tbs = + epicsThreadLowestPriorityLevelAbove ( selfPriority, &above ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + above = selfPriority; + } + int status = db_start_events ( tmpctx, "CAC-event", + cacAttachClientCtx, ca_current_context (), above ); + if ( status ) { + db_close_events ( tmpctx ); + throw std::bad_alloc (); + } + } + if ( this->ctx ) { + // another thread tried to simultaneously setup + // the event system + db_close_events ( tmpctx ); + } + else { + this->ctx = tmpctx; + } + } + + dbSubscriptionIO & subscr = + * new ( this->dbSubscriptionIOFreeList ) + dbSubscriptionIO ( guard, this->mutex, *this, chan, + addr, notifyIn, type, count, mask, this->ctx ); + chan.dbContextPrivateListOfIO::eventq.add ( subscr ); + this->ioTable.idAssignAdd ( subscr ); + + if ( pId ) { + *pId = subscr.getId (); + } +} + +void dbContext::initiatePutNotify ( + epicsGuard < epicsMutex > & guard, + dbChannelIO & chan, struct dbAddr & addr, + unsigned type, unsigned long count, const void * pValue, + cacWriteNotify & notifyIn, cacChannel::ioid * pId ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( ! chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker = + new ( this->dbPutNotifyBlockerFreeList ) + dbPutNotifyBlocker ( this->mutex ); + this->ioTable.idAssignAdd ( *chan.dbContextPrivateListOfIO::pBlocker ); + } + chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( + guard, notifyIn, addr, type, count, pValue ); + if ( pId ) { + *pId = chan.dbContextPrivateListOfIO::pBlocker->getId (); + } +} + +void dbContext::destroyAllIO ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) +{ + guard.assertIdenticalMutex ( this->mutex ); + dbSubscriptionIO * pIO; + tsDLList < dbSubscriptionIO > tmp; + + while ( ( pIO = chan.dbContextPrivateListOfIO::eventq.get() ) ) { + this->ioTable.remove ( *pIO ); + tmp.add ( *pIO ); + } + if ( chan.dbContextPrivateListOfIO::pBlocker ) { + this->ioTable.remove ( *chan.dbContextPrivateListOfIO::pBlocker ); + } + + while ( ( pIO = tmp.get() ) ) { + // This prevents a db event callback from coming + // through after the notify IO is deleted + pIO->unsubscribe ( guard ); + // If they call ioCancel() here it will be ignored + // because the IO has been unregistered above. + pIO->channelDeleteException ( guard ); + pIO->destructor ( guard ); + this->dbSubscriptionIOFreeList.release ( pIO ); + } + + if ( chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker->destructor ( guard ); + this->dbPutNotifyBlockerFreeList.release ( chan.dbContextPrivateListOfIO::pBlocker ); + chan.dbContextPrivateListOfIO::pBlocker = 0; + } +} + +void dbContext::ioCancel ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan, + const cacChannel::ioid &id ) +{ + guard.assertIdenticalMutex ( this->mutex ); + dbBaseIO * pIO = this->ioTable.remove ( id ); + if ( pIO ) { + dbSubscriptionIO *pSIO = pIO->isSubscription (); + if ( pSIO ) { + chan.dbContextPrivateListOfIO::eventq.remove ( *pSIO ); + pSIO->unsubscribe ( guard ); + pSIO->channelDeleteException ( guard ); + pSIO->destructor ( guard ); + this->dbSubscriptionIOFreeList.release ( pSIO ); + } + else if ( pIO == chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker->cancel ( guard ); + } + else { + errlogPrintf ( "dbContext::ioCancel() unrecognized IO was probably leaked or not canceled\n" ); + } + } +} + +void dbContext::ioShow ( + epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, + unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + const dbBaseIO * pIO = this->ioTable.lookup ( id ); + if ( pIO ) { + pIO->show ( guard, level ); + } +} + +void dbContext::showAllIO ( const dbChannelIO & chan, unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + tsDLIterConst < dbSubscriptionIO > pItem = + chan.dbContextPrivateListOfIO::eventq.firstIter (); + while ( pItem.valid () ) { + pItem->show ( guard, level ); + pItem++; + } + if ( chan.dbContextPrivateListOfIO::pBlocker ) { + chan.dbContextPrivateListOfIO::pBlocker->show ( guard, level ); + } +} + +void dbContext::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->show ( guard, level ); +} + +void dbContext::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + printf ( "dbContext at %p\n", + static_cast ( this ) ); + if ( level > 0u ) { + printf ( "\tevent call back cache location %p, and its size %lu\n", + static_cast ( this->pStateNotifyCache ), this->stateNotifyCacheSize ); + this->readNotifyCache.show ( guard, level - 1 ); + } + if ( level > 1u ) { + this->mutex.show ( level - 2u ); + } + if ( this->pNetContext.get() ) { + this->pNetContext.get()->show ( guard, level ); + } +} + +void dbContext::flush ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->pNetContext.get() ) { + this->pNetContext.get()->flush ( guard ); + } +} + +unsigned dbContext::circuitCount ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->pNetContext.get() ) { + return this->pNetContext.get()->circuitCount ( guard ); + } + else { + return 0u; + } +} + +void dbContext::selfTest ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + this->ioTable.verify (); + + if ( this->pNetContext.get() ) { + this->pNetContext.get()->selfTest ( guard ); + } +} + +unsigned dbContext::beaconAnomaliesSinceProgramStart ( + epicsGuard < epicsMutex > & guard ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->pNetContext.get() ) { + return this->pNetContext.get()->beaconAnomaliesSinceProgramStart ( guard ); + } + else { + return 0u; + } +} + + diff --git a/src/db/dbContextReadNotifyCache.cpp b/src/db/dbContextReadNotifyCache.cpp new file mode 100644 index 000000000..de620250e --- /dev/null +++ b/src/db/dbContextReadNotifyCache.cpp @@ -0,0 +1,164 @@ +/*************************************************************************\ +* 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 + * Auther Jeff Hill + */ + +#include "epicsMutex.h" + +#include "cadef.h" // this can be eliminated when the callbacks use the new interface +#include "db_access.h" // should be eliminated here in the future + +#define epicsExportSharedSymbols + +#include "db_access_routines.h" +#include "dbCAC.h" + +dbContextReadNotifyCache::dbContextReadNotifyCache ( epicsMutex & mutexIn ) : + _mutex ( mutexIn ) +{ +} + +class privateAutoDestroyPtr { +public: + privateAutoDestroyPtr ( + dbContextReadNotifyCacheAllocator & allocator, unsigned long size ) : + _allocator ( allocator ), _p ( allocator.alloc ( size ) ) {} + ~privateAutoDestroyPtr () { _allocator.free ( _p ); } + char * get () const { return _p; } +private: + dbContextReadNotifyCacheAllocator & _allocator; + char * _p; + privateAutoDestroyPtr ( const privateAutoDestroyPtr & ); + privateAutoDestroyPtr & operator = ( const privateAutoDestroyPtr & ); +}; + +// extra effort taken here to not hold the lock when calling the callback +void dbContextReadNotifyCache::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbAddr & addr, + unsigned type, unsigned long count, cacReadNotify & notify ) +{ + guard.assertIdenticalMutex ( _mutex ); + + if ( type > INT_MAX ) { + notify.exception ( guard, ECA_BADTYPE, + "type code out of range (high side)", + type, count ); + return; + } + + if ( addr.no_elements < 0 ) { + notify.exception ( guard, ECA_BADCOUNT, + "database has negetive element count", + type, count); + return; + } + + if ( count > static_cast < unsigned > ( addr.no_elements ) ) { + notify.exception ( guard, ECA_BADCOUNT, + "element count out of range (high side)", + type, count); + return; + } + + unsigned long size = dbr_size_n ( type, count ); + privateAutoDestroyPtr ptr ( _allocator, size ); + int status; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + status = db_get_field ( &addr, static_cast ( type ), + ptr.get (), static_cast ( count ), 0 ); + } + if ( status ) { + notify.exception ( guard, ECA_GETFAIL, + "db_get_field() completed unsuccessfuly", + type, count ); + } + else { + notify.completion ( + guard, type, count, ptr.get () ); + } +} + +void dbContextReadNotifyCache::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( _mutex ); + + printf ( "dbContextReadNotifyCache\n" ); + if ( level > 0 ) { + this->_allocator.show ( level - 1 ); + } +} + +dbContextReadNotifyCacheAllocator::dbContextReadNotifyCacheAllocator () : + _readNotifyCacheSize ( 0 ), _pReadNotifyCache ( 0 ) +{ +} + +dbContextReadNotifyCacheAllocator::~dbContextReadNotifyCacheAllocator () +{ + this->reclaimAllCacheEntries (); +} + +void dbContextReadNotifyCacheAllocator::reclaimAllCacheEntries () +{ + + while ( _pReadNotifyCache ) { + cacheElem_t * pNext = _pReadNotifyCache->pNext; + delete [] _pReadNotifyCache; + _pReadNotifyCache = pNext; + } +} + +char * dbContextReadNotifyCacheAllocator::alloc ( unsigned long size ) +{ + if ( size > _readNotifyCacheSize ) { + this->reclaimAllCacheEntries (); + _readNotifyCacheSize = size; + } + + cacheElem_t * pAlloc = _pReadNotifyCache; + if ( pAlloc ) { + _pReadNotifyCache = pAlloc->pNext; + } + else { + size_t nElem = _readNotifyCacheSize / sizeof ( cacheElem_t ); + pAlloc = new cacheElem_t [ nElem + 1 ]; + } + return reinterpret_cast < char * > ( pAlloc ); +} + +void dbContextReadNotifyCacheAllocator::free ( char * pFree ) +{ + cacheElem_t * pAlloc = reinterpret_cast < cacheElem_t * > ( pFree ); + pAlloc->pNext = _pReadNotifyCache; + _pReadNotifyCache = pAlloc; +} + +void dbContextReadNotifyCacheAllocator::show ( unsigned level ) const +{ + printf ( "dbContextReadNotifyCacheAlocator\n" ); + if ( level > 0 ) { + size_t count =0; + cacheElem_t * pNext = _pReadNotifyCache; + while ( pNext ) { + pNext = _pReadNotifyCache->pNext; + count++; + } + printf ( "\tcount %lu and size %lu\n", + static_cast < unsigned long > ( count ), + _readNotifyCacheSize ); + } +} + + diff --git a/src/db/dbConvert.c b/src/db/dbConvert.c new file mode 100644 index 000000000..f370e0aa2 --- /dev/null +++ b/src/db/dbConvert.c @@ -0,0 +1,4651 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbConvert.c */ +/* + * Original Author: Bob Dalesio + * Current Author: Marty Kraimer + * Date: 11-7-90 +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsConvert.h" +#include "dbDefs.h" +#include "errlog.h" +#include "cvtFast.h" +#include "dbBase.h" +#include "link.h" +#include "dbFldTypes.h" +#include "dbStaticLib.h" +#include "errMdef.h" +#include "recSup.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbAccessDefs.h" +#include "recGbl.h" +#include "dbConvert.h" + +/* DATABASE ACCESS GET CONVERSION SUPPORT */ + +static long getStringString ( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + char *psrc=paddr->pfield; + short size=paddr->field_size; + short sizeto; + + /* always force result string to be null terminated*/ + sizeto = size; + if(sizeto>=MAX_STRING_SIZE) sizeto = MAX_STRING_SIZE-1; + + if(nRequest==1 && offset==0) { + strncpy(pbuffer,psrc,sizeto); + *(pbuffer+sizeto) = 0; + return(0); + } + psrc+= (size*offset); + while (nRequest) { + strncpy(pbuffer,psrc,sizeto); + *(pbuffer+sizeto) = 0; + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += size; + nRequest--; + } + return(0); +} + +static long getStringChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + char *psrc=(char *)paddr->pfield; + short value; + + if(nRequest==1 && offset==0) { + if(sscanf(psrc,"%hd",&value) == 1) { + *pbuffer = (char)value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(sscanf(psrc,"%hd",&value) == 1) { + *pbuffer = (char)value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + char *psrc=(char *)paddr->pfield; + unsigned short value; + + if(nRequest==1 && offset==0) { + if(sscanf(psrc,"%hu",&value) == 1) { + *pbuffer = (unsigned char)value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(sscanf(psrc,"%hu",&value) == 1) { + *pbuffer = (unsigned char)value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + char *psrc=(char *)paddr->pfield; + short value; + + if(nRequest==1 && offset==0) { + if(sscanf(psrc,"%hd",&value) == 1) { + *pbuffer = value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(sscanf(psrc,"%hd",&value) == 1) { + *pbuffer = value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + char *psrc=(char *)paddr->pfield; + unsigned short value; + + if(nRequest==1 && offset==0) { + if(sscanf(psrc,"%hu",&value) == 1) { + *pbuffer = value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(sscanf(psrc,"%hu",&value) == 1) { + *pbuffer = value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + char *psrc=(char *)paddr->pfield; + epicsInt32 value; + + if(nRequest==1 && offset==0) { + if(sscanf(psrc,"%d",&value) == 1) { + *pbuffer = value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(sscanf(psrc,"%d",&value) == 1) { + *pbuffer = value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + char *psrc=(char *)paddr->pfield; + double value; + + /*Convert to double first so that numbers like 1.0e3 convert properly*/ + /*Problem was old database access said to get unsigned long as double*/ + if(nRequest==1 && offset==0) { + if(epicsScanDouble(psrc, &value) == 1) { + *pbuffer = (epicsUInt32)value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(epicsScanDouble(psrc, &value) == 1) { + *pbuffer = (epicsUInt32)value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + char *psrc=(char *)paddr->pfield; + float value; + + if(nRequest==1 && offset==0) { + if(epicsScanFloat(psrc, &value) == 1) { + *pbuffer = value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(epicsScanFloat(psrc, &value) == 1) { + *pbuffer = value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + char *psrc=(char *)paddr->pfield; + double value; + + if(nRequest==1 && offset==0) { + if(epicsScanDouble(psrc, &value) == 1) { + *pbuffer = value; + return(0); + } else if(strlen(psrc) == 0) { + *pbuffer = 0.0; + return(0); + } else { + return(-1); + } + } + psrc += MAX_STRING_SIZE*offset; + while (nRequest) { + if(epicsScanDouble(psrc, &value) == 1) { + *pbuffer = value; + } else if(strlen(psrc) == 0) { + *pbuffer = 0.0; + } else { + return(-1); + } + pbuffer++; + if(++offset==no_elements) + psrc=paddr->pfield; + else + psrc += MAX_STRING_SIZE; + nRequest--; + } + return(0); +} + +static long getStringEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + return(getStringUshort(paddr,pto,nRequest,no_elements,offset)); +} + +static long getCharString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + cvtCharToString(*psrc,pbuffer); + return(0); + } + psrc += offset; + while (nRequest) { + cvtCharToString(*psrc,pbuffer); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(char *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(0); +} + +static long getCharChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getCharEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + char *psrc=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + cvtUcharToString(*psrc,pbuffer); + return(0); + } + psrc += offset; + while (nRequest) { + cvtUcharToString(*psrc,pbuffer); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(unsigned char *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(0); +} + +static long getUcharChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUcharEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + unsigned char *psrc=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + cvtShortToString(*psrc,pbuffer); + return(0); + } + psrc += offset; + while (nRequest) { + cvtShortToString(*psrc,pbuffer); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(short *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(0); +} + +static long getShortChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} +static long getShortShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getShortEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + short *psrc=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + cvtUshortToString(*psrc,pbuffer); + return(0); + } + psrc += offset; + while (nRequest) { + cvtUshortToString(*psrc,pbuffer); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(unsigned short *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(0); +} + +static long getUshortChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} +static long getUshortShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUshortEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + unsigned short *psrc=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + cvtLongToString(*psrc,pbuffer); + return(0); + } + psrc += offset; + while (nRequest) { + cvtLongToString(*psrc,pbuffer); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(epicsInt32 *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(0); +} + +static long getLongChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getLongEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + epicsInt32 *psrc=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + cvtUlongToString(*psrc,pbuffer); + return(0); + } + psrc += offset; + while (nRequest) { + cvtUlongToString(*psrc,pbuffer); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(epicsUInt32 *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(0); +} + +static long getUlongChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getUlongEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + epicsUInt32 *psrc=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + float *psrc=(float *)(paddr->pfield); + long status = 0; + int precision = 6; + struct rset *prset = 0; + + if(paddr) prset = dbGetRset(paddr); + if(prset && (prset->get_precision)) + status = (*prset->get_precision)(paddr,&precision); + if(nRequest==1 && offset==0) { + cvtFloatToString(*psrc,pbuffer,precision); + return(status); + } + psrc += offset; + while (nRequest) { + cvtFloatToString(*psrc,pbuffer,precision); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(float *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(status); +} + +static long getFloatChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getFloatEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + float *psrc=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + double *psrc=(double *)(paddr->pfield); + long status = 0; + int precision = 6; + struct rset *prset = 0; + + if(paddr) prset = dbGetRset(paddr); + if(prset && (prset->get_precision)) + status = (*prset->get_precision)(paddr,&precision); + if(nRequest==1 && offset==0) { + cvtDoubleToString(*psrc,pbuffer,precision); + return(status); + } + psrc += offset; + while (nRequest) { + cvtDoubleToString(*psrc,pbuffer,precision); + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + psrc=(double *)paddr->pfield; + else + psrc++; + nRequest--; + } + return(status); +} + +static long getDoubleChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = (epicsInt32)*psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = (epicsInt32)*psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = (epicsUInt32)*psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = (epicsUInt32)*psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = epicsConvertDoubleToFloat(*psrc); + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer = epicsConvertDoubleToFloat(*psrc); + ++psrc; ++pbuffer; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getDoubleEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + double *psrc=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + struct rset *prset; + long status; + + if((prset=dbGetRset(paddr)) && (prset->get_enum_str)) + return( (*prset->get_enum_str)(paddr,pbuffer) ); + status=S_db_noRSET; + recGblRecSupError(status,paddr,"dbGet","get_enum_str"); + return(S_db_badDbrtype); +} + +static long getEnumChar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumUchar( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned char *pbuffer = (unsigned char *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumShort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + short *pbuffer = (short *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumUshort( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + unsigned short *pbuffer = (unsigned short *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumLong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsInt32 *pbuffer = (epicsInt32 *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumUlong( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsUInt32 *pbuffer = (epicsUInt32 *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumFloat( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + float *pbuffer = (float *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumDouble( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + double *pbuffer = (double *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getEnumEnum( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + epicsEnum16 *pbuffer = (epicsEnum16 *)pto; + epicsEnum16 *psrc=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pbuffer = *psrc; + return(0); + } + psrc += offset; + while (nRequest) { + *pbuffer++ = *psrc++; + if(++offset==no_elements) psrc=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long getMenuString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + dbFldDes *pdbFldDes = paddr->pfldDes; + dbMenu *pdbMenu; + char **papChoiceValue; + char *pchoice; + epicsEnum16 choice_ind= *((epicsEnum16*)paddr->pfield); + + if(no_elements!=1){ + recGblDbaddrError(S_db_onlyOne,paddr,"dbGet(getMenuString)"); + return(S_db_onlyOne); + } + if(!pdbFldDes + || !(pdbMenu = (dbMenu *)pdbFldDes->ftPvt) + || (choice_ind>=pdbMenu->nChoice) + || !(papChoiceValue = pdbMenu->papChoiceValue) + || !(pchoice=papChoiceValue[choice_ind])) { + recGblDbaddrError(S_db_badChoice,paddr,"dbGet(getMenuString)"); + return(S_db_badChoice); + } + strncpy(pbuffer,pchoice,MAX_STRING_SIZE); + return(0); +} + +static long getDeviceString( + const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset) +{ + char *pbuffer = (char *)pto; + dbFldDes *pdbFldDes = paddr->pfldDes; + dbDeviceMenu *pdbDeviceMenu; + char **papChoice; + char *pchoice; + epicsEnum16 choice_ind= *((epicsEnum16*)paddr->pfield); + + if(no_elements!=1){ + recGblDbaddrError(S_db_onlyOne,paddr,"dbGet(getDeviceString)"); + return(S_db_onlyOne); + } + if(!pdbFldDes + || !(pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt) + || (choice_ind>=pdbDeviceMenu->nChoice ) + || !(papChoice = pdbDeviceMenu->papChoice) + || !(pchoice=papChoice[choice_ind])) { + recGblDbaddrError(S_db_badChoice,paddr,"dbGet(getDeviceString)"); + return(S_db_badChoice); + } + strncpy(pbuffer,pchoice,MAX_STRING_SIZE); + return(0); +} + +/* DATABASE ACCESS PUT CONVERSION SUPPORT */ + +static long putStringString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + char *pdest=paddr->pfield; + short size=paddr->field_size; + + if(nRequest==1 && offset==0) { + strncpy(pdest,pbuffer,size); + *(pdest+size-1) = 0; + return(0); + } + pdest+= (size*offset); + while (nRequest) { + strncpy(pdest,pbuffer,size); + *(pdest+size-1) = 0; + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putStringChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + char *pdest=(char *)paddr->pfield; + short value; + + if(nRequest==1 && offset==0) { + if(sscanf(pbuffer,"%hd",&value) == 1) { + *pdest = (char)value; + return(0); + } + else return(-1); + } + pdest += offset; + while (nRequest) { + if(sscanf(pbuffer,"%hd",&value) == 1) { + *pdest = (char)value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + unsigned char *pdest=(unsigned char *)paddr->pfield; + unsigned short value; + + if(nRequest==1 && offset==0) { + if(sscanf(pbuffer,"%hu",&value) == 1) { + *pdest = (unsigned char)value; + return(0); + } + else return(-1); + } + pdest += offset; + while (nRequest) { + if(sscanf(pbuffer,"%hu",&value) == 1) { + *pdest = (unsigned char)value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + short *pdest=(short *)paddr->pfield; + short value; + + if(nRequest==1 && offset==0) { + if(sscanf(pbuffer,"%hd",&value) == 1) { + *pdest = value; + return(0); + } + else return(-1); + } + pdest += offset; + while (nRequest) { + if(sscanf(pbuffer,"%hd",&value) == 1) { + *pdest = value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + unsigned short *pdest=(unsigned short *)paddr->pfield; + unsigned short value; + + if(nRequest==1 && offset==0) { + if(sscanf(pbuffer,"%hu",&value) == 1) { + *pdest = value; + return(0); + } + else return(-1); + } + pdest += offset; + while (nRequest) { + if(sscanf(pbuffer,"%hu",&value) == 1) { + *pdest = value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)paddr->pfield; + long value; + + if(nRequest==1 && offset==0) { + if(sscanf(pbuffer,"%ld",&value) == 1) { + *pdest = value; + return(0); + } + else return(-1); + } + pdest += offset; + while (nRequest) { + if(sscanf(pbuffer,"%ld",&value) == 1) { + *pdest = value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)paddr->pfield; + double value; + + /*Convert to double first so that numbers like 1.0e3 convert properly*/ + /*Problem was old database access said to get unsigned long as double*/ + if(nRequest==1 && offset==0) { + if(epicsScanDouble(pbuffer, &value) == 1) { + *pdest = (epicsUInt32)value; + return(0); + } + else return(-1); + } + pdest += offset; + while (nRequest) { + if(epicsScanDouble(pbuffer, &value) == 1) { + *pdest = (epicsUInt32)value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + float *pdest=(float *)paddr->pfield; + float value; + + if(nRequest==1 && offset==0) { + if(epicsScanFloat(pbuffer, &value) == 1) { + *pdest = value; + return(0); + } else { + return(-1); + } + } + pdest += offset; + while (nRequest) { + if(epicsScanFloat(pbuffer, &value) == 1) { + *pdest = value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + double *pdest=(double *)paddr->pfield; + double value; + + if(nRequest==1 && offset==0) { + if(epicsScanDouble(pbuffer, &value) == 1) { + *pdest = value; + return(0); + } else { + return(-1); + } + } + pdest += offset; + while (nRequest) { + if(epicsScanDouble(pbuffer, &value) == 1) { + *pdest = value; + } else { + return(-1); + } + pbuffer += MAX_STRING_SIZE; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest++; + nRequest--; + } + return(0); +} + +static long putStringEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + struct rset *prset; + epicsEnum16 *pfield= (epicsEnum16*)(paddr->pfield); + long status; + unsigned int nchoices,ind; + int nargs,nchars; + struct dbr_enumStrs enumStrs; + + if((prset=dbGetRset(paddr)) + && (prset->put_enum_str)) { + status = (*prset->put_enum_str)(paddr,pbuffer); + if(!status) return(0); + if(prset->get_enum_strs) { + status = (*prset->get_enum_strs)(paddr,&enumStrs); + if(!status) { + nchoices = enumStrs.no_str; + nargs = sscanf(pbuffer," %u %n",&ind,&nchars); + if(nargs==1 && nchars==strlen(pbuffer) && indprecord,pbuffer); + } + return(status); +} + +static long putStringMenu( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + dbFldDes *pdbFldDes = paddr->pfldDes; + dbMenu *pdbMenu; + char **papChoiceValue; + char *pchoice; + epicsEnum16 *pfield= (epicsEnum16*)(paddr->pfield); + unsigned int nChoice,ind; + int nargs,nchars; + + if(no_elements!=1){ + recGblDbaddrError(S_db_onlyOne,paddr,"dbPut(putStringMenu)"); + return(S_db_onlyOne); + } + if(pdbFldDes + && (pdbMenu = (dbMenu *)pdbFldDes->ftPvt) + && (papChoiceValue = pdbMenu->papChoiceValue)) { + nChoice = pdbMenu->nChoice; + for(ind=0; indpfldDes; + dbDeviceMenu *pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; + char **papChoice; + char *pchoice; + epicsEnum16 *pfield= (epicsEnum16*)(paddr->pfield); + unsigned int nChoice,ind; + int nargs,nchars; + + if(no_elements!=1){ + recGblDbaddrError(S_db_onlyOne,paddr,"dbPut(putStringDevice)"); + return(S_db_onlyOne); + } + if(pdbFldDes + && (pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt) + && (papChoice = pdbDeviceMenu->papChoice)) { + nChoice = pdbDeviceMenu->nChoice; + for(ind=0; indpfield); + short size=paddr->field_size; + + + if(nRequest==1 && offset==0) { + cvtCharToString(*pbuffer,pdest); + return(0); + } + pdest += (size*offset); + while (nRequest) { + cvtCharToString(*pbuffer,pdest); + pbuffer++; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putCharChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putCharEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const char *pbuffer = (const char *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + char *pdest=(char *)(paddr->pfield); + short size=paddr->field_size; + + + if(nRequest==1 && offset==0) { + cvtUcharToString(*pbuffer,pdest); + return(0); + } + pdest += (size*offset); + while (nRequest) { + cvtUcharToString(*pbuffer,pdest); + pbuffer++; + if(++offset==no_elements) + pdest=paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putUcharChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUcharEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned char *pbuffer = (const unsigned char *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + char *pdest=(char *)(paddr->pfield); + short size=paddr->field_size; + + + if(nRequest==1 && offset==0) { + cvtShortToString(*pbuffer,pdest); + return(0); + } + pdest += (size*offset); + while (nRequest) { + cvtShortToString(*pbuffer,pdest); + pbuffer++; + if(++offset==no_elements) + pdest=(char *)paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putShortChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putShortEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const short *pbuffer = (const short *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + char *pdest=(char *)(paddr->pfield); + short size=paddr->field_size; + + + if(nRequest==1 && offset==0) { + cvtUshortToString(*pbuffer,pdest); + return(0); + } + pdest += (size*offset); + while (nRequest) { + cvtUshortToString(*pbuffer,pdest); + pbuffer++; + if(++offset==no_elements) + pdest=(char *)paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putUshortChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUshortEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const unsigned short *pbuffer = (const unsigned short *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + char *pdest=(char *)(paddr->pfield); + short size=paddr->field_size; + + + if(nRequest==1 && offset==0) { + cvtLongToString(*pbuffer,pdest); + return(0); + } + pdest += (size*offset); + while (nRequest) { + cvtLongToString(*pbuffer,pdest); + pbuffer++; + if(++offset==no_elements) + pdest=(char *)paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putLongChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putLongEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsInt32 *pbuffer = (const epicsInt32 *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + char *pdest=(char *)(paddr->pfield); + short size=paddr->field_size; + + + if(nRequest==1 && offset==0) { + cvtUlongToString(*pbuffer,pdest); + return(0); + } + pdest += (size*offset); + while (nRequest) { + cvtUlongToString(*pbuffer,pdest); + pbuffer++; + if(++offset==no_elements) + pdest=(char *)paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putUlongChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putUlongEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsUInt32 *pbuffer = (const epicsUInt32 *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + char *pdest=(char *)(paddr->pfield); + long status = 0; + int precision = 6; + struct rset *prset = 0; + short size=paddr->field_size; + + if(paddr) prset = dbGetRset(paddr); + if(prset && (prset->get_precision)) + status = (*prset->get_precision)(paddr,&precision); + if(nRequest==1 && offset==0) { + cvtFloatToString(*pbuffer,pdest,precision); + return(status); + } + pdest += (size*offset); + while (nRequest) { + cvtFloatToString(*pbuffer,pdest,precision); + pbuffer++; + if(++offset==no_elements) + pdest=(char *)paddr->pfield; + else + pdest += size; + nRequest--; + } + return(status); +} + +static long putFloatChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putFloatEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const float *pbuffer = (const float *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + char *pdest=(char *)(paddr->pfield); + long status = 0; + int precision = 6; + struct rset *prset = 0; + short size=paddr->field_size; + + if(paddr) prset = dbGetRset(paddr); + if(prset && (prset->get_precision)) + status = (*prset->get_precision)(paddr,&precision); + if(nRequest==1 && offset==0) { + cvtDoubleToString(*pbuffer,pdest,precision); + return(status); + } + pdest += (size*offset); + while (nRequest) { + cvtDoubleToString(*pbuffer,pdest,precision); + pbuffer++; + if(++offset==no_elements) + pdest=(char *)paddr->pfield; + else + pdest += size; + nRequest--; + } + return(status); +} + +static long putDoubleChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = (epicsInt32)*pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = (epicsInt32)*pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = (epicsUInt32)*pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = (epicsUInt32)*pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = epicsConvertDoubleToFloat(*pbuffer); + return(0); + } + pdest += offset; + while (nRequest) { + *pdest = epicsConvertDoubleToFloat(*pbuffer); + ++pbuffer; ++pdest; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putDoubleEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const double *pbuffer = (const double *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumString( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + char *pdest=(char *)(paddr->pfield); + short size=paddr->field_size; + + + if(nRequest==1 && offset==0) { + cvtUshortToString(*pbuffer,pdest); + return(0); + } + pdest += (size*offset); + while (nRequest) { + cvtUshortToString(*pbuffer,pdest); + pbuffer++; + if(++offset==no_elements) + pdest=(char *)paddr->pfield; + else + pdest += size; + nRequest--; + } + return(0); +} + +static long putEnumChar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + char *pdest=(char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumUchar( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + unsigned char *pdest=(unsigned char *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned char *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumShort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + short *pdest=(short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumUshort( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + unsigned short *pdest=(unsigned short *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(unsigned short *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumLong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + epicsInt32 *pdest=(epicsInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumUlong( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + epicsUInt32 *pdest=(epicsUInt32 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsUInt32 *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumFloat( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + float *pdest=(float *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(float *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumDouble( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + double *pdest=(double *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(double *)paddr->pfield; + nRequest--; + } + return(0); +} + +static long putEnumEnum( + dbAddr *paddr, + const void *pfrom, long nRequest, long no_elements, long offset) +{ + const epicsEnum16 *pbuffer = (const epicsEnum16 *)pfrom; + epicsEnum16 *pdest=(epicsEnum16 *)(paddr->pfield); + + if(nRequest==1 && offset==0) { + *pdest = *pbuffer; + return(0); + } + pdest += offset; + while (nRequest) { + *pdest++ = *pbuffer++; + if(++offset==no_elements) pdest=(epicsEnum16 *)paddr->pfield; + nRequest--; + } + return(0); +} + +/* This is the table of routines for converting database fields */ +/* the rows represent the field type of the database field */ +/* the columns represent the types of the buffer in which they are placed */ + +/* buffer types are******************************************************** + DBR_STRING, DBR_CHR, DBR_UCHAR, DBR_SHORT, DBR_USHORT, + DBR_LONG, DBR_ULONG, DBR_FLOAT, DBR_DOUBLE, DBR_ENUM + ***************************************************************************/ + +epicsShareDef GETCONVERTFUNC dbGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1] = { + +/* source is a DBF_STRING */ +{getStringString, getStringChar, getStringUchar, getStringShort, getStringUshort, + getStringLong, getStringUlong, getStringFloat, getStringDouble, getStringEnum}, +/* source is a DBF_CHAR */ +{getCharString, getCharChar, getCharUchar, getCharShort, getCharUshort, + getCharLong, getCharUlong, getCharFloat, getCharDouble, getCharEnum}, +/* source is a DBF_UCHAR */ +{getUcharString, getUcharChar, getUcharUchar, getUcharShort, getUcharUshort, + getUcharLong, getUcharUlong, getUcharFloat, getUcharDouble, getUcharEnum}, +/* source is a DBF_SHORT */ +{getShortString, getShortChar, getShortUchar, getShortShort, getShortUshort, + getShortLong, getShortUlong, getShortFloat, getShortDouble, getShortEnum}, +/* source is a DBF_USHORT */ +{getUshortString, getUshortChar, getUshortUchar, getUshortShort, getUshortUshort, + getUshortLong, getUshortUlong, getUshortFloat, getUshortDouble, getUshortEnum}, +/* source is a DBF_LONG */ +{getLongString, getLongChar, getLongUchar, getLongShort, getLongUshort, + getLongLong, getLongUlong, getLongFloat, getLongDouble, getLongEnum}, +/* source is a DBF_ULONG */ +{getUlongString, getUlongChar, getUlongUchar, getUlongShort, getUlongUshort, + getUlongLong, getUlongUlong, getUlongFloat, getUlongDouble, getUlongEnum}, +/* source is a DBF_FLOAT */ +{getFloatString, getFloatChar, getFloatUchar, getFloatShort, getFloatUshort, + getFloatLong, getFloatUlong, getFloatFloat, getFloatDouble, getFloatEnum}, +/* source is a DBF_DOUBLE */ +{getDoubleString, getDoubleChar, getDoubleUchar, getDoubleShort, getDoubleUshort, + getDoubleLong, getDoubleUlong, getDoubleFloat, getDoubleDouble, getDoubleEnum}, +/* source is a DBF_ENUM */ +{getEnumString, getEnumChar, getEnumUchar, getEnumShort, getEnumUshort, + getEnumLong, getEnumUlong, getEnumFloat, getEnumDouble, getEnumEnum}, +/* source is a DBF_MENU */ +{getMenuString, getEnumChar, getEnumUchar, getEnumShort, getEnumUshort, + getEnumLong, getEnumUlong, getEnumFloat, getEnumDouble, getEnumEnum}, +/* source is a DBF_DEVICE */ +{getDeviceString,getEnumChar, getEnumUchar, getEnumShort, getEnumUshort, + getEnumLong, getEnumUlong, getEnumFloat, getEnumDouble, getEnumEnum}, +}; + +/* This is the table of routines for converting database fields */ +/* the rows represent the buffer types */ +/* the columns represent the field types */ + +/* field types are******************************************************** + DBF_STRING, DBF_CHAR, DBF_UCHAR, DBF_SHORT, DBF_USHORT, + DBF_LONG, DBF_ULONG, DBF_FLOAT, DBF_DOUBLE, DBF_ENUM + DBF_MENU, DBF_DEVICE + ***************************************************************************/ + +epicsShareDef PUTCONVERTFUNC dbPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1] = { +/* source is a DBR_STRING */ +{putStringString, putStringChar, putStringUchar, putStringShort, putStringUshort, + putStringLong, putStringUlong, putStringFloat, putStringDouble, putStringEnum, + putStringMenu,putStringDevice}, +/* source is a DBR_CHAR */ +{putCharString, putCharChar, putCharUchar, putCharShort, putCharUshort, + putCharLong, putCharUlong, putCharFloat, putCharDouble, putCharEnum, + putCharEnum, putCharEnum}, +/* source is a DBR_UCHAR */ +{putUcharString, putUcharChar, putUcharUchar, putUcharShort, putUcharUshort, + putUcharLong, putUcharUlong, putUcharFloat, putUcharDouble, putUcharEnum, + putUcharEnum, putUcharEnum}, +/* source is a DBR_SHORT */ +{putShortString, putShortChar, putShortUchar, putShortShort, putShortUshort, + putShortLong, putShortUlong, putShortFloat, putShortDouble, putShortEnum, + putShortEnum, putShortEnum}, +/* source is a DBR_USHORT */ +{putUshortString, putUshortChar, putUshortUchar, putUshortShort, putUshortUshort, + putUshortLong, putUshortUlong, putUshortFloat, putUshortDouble, putUshortEnum, + putUshortEnum, putUshortEnum}, +/* source is a DBR_LONG */ +{putLongString, putLongChar, putLongUchar, putLongShort, putLongUshort, + putLongLong, putLongUlong, putLongFloat, putLongDouble, putLongEnum, + putLongEnum, putLongEnum}, +/* source is a DBR_ULONG */ +{putUlongString, putUlongChar, putUlongUchar, putUlongShort, putUlongUshort, + putUlongLong, putUlongUlong, putUlongFloat, putUlongDouble, putUlongEnum, + putUlongEnum, putUlongEnum}, +/* source is a DBR_FLOAT */ +{putFloatString, putFloatChar, putFloatUchar, putFloatShort, putFloatUshort, + putFloatLong, putFloatUlong, putFloatFloat, putFloatDouble, putFloatEnum, + putFloatEnum, putFloatEnum}, +/* source is a DBR_DOUBLE */ +{putDoubleString, putDoubleChar, putDoubleUchar, putDoubleShort, putDoubleUshort, + putDoubleLong, putDoubleUlong, putDoubleFloat, putDoubleDouble, putDoubleEnum, + putDoubleEnum, putDoubleEnum}, +/* source is a DBR_ENUM */ +{putEnumString, putEnumChar, putEnumUchar, putEnumShort, putEnumUshort, + putEnumLong, putEnumUlong, putEnumFloat, putEnumDouble, putEnumEnum, + putEnumEnum, putEnumEnum} +}; diff --git a/src/db/dbConvert.h b/src/db/dbConvert.h new file mode 100644 index 000000000..a7e3dc70a --- /dev/null +++ b/src/db/dbConvert.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* dbConvert.h */ + +#ifndef INCdbConverth +#define INCdbConverth + +#include "dbFldTypes.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long (*GETCONVERTFUNC)(const DBADDR *paddr, void *pbuffer, + long nRequest, long no_elements, long offset); +typedef long (*PUTCONVERTFUNC)(DBADDR *paddr, const void *pbuffer, + long nRequest, long no_elements, long offset); + +epicsShareExtern GETCONVERTFUNC dbGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1]; +epicsShareExtern PUTCONVERTFUNC dbPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1]; + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbConverth*/ diff --git a/src/db/dbConvertFast.h b/src/db/dbConvertFast.h new file mode 100644 index 000000000..cd9f4f963 --- /dev/null +++ b/src/db/dbConvertFast.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbConvertFast.h */ + +#ifndef INCdbConvertFasth +#define INCdbConvertFasth + +#include "dbFldTypes.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])(); +epicsShareExtern long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])(); + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbConvertFasth*/ diff --git a/src/db/dbEvent.c b/src/db/dbEvent.c new file mode 100644 index 000000000..7dfb44b3c --- /dev/null +++ b/src/db/dbEvent.c @@ -0,0 +1,1051 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbEvent.c */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* routines for scheduling events to lower priority tasks via the RT kernel */ +/* + * Author: Jeffrey O. Hill + * Date: 4-1-89 +*/ + +#include +#include +#include +#include +#include +#include + +#include "epicsAssert.h" +#include "cantProceed.h" +#include "dbDefs.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsThread.h" +#include "errlog.h" +#include "taskwd.h" +#include "freeList.h" +#include "dbBase.h" +#include "dbFldTypes.h" +#include "link.h" +#include "dbCommon.h" +#include "caeventmask.h" +#include "db_field_log.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbLock.h" +#include "dbAccessDefs.h" +#include "dbEvent.h" + +#define EVENTSPERQUE 32 +#define EVENTENTRIES 4 /* the number of que entries for each event */ +#define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE) +#define EVENTQEMPTY ((struct evSubscrip *)NULL) + +/* + * event subscruiption + */ +struct evSubscrip { + ELLNODE node; + struct dbAddr *paddr; + EVENTFUNC *user_sub; + void *user_arg; + struct event_que *ev_que; + db_field_log *pLastLog; + unsigned long npend; /* n times this event is on the queue */ + unsigned long nreplace; /* n times replacing event on the queue */ + unsigned char select; + char valque; + char callBackInProgress; + char enabled; +}; + +/* + * really a ring buffer + */ +struct event_que { + /* lock writers to the ring buffer only */ + /* readers must never slow up writers */ + epicsMutexId writelock; + db_field_log valque[EVENTQUESIZE]; + struct evSubscrip *evque[EVENTQUESIZE]; + struct event_que *nextque; /* in case que quota exceeded */ + struct event_user *evUser; /* event user parent struct */ + unsigned short putix; + unsigned short getix; + unsigned short quota; /* the number of assigned entries*/ + unsigned short nDuplicates; /* N events duplicated on this q */ + unsigned short nCanceled; /* the number of canceled entries */ +}; + +struct event_user { + struct event_que firstque; /* the first event que */ + + epicsMutexId lock; + epicsEventId ppendsem; /* Wait while empty */ + epicsEventId pflush_sem; /* wait for flush */ + + EXTRALABORFUNC *extralabor_sub;/* off load to event task */ + void *extralabor_arg;/* parameter to above */ + + epicsThreadId taskid; /* event handler task id */ + struct evSubscrip *pSuicideEvent; /* event that is deleteing itself */ + unsigned queovr; /* event que overflow count */ + unsigned char pendexit; /* exit pend task */ + unsigned char extra_labor; /* if set call extra labor func */ + unsigned char flowCtrlMode; /* replace existing monitor */ + unsigned char extraLaborBusy; + void (*init_func)(); + epicsThreadId init_func_arg; +}; + +/* + * Reliable intertask communication requires copying the current value of the + * channel for later queing so 3 stepper motor steps of 10 each do not turn + * into only 10 or 20 total steps part of the time. + */ + +#define RNGINC(OLD)\ +( (unsigned short) ( (OLD) >= (EVENTQUESIZE-1) ? 0 : (OLD)+1 ) ) + +#define LOCKEVQUE(EV_QUE)\ +epicsMutexMustLock((EV_QUE)->writelock); + +#define UNLOCKEVQUE(EV_QUE)\ +epicsMutexUnlock((EV_QUE)->writelock); + +#define LOCKREC(RECPTR)\ +epicsMutexMustLock((RECPTR)->mlok); + +#define UNLOCKREC(RECPTR)\ +epicsMutexUnlock((RECPTR)->mlok); + +static void *dbevEventUserFreeList; +static void *dbevEventQueueFreeList; +static void *dbevEventBlockFreeList; + +static char *EVENT_PEND_NAME = "eventTask"; + +static struct evSubscrip canceledEvent; + +static unsigned short ringSpace ( const struct event_que *pevq ) +{ + if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) { + if ( pevq->getix > pevq->putix ) { + return ( unsigned short ) ( pevq->getix - pevq->putix ); + } + else { + return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix ); + } + } + return 0; +} + +/* + * db_event_list () + */ +int epicsShareAPI db_event_list ( const char *pname, unsigned level ) +{ + return dbel ( pname, level ); +} + +/* + * dbel () + */ +int epicsShareAPI dbel ( const char *pname, unsigned level ) +{ + DBADDR addr; + long status; + struct evSubscrip *pevent; + dbFldDes *pdbFldDes; + + if ( ! pname ) return DB_EVENT_OK; + status = dbNameToAddr ( pname, &addr ); + if ( status != 0 ) { + errMessage ( status, " dbNameToAddr failed" ); + return DB_EVENT_ERROR; + } + + LOCKREC ( addr.precord ); + + pevent = (struct evSubscrip *) ellFirst ( &addr.precord->mlis ); + + if ( ! pevent ) { + printf ( "\"%s\": No PV event subscriptions ( monitors ).\n", pname ); + UNLOCKREC (addr.precord); + return DB_EVENT_OK; + } + + printf ( "%u PV Event Subscriptions ( monitors ).\n", + ellCount ( &addr.precord->mlis ) ); + + while ( pevent ) { + pdbFldDes = pevent->paddr->pfldDes; + + if ( level > 0 ) { + printf ( "%4.4s", pdbFldDes->name ); + + printf ( " { " ); + if ( pevent->select & DBE_VALUE ) printf( "VALUE " ); + if ( pevent->select & DBE_LOG ) printf( "LOG " ); + if ( pevent->select & DBE_ALARM ) printf( "ALARM " ); + if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " ); + printf ( "}" ); + + if ( pevent->npend ) { + printf ( " undelivered=%ld", pevent->npend ); + } + + if ( level > 1 ) { + unsigned nEntriesFree = ringSpace ( pevent->ev_que ); + if ( nEntriesFree == 0u ) { + printf ( ", thread=%p, queue full", + (void *) pevent->ev_que->evUser->taskid ); + } + else if ( nEntriesFree == EVENTQUESIZE ) { + printf ( ", thread=%p, queue empty", + (void *) pevent->ev_que->evUser->taskid ); + } + else { + printf ( ", thread=%p, unused entries=%u", + (void *) pevent->ev_que->evUser->taskid, nEntriesFree ); + } + } + + if ( level > 2 ) { + if ( pevent->nreplace ) { + printf (", discarded by replacement=%ld", pevent->nreplace); + } + if ( ! pevent->valque ) { + printf (", queueing disabled" ); + } + if ( pevent->ev_que->nDuplicates ) { + printf (", duplicate count =%u\n", + pevent->ev_que->nDuplicates ); + } + if ( pevent->ev_que->nCanceled ) { + printf (", canceled count =%u\n", + pevent->ev_que->nCanceled ); + } + } + + if ( level > 3 ) { + printf ( ", ev %p, ev que %p, ev user %p", + ( void * ) pevent, + ( void * ) pevent->ev_que, + ( void * ) pevent->ev_que->evUser ); + } + + printf( "\n" ); + } + + pevent = (struct evSubscrip *) ellNext ( &pevent->node ); + } + + UNLOCKREC ( addr.precord ); + + return DB_EVENT_OK; +} + +/* + * DB_INIT_EVENTS() + * + * + * Initialize the event facility for this task. Must be called at least once + * by each task which uses the db event facility + * + * returns: ptr to event user block or NULL if memory can't be allocated + */ +dbEventCtx epicsShareAPI db_init_events (void) +{ + struct event_user *evUser; + + if (!dbevEventUserFreeList) { + freeListInitPvt(&dbevEventUserFreeList, + sizeof(struct event_user),8); + } + if (!dbevEventQueueFreeList) { + freeListInitPvt(&dbevEventQueueFreeList, + sizeof(struct event_que),8); + } + if (!dbevEventBlockFreeList) { + freeListInitPvt(&dbevEventBlockFreeList, + sizeof(struct evSubscrip),256); + } + + evUser = (struct event_user *) + freeListCalloc(dbevEventUserFreeList); + if (!evUser) { + return NULL; + } + + evUser->firstque.evUser = evUser; + evUser->firstque.writelock = epicsMutexCreate(); + if (!evUser->firstque.writelock) { + return NULL; + } + + evUser->ppendsem = epicsEventCreate(epicsEventEmpty); + if (!evUser->ppendsem) { + epicsMutexDestroy (evUser->firstque.writelock); + return NULL; + } + evUser->pflush_sem = epicsEventCreate(epicsEventEmpty); + if (!evUser->pflush_sem) { + epicsMutexDestroy (evUser->firstque.writelock); + epicsEventDestroy (evUser->ppendsem); + return NULL; + } + evUser->lock = epicsMutexCreate(); + if (!evUser->lock) { + epicsMutexDestroy (evUser->firstque.writelock); + epicsEventDestroy (evUser->pflush_sem); + epicsEventDestroy (evUser->ppendsem); + return NULL; + } + + evUser->flowCtrlMode = FALSE; + evUser->extraLaborBusy = FALSE; + evUser->pSuicideEvent = NULL; + return (dbEventCtx) evUser; +} + +/* + * DB_CLOSE_EVENTS() + * + * evUser block and additional event queues + * deallocated when the event thread terminates + * itself + * + */ +void epicsShareAPI db_close_events (dbEventCtx ctx) +{ + struct event_user *evUser = (struct event_user *) ctx; + + /* + * Exit not forced on event blocks for now - this is left to channel + * access and any other tasks using this facility which can find them + * more efficiently. + * + * NOTE: not deleting events before calling this routine could be + * hazardous to the system's health. + */ + + evUser->pendexit = TRUE; + + /* notify the waiting task */ + epicsEventSignal(evUser->ppendsem); +} + +/* + * DB_ADD_EVENT() + */ +dbEventSubscription epicsShareAPI db_add_event ( + dbEventCtx ctx, struct dbAddr *paddr, + EVENTFUNC *user_sub, void *user_arg, unsigned select) +{ + struct event_user *evUser = (struct event_user *) ctx; + struct dbCommon *precord; + struct event_que *ev_que; + struct event_que *tmp_que; + struct evSubscrip *pevent; + + precord = paddr->precord; + + /* + * Don't add events which will not be triggered + */ + if ( select==0 || select > UCHAR_MAX ) { + return NULL; + } + + pevent = freeListCalloc (dbevEventBlockFreeList); + if (!pevent) { + return NULL; + } + + /* find an event que block with enough quota */ + /* otherwise add a new one to the list */ + ev_que = &evUser->firstque; + while (TRUE) { + if (ev_que->quota + ev_que->nCanceled < EVENTQUESIZE - EVENTENTRIES) { + break; + } + if (!ev_que->nextque) { + tmp_que = (struct event_que *) + freeListCalloc(dbevEventQueueFreeList); + if (!tmp_que) { + freeListFree (dbevEventBlockFreeList, pevent); + return NULL; + } + tmp_que->evUser = evUser; + tmp_que->writelock = epicsMutexCreate(); + if (!tmp_que->writelock) { + freeListFree (dbevEventBlockFreeList, pevent); + freeListFree (dbevEventQueueFreeList, tmp_que); + return NULL; + } + ev_que->nextque = tmp_que; + ev_que = tmp_que; + break; + } + ev_que = ev_que->nextque; + } + + pevent->npend = 0ul; + pevent->nreplace = 0ul; + pevent->user_sub = user_sub; + pevent->user_arg = user_arg; + pevent->paddr = paddr; + pevent->select = (unsigned char) select; + pevent->pLastLog = NULL; /* not yet in the queue */ + pevent->callBackInProgress = FALSE; + pevent->enabled = FALSE; + pevent->ev_que = ev_que; + + LOCKEVQUE(ev_que); + ev_que->quota += EVENTENTRIES; + UNLOCKEVQUE(ev_que); + + /* + * Simple types values queued up for reliable interprocess + * communication (for other types they get whatever happens to be + * there upon wakeup) + */ + if( paddr->no_elements == 1 && + paddr->field_size <= sizeof(union native_value)) { + pevent->valque = TRUE; + } + else { + pevent->valque = FALSE; + } + + return pevent; +} + +/* + * db_event_enable() + */ +void epicsShareAPI db_event_enable (dbEventSubscription es) +{ + struct evSubscrip *pevent = (struct evSubscrip *) es; + struct dbCommon *precord; + + precord = (struct dbCommon *) pevent->paddr->precord; + + LOCKREC(precord); + if ( ! pevent->enabled ) { + ellAdd (&precord->mlis, &pevent->node); + pevent->enabled = TRUE; + } + UNLOCKREC(precord); +} + +/* + * db_event_disable() + */ +void epicsShareAPI db_event_disable (dbEventSubscription es) +{ + struct evSubscrip *pevent = (struct evSubscrip *) es; + struct dbCommon *precord; + + precord = (struct dbCommon *) pevent->paddr->precord; + + LOCKREC(precord); + if ( pevent->enabled ) { + ellDelete(&precord->mlis, &pevent->node); + pevent->enabled = FALSE; + } + UNLOCKREC(precord); +} + +/* + * event_remove() + * event queue lock _must_ be applied + */ +static void event_remove ( struct event_que *ev_que, + unsigned short index, struct evSubscrip *placeHolder ) +{ + struct evSubscrip *pEvent = ev_que->evque[index]; + + ev_que->evque[index] = placeHolder; + if ( pEvent->npend == 1u ) { + pEvent->pLastLog = NULL; + } + else { + assert ( pEvent->npend > 1u ); + assert ( ev_que->nDuplicates >= 1u ); + ev_que->nDuplicates--; + } + pEvent->npend--; +} + +/* + * DB_CANCEL_EVENT() + * + * This routine does not prevent two threads from deleting + * the same block at the same time. + * + */ +void epicsShareAPI db_cancel_event (dbEventSubscription es) +{ + struct evSubscrip * pevent = ( struct evSubscrip * ) es; + struct dbCommon * precord; + unsigned short getix; + + precord = ( struct dbCommon * ) pevent->paddr->precord; + + db_event_disable ( es ); + + /* + * flag the event as canceled by NULLing out the callback handler + * + * make certain that the event isnt being accessed while + * its call back changes + */ + LOCKEVQUE ( pevent->ev_que ) + + pevent->user_sub = NULL; + + /* + * purge this event from the queue + * + * Its better to take this approach rather than waiting + * for the event thread to finish removing this event + * from the queue because the event thread will not + * process if we are in flow control mode. Since blocking + * here will block CA's TCP input queue then a dead lock + * would be possible. + */ + for ( getix = pevent->ev_que->getix; + pevent->ev_que->evque[getix] != EVENTQEMPTY; ) { + if ( pevent->ev_que->evque[getix] == pevent ) { + assert ( pevent->ev_que->nCanceled < USHRT_MAX ); + pevent->ev_que->nCanceled++; + event_remove ( pevent->ev_que, getix, &canceledEvent ); + } + getix = RNGINC ( getix ); + if ( getix == pevent->ev_que->getix ) { + break; + } + } + assert ( pevent->npend == 0u ); + + if ( pevent->ev_que->evUser->taskid == epicsThreadGetIdSelf() ) { + pevent->ev_que->evUser->pSuicideEvent = pevent; + } + else { + while ( pevent->callBackInProgress ) { + UNLOCKEVQUE ( pevent->ev_que ) + epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem ); + LOCKEVQUE ( pevent->ev_que ) + } + } + + pevent->ev_que->quota -= EVENTENTRIES; + + UNLOCKEVQUE ( pevent->ev_que ) + + freeListFree ( dbevEventBlockFreeList, pevent ); + + return; +} + +/* + * DB_FLUSH_EXTRA_LABOR_EVENT() + * + * waits for extra labor in progress to finish + */ +void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx) +{ + struct event_user *evUser = (struct event_user *) ctx; + + epicsMutexMustLock ( evUser->lock ); + while ( evUser->extraLaborBusy ) { + epicsMutexUnlock ( evUser->lock ); + epicsThreadSleep(1.0); + epicsMutexMustLock ( evUser->lock ); + } + epicsMutexUnlock ( evUser->lock ); +} + +/* + * DB_ADD_EXTRA_LABOR_EVENT() + * + * Specify a routine to be called + * when labor is offloaded to the + * event task + */ +int epicsShareAPI db_add_extra_labor_event ( + dbEventCtx ctx, EXTRALABORFUNC *func, void *arg) +{ + struct event_user *evUser = (struct event_user *) ctx; + + epicsMutexMustLock ( evUser->lock ); + evUser->extralabor_sub = func; + evUser->extralabor_arg = arg; + epicsMutexUnlock ( evUser->lock ); + + return DB_EVENT_OK; +} + +/* + * DB_POST_EXTRA_LABOR() + */ +int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) +{ + struct event_user *evUser = (struct event_user *) ctx; + int doit; + + epicsMutexMustLock ( evUser->lock ); + if ( ! evUser->extra_labor ) { + evUser->extra_labor = TRUE; + doit = TRUE; + } + else { + doit = FALSE; + } + epicsMutexUnlock ( evUser->lock ); + + if ( doit ) { + epicsEventSignal(evUser->ppendsem); + } + + return DB_EVENT_OK; +} + +/* + * DB_POST_SINGLE_EVENT_PRIVATE() + * + * NOTE: This assumes that the db scan lock is already applied + */ +static void db_post_single_event_private (struct evSubscrip *event) +{ + struct event_que *ev_que; + db_field_log *pLog; + int firstEventFlag; + unsigned rngSpace; + + ev_que = event->ev_que; + + /* + * evUser ring buffer must be locked for the multiple + * threads writing/reading it + */ + + LOCKEVQUE(ev_que) + + /* + * if we have an event on the queue and we are + * not saving the current value (because this is a + * string or an array) then ignore duplicate + * events (saving them without the current valuye + * serves no purpose) + */ + if (!event->valque && event->npend>0u) { + UNLOCKEVQUE(ev_que) + return; + } + + /* + * add to task local event que + */ + + /* + * if an event is on the queue and one of + * {flowCtrlMode, not room for one more of each monitor attached} + * then replace the last event on the queue (for this monitor) + */ + rngSpace = ringSpace ( ev_que ); + if ( event->npend>0u && + (ev_que->evUser->flowCtrlMode || rngSpace<=EVENTSPERQUE) ) { + /* + * replace last event if no space is left + */ + pLog = event->pLastLog; + event->nreplace++; + /* + * the event task has already been notified about + * this so we dont need to post the semaphore + */ + firstEventFlag = 0; + } + /* + * Otherwise, the current entry must be available. + * Fill it in and advance the ring buffer. + */ + else { + assert ( ev_que->evque[ev_que->putix] == EVENTQEMPTY ); + pLog = &ev_que->valque[ev_que->putix]; + ev_que->evque[ev_que->putix] = event; + if (event->npend>0u) { + ev_que->nDuplicates++; + } + event->npend++; + /* + * if the ring buffer was empty before + * adding this event + */ + if (rngSpace==EVENTQUESIZE) { + firstEventFlag = 1; + } + else { + firstEventFlag = 0; + } + ev_que->putix = RNGINC ( ev_que->putix ); + } + + if (pLog && event->valque) { + struct dbCommon *precord = event->paddr->precord; + pLog->stat = precord->stat; + pLog->sevr = precord->sevr; + pLog->time = precord->time; + + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy( (char *)&pLog->field, + (char *)event->paddr->pfield, + event->paddr->field_size); + + event->pLastLog = pLog; + } + + UNLOCKEVQUE(ev_que) + + /* + * its more efficent to notify the event handler + * only after the event is ready and the lock + * is off in case it runs at a higher priority + * than the caller here. + */ + if (firstEventFlag) { + /* + * notify the event handler + */ + epicsEventSignal(ev_que->evUser->ppendsem); + } +} + +/* + * DB_POST_EVENTS() + * + * NOTE: This assumes that the db scan lock is already applied + * + */ +int epicsShareAPI db_post_events( +void *pRecord, +void *pField, +unsigned int caEventMask +) +{ + struct dbCommon *pdbc = (struct dbCommon *)pRecord; + struct evSubscrip *event; + + if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ + + LOCKREC(pdbc); + + for (event = (struct evSubscrip *) pdbc->mlis.node.next; + event; event = (struct evSubscrip *) event->node.next){ + + /* + * Only send event msg if they are waiting on the field which + * changed or pval==NULL and waiting on alarms and alarms changed + */ + if ( (event->paddr->pfield == (void *)pField || pField==NULL) && + (caEventMask & event->select)) { + + db_post_single_event_private (event); + } + } + + UNLOCKREC(pdbc); + return DB_EVENT_OK; + +} + +/* + * DB_POST_SINGLE_EVENT() + */ +void epicsShareAPI db_post_single_event (dbEventSubscription es) +{ + struct evSubscrip *event = (struct evSubscrip *) es; + struct dbCommon *precord = event->paddr->precord; + + dbScanLock (precord); + db_post_single_event_private (event); + dbScanUnlock (precord); +} + +/* + * EVENT_READ() + */ +static int event_read ( struct event_que *ev_que ) +{ + db_field_log *pfl; + void ( *user_sub ) ( void *user_arg, struct dbAddr *paddr, + int eventsRemaining, db_field_log *pfl ); + + /* + * evUser ring buffer must be locked for the multiple + * threads writing/reading it + */ + LOCKEVQUE ( ev_que ) + + /* + * if in flow control mode drain duplicates and then + * suspend processing events until flow control + * mode is over + */ + if ( ev_que->evUser->flowCtrlMode && ev_que->nDuplicates == 0u ) { + UNLOCKEVQUE(ev_que); + return DB_EVENT_OK; + } + + while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { + db_field_log fl = ev_que->valque[ev_que->getix]; + struct evSubscrip *event = ev_que->evque[ev_que->getix]; + + if ( event == &canceledEvent ) { + ev_que->evque[ev_que->getix] = EVENTQEMPTY; + ev_que->getix = RNGINC ( ev_que->getix ); + assert ( ev_que->nCanceled > 0 ); + ev_que->nCanceled--; + continue; + } + + /* + * Simple type values queued up for reliable interprocess + * communication. (for other types they get whatever happens + * to be there upon wakeup) + */ + if ( event->valque ) { + pfl = &fl; + } + else { + pfl = NULL; + } + + event_remove ( ev_que, ev_que->getix, EVENTQEMPTY ); + ev_que->getix = RNGINC ( ev_que->getix ); + + /* + * create a local copy of the call back parameters while + * we still have the lock + */ + user_sub = event->user_sub; + + /* + * Next event pointer can be used by event tasks to determine + * if more events are waiting in the queue + * + * Must remove the lock here so that we dont deadlock if + * this calls dbGetField() and blocks on the record lock, + * dbPutField() is in progress in another task, it has the + * record lock, and it is calling db_post_events() waiting + * for the event queue lock (which this thread now has). + */ + if ( user_sub ) { + /* + * This provides a way to test to see if an event is in use + * despite the fact that the event queue does not point to + * it. + */ + event->callBackInProgress = TRUE; + UNLOCKEVQUE ( ev_que ) + ( *user_sub ) ( event->user_arg, event->paddr, + ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl ); + LOCKEVQUE ( ev_que ) + + /* + * check to see if this event has been canceled each + * time that the callBackInProgress flag is set to false + * while we have the event queue lock, and post the flush + * complete sem if there are no longer any events on the + * queue + */ + if ( ev_que->evUser->pSuicideEvent == event ) { + ev_que->evUser->pSuicideEvent = NULL; + } + else { + if ( event->user_sub==NULL && event->npend==0u ) { + event->callBackInProgress = FALSE; + epicsEventSignal ( ev_que->evUser->pflush_sem ); + } + else { + event->callBackInProgress = FALSE; + } + } + } + } + + UNLOCKEVQUE ( ev_que ) + + return DB_EVENT_OK; +} + +/* + * EVENT_TASK() + */ +static void event_task (void *pParm) +{ + struct event_user *evUser = (struct event_user *) pParm; + struct event_que *ev_que; + + /* init hook */ + if (evUser->init_func) { + (*evUser->init_func)(evUser->init_func_arg); + } + + taskwdInsert ( epicsThreadGetIdSelf(), NULL, NULL ); + + do{ + void (*pExtraLaborSub) (void *); + void *pExtraLaborArg; + epicsEventMustWait(evUser->ppendsem); + + /* + * check to see if the caller has offloaded + * labor to this task + */ + epicsMutexMustLock ( evUser->lock ); + evUser->extraLaborBusy = TRUE; + if ( evUser->extra_labor && evUser->extralabor_sub ) { + evUser->extra_labor = FALSE; + pExtraLaborSub = evUser->extralabor_sub; + pExtraLaborArg = evUser->extralabor_arg; + } + else { + pExtraLaborSub = NULL; + pExtraLaborArg = NULL; + } + if ( pExtraLaborSub ) { + epicsMutexUnlock ( evUser->lock ); + (*pExtraLaborSub)(pExtraLaborArg); + epicsMutexMustLock ( evUser->lock ); + } + evUser->extraLaborBusy = FALSE; + epicsMutexUnlock ( evUser->lock ); + + for ( ev_que = &evUser->firstque; ev_que; + ev_que = ev_que->nextque ) { + event_read (ev_que); + } + + } while( ! evUser->pendexit ); + + epicsMutexDestroy(evUser->firstque.writelock); + + { + struct event_que *nextque; + + ev_que = evUser->firstque.nextque; + while(ev_que){ + nextque = ev_que->nextque; + epicsMutexDestroy(ev_que->writelock); + freeListFree(dbevEventQueueFreeList, ev_que); + ev_que = nextque; + } + } + + epicsEventDestroy(evUser->ppendsem); + epicsEventDestroy(evUser->pflush_sem); + epicsMutexDestroy(evUser->lock); + + freeListFree(dbevEventUserFreeList, evUser); + + taskwdRemove(epicsThreadGetIdSelf()); + + return; +} + +/* + * DB_START_EVENTS() + */ +int epicsShareAPI db_start_events ( + dbEventCtx ctx,const char *taskname, void (*init_func)(void *), + void *init_func_arg, unsigned osiPriority ) +{ + struct event_user *evUser = (struct event_user *) ctx; + + epicsMutexMustLock ( evUser->lock ); + + /* + * only one ca_pend_event thread may be + * started for each evUser + */ + if (evUser->taskid) { + epicsMutexUnlock ( evUser->lock ); + return DB_EVENT_OK; + } + + evUser->pendexit = FALSE; + evUser->init_func = init_func; + evUser->init_func_arg = init_func_arg; + if (!taskname) { + taskname = EVENT_PEND_NAME; + } + evUser->taskid = epicsThreadCreate ( + taskname, osiPriority, epicsThreadGetStackSize(epicsThreadStackMedium), + event_task, (void *)evUser); + if (!evUser->taskid) { + epicsMutexUnlock ( evUser->lock ); + return DB_EVENT_ERROR; + } + epicsMutexUnlock ( evUser->lock ); + return DB_EVENT_OK; +} + +/* + * db_event_change_priority() + */ +void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority ) +{ + struct event_user * evUser = ( struct event_user * ) ctx; + epicsThreadSetPriority ( evUser->taskid, epicsPriority ); +} + +/* + * db_event_flow_ctrl_mode_on() + */ +void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx) +{ + struct event_user *evUser = (struct event_user *) ctx; + + evUser->flowCtrlMode = TRUE; + /* + * notify the event handler task + */ + epicsEventSignal(evUser->ppendsem); +#ifdef DEBUG + printf("fc on %lu\n", tickGet()); +#endif +} + +/* + * db_event_flow_ctrl_mode_off() + */ +void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx) +{ + struct event_user *evUser = (struct event_user *) ctx; + + evUser->flowCtrlMode = FALSE; + /* + * notify the event handler task + */ + epicsEventSignal (evUser->ppendsem); +#ifdef DEBUG + printf("fc off %lu\n", tickGet()); +#endif +} + + diff --git a/src/db/dbEvent.h b/src/db/dbEvent.h new file mode 100644 index 000000000..6aa6426be --- /dev/null +++ b/src/db/dbEvent.h @@ -0,0 +1,83 @@ +/*************************************************************************\ +* 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 + * Date: 030393 + */ + +#ifndef INCLdbEventh +#define INCLdbEventh + +#ifdef epicsExportSharedSymbols +# undef epicsExportSharedSymbols +# define INCLdbEventhExporting +#endif + +#include "epicsThread.h" + +#ifdef INCLdbEventhExporting +# define epicsExportSharedSymbols +#endif + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct dbAddr; +struct db_field_log; + +epicsShareFunc int epicsShareAPI db_event_list ( + const char *name, unsigned level); +epicsShareFunc int epicsShareAPI dbel ( + const char *name, unsigned level); +epicsShareFunc int epicsShareAPI db_post_events ( + void *pRecord, void *pField, unsigned caEventMask ); + +typedef void * dbEventCtx; + +typedef void EXTRALABORFUNC (void *extralabor_arg); +epicsShareFunc dbEventCtx epicsShareAPI db_init_events (void); +epicsShareFunc int epicsShareAPI db_start_events ( + dbEventCtx ctx, const char *taskname, void (*init_func)(void *), + void *init_func_arg, unsigned osiPriority ); +epicsShareFunc void epicsShareAPI db_close_events (dbEventCtx ctx); +epicsShareFunc void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx); +epicsShareFunc void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx); +epicsShareFunc int epicsShareAPI db_add_extra_labor_event ( + dbEventCtx ctx, EXTRALABORFUNC *func, void *arg); +epicsShareFunc void epicsShareAPI db_flush_extra_labor_event (dbEventCtx); +epicsShareFunc int epicsShareAPI db_post_extra_labor (dbEventCtx ctx); +epicsShareFunc void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority ); + +typedef void EVENTFUNC (void *user_arg, struct dbAddr *paddr, + int eventsRemaining, struct db_field_log *pfl); + +typedef void * dbEventSubscription; +epicsShareFunc dbEventSubscription epicsShareAPI db_add_event ( + dbEventCtx ctx, struct dbAddr *paddr, + EVENTFUNC *user_sub, void *user_arg, unsigned select); +epicsShareFunc void epicsShareAPI db_cancel_event (dbEventSubscription es); +epicsShareFunc void epicsShareAPI db_post_single_event (dbEventSubscription es); +epicsShareFunc void epicsShareAPI db_event_enable (dbEventSubscription es); +epicsShareFunc void epicsShareAPI db_event_disable (dbEventSubscription es); + +#define DB_EVENT_OK 0 +#define DB_EVENT_ERROR (-1) + +#ifdef __cplusplus +} +#endif + +#endif /*INCLdbEventh*/ + diff --git a/src/db/dbFastLinkConv.c b/src/db/dbFastLinkConv.c new file mode 100644 index 000000000..2f26e2b53 --- /dev/null +++ b/src/db/dbFastLinkConv.c @@ -0,0 +1,1175 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbFastLinkConv.c */ +/* base/src/db Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Matthew Needes + * Date: 12-9-93 + */ +#include +#include +#include +#include +#include +#include + +#include "epicsConvert.h" +#include "dbDefs.h" +#include "errlog.h" +#include "cvtFast.h" +#include "alarm.h" +#include "dbBase.h" +#include "link.h" +#include "dbFldTypes.h" +#include "dbStaticLib.h" +#include "dbCommon.h" +#include "dbFldTypes.h" +#include "errMdef.h" +#include "recSup.h" +#include "special.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbAccessDefs.h" +#include "recGbl.h" +#include "dbConvertFast.h" + + +/* + * In the following functions: + * + * cvt_y_z(ARGS) + * + * converts from type y to type z. If + * type y and z are the same, it merely copies. + * + * where st - string + * c - epicsInt8 + * uc - epicsUInt8 + * s - epicsInt16 + * us - epicsUInt16 + * l - epicsInt32 + * ul - epicsUInt32 + * f - epicsFloat32 + * d - epicsFloat64 + * e - enum + * + * These functions are _single_ value functions, + * i.e.: do not deal with array types. + */ + +/* + * A DB_LINK that is not initialized with recGblInitFastXXXLink() + * will have this conversion. + */ + +/* Convert String to String */ +static long cvt_st_st( + char *from, + char *to, + const dbAddr *paddr) +{ + size_t size; + + if (paddr && paddr->field_size < MAX_STRING_SIZE) { + size = paddr->field_size - 1; + } else { + size = MAX_STRING_SIZE - 1; + } + strncpy(to, from, size); + to[size] = 0; + return 0; +} + +/* Convert String to Char */ +static long cvt_st_c( + char *from, + epicsInt8 *to, + const dbAddr *paddr) +{ + char *end; + long value; + + if (*from == 0) { + *to = 0; + return 0; + } + value = strtol(from, &end, 10); + if (end > from) { + *to = (epicsInt8) value; + return 0; + } + return -1; +} + +/* Convert String to Unsigned Char */ +static long cvt_st_uc( + char *from, + epicsUInt8 *to, + const dbAddr *paddr) +{ + char *end; + unsigned long value; + + if (*from == 0) { + *to = 0; + return 0; + } + value = strtoul(from, &end, 10); + if (end > from) { + *to = (epicsUInt8) value; + return 0; + } + return -1; +} + +/* Convert String to Short */ +static long cvt_st_s( + char *from, + epicsInt16 *to, + const dbAddr *paddr) +{ + char *end; + long value; + + if (*from == 0) { + *to = 0; + return 0; + } + value = strtol(from, &end, 10); + if (end > from) { + *to = (epicsInt16) value; + return 0; + } + return -1; +} + +/* Convert String to Unsigned Short */ +static long cvt_st_us( + char *from, + epicsUInt16 *to, + const dbAddr *paddr) +{ + char *end; + unsigned long value; + + if (*from == 0) { + *to = 0; + return 0; + } + value = strtoul(from, &end, 10); + if (end > from) { + *to = (epicsUInt16) value; + return 0; + } + return -1; +} + +/* Convert String to Long */ +static long cvt_st_l( + char *from, + epicsInt32 *to, + const dbAddr *paddr) + { + char *end; + long value; + + if (*from == 0) { + *to = 0; + return 0; + } + value = strtol(from, &end, 10); + if (end > from) { + *to = (epicsInt32) value; + return 0; + } + return -1; + } + +/* Convert String to Unsigned Long */ +static long cvt_st_ul( + char *from, + epicsUInt32 *to, + const dbAddr *paddr) + { + double value; + + if (*from == 0) { + *to = 0; + return 0; + } + /*Convert via double so that numbers like 1.0e3 convert properly*/ + /*Problem was old database access said to get unsigned long as double*/ + if (epicsScanDouble(from, &value) == 1) { + *to = (epicsUInt32)value; + return 0; + } + return -1; + } + +/* Convert String to Float */ +static long cvt_st_f( + char *from, + epicsFloat32 *to, + const dbAddr *paddr) + { + float value; + + if (*from == 0) { + *to = 0; + return 0; + } + if (epicsScanFloat(from, &value) == 1) { + *to = value; + return 0; + } + return -1; + } + +/* Convert String to Double */ +static long cvt_st_d( + char *from, + epicsFloat64 *to, + const dbAddr *paddr) + { + double value; + + if (*from == 0) { + *to = 0.0; + return 0; + } + if (epicsScanDouble(from, &value) == 1) { + *to = value; + return 0; + } + return -1; + } + +/* Convert String to Enumerated */ +static long cvt_st_e( + char *from, + epicsEnum16 *to, + const dbAddr *paddr) + { + struct rset *prset = 0; + long status; + epicsEnum16 *pfield= (epicsEnum16*)(paddr->pfield); + unsigned int nchoices,ind; + int nargs,nchars; + struct dbr_enumStrs enumStrs; + + if(paddr && (prset=dbGetRset(paddr)) + && (prset->put_enum_str)) { + status = (*prset->put_enum_str)(paddr,from); + if(!status) return(0); + if(prset->get_enum_strs) { + status = (*prset->get_enum_strs)(paddr,&enumStrs); + if(!status) { + nchoices = enumStrs.no_str; + nargs = sscanf(from," %u %n",&ind,&nchars); + if(nargs==1 && nchars==strlen(from) && indprecord,from); + } + return(status); + } + +/* Convert String to Menu */ +static long cvt_st_menu( + char *from, + epicsEnum16 *to, + const dbAddr *paddr) +{ + dbFldDes *pdbFldDes = paddr->pfldDes; + dbMenu *pdbMenu = (dbMenu *)pdbFldDes->ftPvt; + char **papChoiceValue; + char *pchoice; + unsigned int nChoice,ind; + int nargs,nchars; + + if( pdbMenu && (papChoiceValue = pdbMenu->papChoiceValue)) { + nChoice = pdbMenu->nChoice; + for(ind=0; indpfldDes; + dbDeviceMenu *pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; + char **papChoice; + char *pchoice; + unsigned int nChoice,ind; + int nargs,nchars; + + if( pdbDeviceMenu && (papChoice = pdbDeviceMenu->papChoice)) { + nChoice = pdbDeviceMenu->nChoice; + for(ind=0; indget_precision) + status = (*prset->get_precision)(paddr, &precision); + cvtFloatToString(*from, to, precision); + return(status); + } + +/* Convert Float to Char */ +static long cvt_f_c( + epicsFloat32 *from, + epicsInt8 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Unsigned Char */ +static long cvt_f_uc( + epicsFloat32 *from, + epicsUInt8 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Short */ +static long cvt_f_s( + epicsFloat32 *from, + epicsInt16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Unsigned Short */ +static long cvt_f_us( + epicsFloat32 *from, + epicsUInt16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Long */ +static long cvt_f_l( + epicsFloat32 *from, + epicsInt32 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Unsigned Long */ +static long cvt_f_ul( + epicsFloat32 *from, + epicsUInt32 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Float */ +static long cvt_f_f( + epicsFloat32 *from, + epicsFloat32 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Double */ +static long cvt_f_d( + epicsFloat32 *from, + epicsFloat64 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Float to Enumerated */ +static long cvt_f_e( + epicsFloat32 *from, + epicsEnum16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Double to String */ +static long cvt_d_st( + epicsFloat64 *from, + char *to, + const dbAddr *paddr) + { + struct rset *prset = 0; + long status = 0; + long precision = 6; + + if(paddr) prset = dbGetRset(paddr); + + if (prset && prset->get_precision) + status = (*prset->get_precision)(paddr, &precision); + cvtDoubleToString(*from, to, precision); + return(status); + } + +/* Convert Double to Char */ +static long cvt_d_c( + epicsFloat64 *from, + epicsInt8 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Double to Unsigned Char */ +static long cvt_d_uc( + epicsFloat64 *from, + epicsUInt8 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Double to Short */ +static long cvt_d_s( + epicsFloat64 *from, + epicsInt16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Double to Unsigned Short */ +static long cvt_d_us( + epicsFloat64 *from, + epicsUInt16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Double to Long */ +static long cvt_d_l( + epicsFloat64 *from, + epicsInt32 *to, + const dbAddr *paddr) + { *to=(epicsInt32)*from; return(0); } + +/* Convert Double to Unsigned Long */ +static long cvt_d_ul( + epicsFloat64 *from, + epicsUInt32 *to, + const dbAddr *paddr) + { *to=(epicsUInt32)*from; return(0); } + +/* Convert Double to Float */ +static long cvt_d_f( + epicsFloat64 *from, + epicsFloat32 *to, + const dbAddr *paddr) +{ *to = epicsConvertDoubleToFloat(*from); return 0;} + +/* Convert Double to Double */ +static long cvt_d_d( + epicsFloat64 *from, + epicsFloat64 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Double to Enumerated */ +static long cvt_d_e( + epicsFloat64 *from, + epicsEnum16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Char */ +static long cvt_e_c( + epicsEnum16 *from, + epicsInt8 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Unsigned Char */ +static long cvt_e_uc( + epicsEnum16 *from, + epicsUInt8 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Short */ +static long cvt_e_s( + epicsEnum16 *from, + epicsInt16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Unsigned Short */ +static long cvt_e_us( + epicsEnum16 *from, + epicsUInt16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Long */ +static long cvt_e_l( + epicsEnum16 *from, + epicsInt32 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Unsigned Long */ +static long cvt_e_ul( + epicsEnum16 *from, + epicsUInt32 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Float */ +static long cvt_e_f( + epicsEnum16 *from, + epicsFloat32 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Double */ +static long cvt_e_d( + epicsEnum16 *from, + epicsFloat64 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Enumerated to Enumerated */ +static long cvt_e_e( + epicsEnum16 *from, + epicsEnum16 *to, + const dbAddr *paddr) + { *to=*from; return(0); } + +/* Convert Choices And Enumerated Types To String ... */ + +/* Get Enumerated to String */ +static long cvt_e_st_get( + epicsEnum16 *from, + char *to, + const dbAddr *paddr) + { + struct rset *prset = 0; + long status; + + if(paddr) prset = dbGetRset(paddr); + + if (prset && prset->get_enum_str) + return (*prset->get_enum_str)(paddr, to); + + status = S_db_noRSET; + recGblRecSupError(status, paddr, "dbGetField", "get_enum_str"); + + return(S_db_badDbrtype); + } + +/* Put Enumerated to String */ +static long cvt_e_st_put( + epicsEnum16 *from, + char *to, + const dbAddr *paddr) + { cvtUshortToString(*from, to); return(0); } + +/* Get Menu to String */ +static long cvt_menu_st( + epicsEnum16 *from, + char *to, + const dbAddr *paddr) + { + dbFldDes *pdbFldDes; + dbMenu *pdbMenu; + char **papChoiceValue; + char *pchoice; + + if(! paddr + || !(pdbFldDes = paddr->pfldDes) + || !(pdbMenu = (dbMenu *)pdbFldDes->ftPvt) + || *from>=pdbMenu->nChoice + || !(papChoiceValue = pdbMenu->papChoiceValue) + || !(pchoice=papChoiceValue[*from])) { + recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_menu_st)"); + return(S_db_badChoice); + } + strncpy(to,pchoice,MAX_STRING_SIZE); + return(0); + } + + +/* Get Device to String */ +static long cvt_device_st( + epicsEnum16 *from, + char *to, + const dbAddr *paddr) + { + dbFldDes *pdbFldDes; + dbDeviceMenu *pdbDeviceMenu; + char **papChoice; + char *pchoice; + + if(!paddr + || !(pdbFldDes = paddr->pfldDes) + || !(pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt) + || *from>=pdbDeviceMenu->nChoice + || !(papChoice= pdbDeviceMenu->papChoice) + || !(pchoice=papChoice[*from])) { + recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_device_st)"); + return(S_db_badChoice); + } + strncpy(to,pchoice,MAX_STRING_SIZE); + return(0); + } + +/* + * Get conversion routine lookup table + * + * Converts type X to ... + * + * DBR_STRING, DBR_CHR, DBR_UCHAR, DBR_SHORT, DBR_USHORT, + * DBR_LONG, DBR_ULONG, DBR_FLOAT, DBR_DOUBLE, DBR_ENUM + * + * NULL implies the conversion is not supported. + */ + +epicsShareDef long (*dbFastGetConvertRoutine[DBF_DEVICE+1][DBR_ENUM+1])() = { + + /* Convert DBF_STRING to ... */ +{ cvt_st_st, cvt_st_c, cvt_st_uc, cvt_st_s, cvt_st_us, cvt_st_l, cvt_st_ul, cvt_st_f, cvt_st_d, cvt_st_e }, + + /* Convert DBF_CHAR to ... */ +{ cvt_c_st, cvt_c_c, cvt_c_uc, cvt_c_s, cvt_c_us, cvt_c_l, cvt_c_ul, cvt_c_f, cvt_c_d, cvt_c_e }, + + /* Convert DBF_UCHAR to ... */ +{ cvt_uc_st, cvt_uc_c, cvt_uc_uc, cvt_uc_s, cvt_uc_us, cvt_uc_l, cvt_uc_ul, cvt_uc_f, cvt_uc_d, cvt_uc_e }, + + /* Convert DBF_SHORT to ... */ +{ cvt_s_st, cvt_s_c, cvt_s_uc, cvt_s_s, cvt_s_us, cvt_s_l, cvt_s_ul, cvt_s_f, cvt_s_d, cvt_s_e }, + + /* Convert DBF_USHORT to ... */ +{ cvt_us_st, cvt_us_c, cvt_us_uc, cvt_us_s, cvt_us_us, cvt_us_l, cvt_us_ul, cvt_us_f, cvt_us_d, cvt_us_e }, + + /* Convert DBF_LONG to ... */ +{ cvt_l_st, cvt_l_c, cvt_l_uc, cvt_l_s, cvt_l_us, cvt_l_l, cvt_l_ul, cvt_l_f, cvt_l_d, cvt_l_e }, + + /* Convert DBF_ULONG to ... */ +{ cvt_ul_st, cvt_ul_c, cvt_ul_uc, cvt_ul_s, cvt_ul_us, cvt_ul_l, cvt_ul_ul, cvt_ul_f, cvt_ul_d, cvt_ul_e }, + + /* Convert DBF_FLOAT to ... */ +{ cvt_f_st, cvt_f_c, cvt_f_uc, cvt_f_s, cvt_f_us, cvt_f_l, cvt_f_ul, cvt_f_f, cvt_f_d, cvt_f_e }, + + /* Convert DBF_DOUBLE to ... */ +{ cvt_d_st, cvt_d_c, cvt_d_uc, cvt_d_s, cvt_d_us, cvt_d_l, cvt_d_ul, cvt_d_f, cvt_d_d, cvt_d_e }, + + /* Convert DBF_ENUM to ... */ +{ cvt_e_st_get, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_f, cvt_e_d, cvt_e_e }, + + /* Convert DBF_MENU to ... */ +{ cvt_menu_st, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_f, cvt_e_d, cvt_e_e }, + + /* Convert DBF_DEVICE to ... */ +{ cvt_device_st, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_f, cvt_e_d, cvt_e_e } }; + +/* + * Put conversion routine lookup table + * + * Converts type X to ... + * + * DBF_STRING DBF_CHAR DBF_UCHAR DBF_SHORT DBF_USHORT + * DBF_LONG DBF_ULONG DBF_FLOAT DBF_DOUBLE DBF_ENUM + * DBF_MENU DBF_DEVICE + * + * NULL implies the conversion is not supported. + */ + +epicsShareDef long (*dbFastPutConvertRoutine[DBR_ENUM+1][DBF_DEVICE+1])() = { + + /* Convert DBR_STRING to ... */ +{ cvt_st_st, cvt_st_c, cvt_st_uc, cvt_st_s, cvt_st_us, cvt_st_l, cvt_st_ul, cvt_st_f, cvt_st_d, cvt_st_e, cvt_st_menu, cvt_st_device}, + + /* Convert DBR_CHAR to ... */ +{ cvt_c_st, cvt_c_c, cvt_c_uc, cvt_c_s, cvt_c_us, cvt_c_l, cvt_c_ul, cvt_c_f, cvt_c_d, cvt_c_e, cvt_c_e, cvt_c_e}, + + /* Convert DBR_UCHAR to ... */ +{ cvt_uc_st, cvt_uc_c, cvt_uc_uc, cvt_uc_s, cvt_uc_us, cvt_uc_l, cvt_uc_ul, cvt_uc_f, cvt_uc_d, cvt_uc_e, cvt_uc_e, cvt_uc_e}, + + /* Convert DBR_SHORT to ... */ +{ cvt_s_st, cvt_s_c, cvt_s_uc, cvt_s_s, cvt_s_us, cvt_s_l, cvt_s_ul, cvt_s_f, cvt_s_d, cvt_s_e, cvt_s_e, cvt_s_e}, + + /* Convert DBR_USHORT to ... */ +{ cvt_us_st, cvt_us_c, cvt_us_uc, cvt_us_s, cvt_us_us, cvt_us_l, cvt_us_ul, cvt_us_f, cvt_us_d, cvt_us_e, cvt_us_e, cvt_us_e}, + + /* Convert DBR_LONG to ... */ +{ cvt_l_st, cvt_l_c, cvt_l_uc, cvt_l_s, cvt_l_us, cvt_l_l, cvt_l_ul, cvt_l_f, cvt_l_d, cvt_l_e, cvt_l_e, cvt_l_e}, + + /* Convert DBR_ULONG to ... */ +{ cvt_ul_st, cvt_ul_c, cvt_ul_uc, cvt_ul_s, cvt_ul_us, cvt_ul_l, cvt_ul_ul, cvt_ul_f, cvt_ul_d, cvt_ul_e, cvt_ul_e, cvt_ul_e}, + + /* Convert DBR_FLOAT to ... */ +{ cvt_f_st, cvt_f_c, cvt_f_uc, cvt_f_s, cvt_f_us, cvt_f_l, cvt_f_ul, cvt_f_f, cvt_f_d, cvt_f_e, cvt_f_e, cvt_f_e}, + + /* Convert DBR_DOUBLE to ... */ +{ cvt_d_st, cvt_d_c, cvt_d_uc, cvt_d_s, cvt_d_us, cvt_d_l, cvt_d_ul, cvt_d_f, cvt_d_d, cvt_d_e, cvt_d_e, cvt_d_e}, + + /* Convert DBR_ENUM to ... */ +{ cvt_e_st_put, cvt_e_c, cvt_e_uc, cvt_e_s, cvt_e_us, cvt_e_l, cvt_e_ul, cvt_e_f, cvt_e_d, cvt_e_e, cvt_e_e, cvt_e_e} }; + diff --git a/src/db/dbIoc.rc b/src/db/dbIoc.rc new file mode 100755 index 000000000..6b261fff3 --- /dev/null +++ b/src/db/dbIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Database Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Database Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "dbIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "dbIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/db/dbIocRegister.c b/src/db/dbIocRegister.c new file mode 100644 index 000000000..a82337f03 --- /dev/null +++ b/src/db/dbIocRegister.c @@ -0,0 +1,334 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "iocsh.h" + +#define epicsExportSharedSymbols +#include "dbBkpt.h" +#include "dbAccess.h" +#include "dbCaTest.h" +#include "dbEvent.h" +#include "dbTest.h" +#include "db_test.h" +#include "dbLock.h" +#include "dbScan.h" +#include "dbNotify.h" +#include "callback.h" +#include "dbIocRegister.h" + +/* dbLoadDatabase */ +static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString}; +static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgString}; +static const iocshArg dbLoadDatabaseArg2 = { "substitutions",iocshArgString}; +static const iocshArg * const dbLoadDatabaseArgs[3] = +{ + &dbLoadDatabaseArg0,&dbLoadDatabaseArg1,&dbLoadDatabaseArg2 +}; +static const iocshFuncDef dbLoadDatabaseFuncDef = + {"dbLoadDatabase",3,dbLoadDatabaseArgs}; +static void dbLoadDatabaseCallFunc(const iocshArgBuf *args) +{ + dbLoadDatabase(args[0].sval,args[1].sval,args[2].sval); +} + +/* dbLoadRecords */ +static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgString}; +static const iocshArg dbLoadRecordsArg1 = { "substitutions",iocshArgString}; +static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoadRecordsArg1}; +static const iocshFuncDef dbLoadRecordsFuncDef = {"dbLoadRecords",2,dbLoadRecordsArgs}; +static void dbLoadRecordsCallFunc(const iocshArgBuf *args) +{ + dbLoadRecords(args[0].sval,args[1].sval); +} + +/* dbb */ +static const iocshArg dbbArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbbArgs[1] = {&dbbArg0}; +static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs}; +static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);} + +/* dbd */ +static const iocshArg dbdArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbdArgs[1] = {&dbdArg0}; +static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs}; +static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);} + +/* dbc */ +static const iocshArg dbcArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbcArgs[1] = {&dbcArg0}; +static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs}; +static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);} + +/* dbs */ +static const iocshArg dbsArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbsArgs[1] = {&dbsArg0}; +static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs}; +static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);} + +/* dbstat */ +static const iocshFuncDef dbstatFuncDef = {"dbstat",0}; +static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();} + +/* dbp */ +static const iocshArg dbpArg0 = { "record name",iocshArgString}; +static const iocshArg dbpArg1 = { "interest level",iocshArgInt}; +static const iocshArg * const dbpArgs[2] = {&dbpArg0,&dbpArg1}; +static const iocshFuncDef dbpFuncDef = {"dbp",2,dbpArgs}; +static void dbpCallFunc(const iocshArgBuf *args) +{ dbp(args[0].sval,args[1].ival);} + +/* dbap */ +static const iocshArg dbapArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbapArgs[1] = {&dbapArg0}; +static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs}; +static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);} + +/* dbcar */ +static const iocshArg dbcarArg0 = { "record name",iocshArgString}; +static const iocshArg dbcarArg1 = { "level",iocshArgInt}; +static const iocshArg * const dbcarArgs[2] = {&dbcarArg0,&dbcarArg1}; +static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs}; +static void dbcarCallFunc(const iocshArgBuf *args) +{ + dbcar(args[0].sval,args[1].ival); +} + +/* dbel */ +static const iocshArg dbelArg0 = { "record name",iocshArgString}; +static const iocshArg dbelArg1 = { "level",iocshArgInt}; +static const iocshArg * const dbelArgs[2] = {&dbelArg0,&dbelArg1}; +static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs}; +static void dbelCallFunc(const iocshArgBuf *args) +{ + dbel(args[0].sval, args[1].ival); +} + +/* dba */ +static const iocshArg dbaArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbaArgs[1] = {&dbaArg0}; +static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs}; +static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);} + +/* dbl */ +static const iocshArg dblArg0 = { "record type",iocshArgString}; +static const iocshArg dblArg1 = { "fields",iocshArgString}; +static const iocshArg * const dblArgs[] = {&dblArg0,&dblArg1}; +static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs}; +static void dblCallFunc(const iocshArgBuf *args) +{ + dbl(args[0].sval,args[1].sval); +} + +/* dbnr */ +static const iocshArg dbnrArg0 = { "verbose",iocshArgInt}; +static const iocshArg * const dbnrArgs[1] = {&dbnrArg0}; +static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs}; +static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);} + +/* dbla */ +static const iocshArg dblaArg0 = { "pattern",iocshArgString}; +static const iocshArg * const dblaArgs[1] = {&dblaArg0}; +static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs}; +static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);} + +/* dbgrep */ +static const iocshArg dbgrepArg0 = { "pattern",iocshArgString}; +static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0}; +static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs}; +static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);} + +/* dbgf */ +static const iocshArg dbgfArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbgfArgs[1] = {&dbgfArg0}; +static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs}; +static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);} + +/* dbpf */ +static const iocshArg dbpfArg0 = { "record name",iocshArgString}; +static const iocshArg dbpfArg1 = { "value",iocshArgString}; +static const iocshArg * const dbpfArgs[2] = {&dbpfArg0,&dbpfArg1}; +static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs}; +static void dbpfCallFunc(const iocshArgBuf *args) +{ dbpf(args[0].sval,args[1].sval);} + +/* dbpr */ +static const iocshArg dbprArg0 = { "record name",iocshArgString}; +static const iocshArg dbprArg1 = { "interest level",iocshArgInt}; +static const iocshArg * const dbprArgs[2] = {&dbprArg0,&dbprArg1}; +static const iocshFuncDef dbprFuncDef = {"dbpr",2,dbprArgs}; +static void dbprCallFunc(const iocshArgBuf *args) +{ dbpr(args[0].sval,args[1].ival);} + +/* dbtr */ +static const iocshArg dbtrArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbtrArgs[1] = {&dbtrArg0}; +static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs}; +static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);} + +/* dbtgf */ +static const iocshArg dbtgfArg0 = { "record name",iocshArgString}; +static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0}; +static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs}; +static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);} + +/* dbtpf */ +static const iocshArg dbtpfArg0 = { "record name",iocshArgString}; +static const iocshArg dbtpfArg1 = { "value",iocshArgString}; +static const iocshArg * const dbtpfArgs[2] = {&dbtpfArg0,&dbtpfArg1}; +static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs}; +static void dbtpfCallFunc(const iocshArgBuf *args) +{ dbtpf(args[0].sval,args[1].sval);} + +/* dbior */ +static const iocshArg dbiorArg0 = { "driver name",iocshArgString}; +static const iocshArg dbiorArg1 = { "interest level",iocshArgInt}; +static const iocshArg * const dbiorArgs[] = {&dbiorArg0,&dbiorArg1}; +static const iocshFuncDef dbiorFuncDef = {"dbior",2,dbiorArgs}; +static void dbiorCallFunc(const iocshArgBuf *args) +{ dbior(args[0].sval,args[1].ival);} + +/* dbhcr */ +static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0}; +static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();} + +/* gft */ +static const iocshArg gftArg0 = { "record name",iocshArgString}; +static const iocshArg * const gftArgs[1] = {&gftArg0}; +static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs}; +static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);} + +/* pft */ +static const iocshArg pftArg0 = { "record name",iocshArgString}; +static const iocshArg pftArg1 = { "value",iocshArgString}; +static const iocshArg * const pftArgs[2] = {&pftArg0,&pftArg1}; +static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs}; +static void pftCallFunc(const iocshArgBuf *args) +{ pft(args[0].sval,args[1].sval);} + +/* dbtpn */ +static const iocshArg dbtpnArg0 = { "record name",iocshArgString}; +static const iocshArg dbtpnArg1 = { "value",iocshArgString}; +static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1}; +static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs}; +static void dbtpnCallFunc(const iocshArgBuf *args) +{ dbtpn(args[0].sval,args[1].sval);} + +/* dbNotifyDump */ +static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0}; +static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();} + +/* tpn */ +static const iocshArg tpnArg0 = { "record name",iocshArgString}; +static const iocshArg tpnArg1 = { "value",iocshArgString}; +static const iocshArg * const tpnArgs[2] = {&tpnArg0,&tpnArg1}; +static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs}; +static void tpnCallFunc(const iocshArgBuf *args) +{ tpn(args[0].sval,args[1].sval);} + +/* dblsr */ +static const iocshArg dblsrArg0 = { "record name",iocshArgString}; +static const iocshArg dblsrArg1 = { "interest level",iocshArgInt}; +static const iocshArg * const dblsrArgs[2] = {&dblsrArg0,&dblsrArg1}; +static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs}; +static void dblsrCallFunc(const iocshArgBuf *args) +{ dblsr(args[0].sval,args[1].ival);} + +/* dbLockShowLocked */ +static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt}; +static const iocshArg * const dbLockShowLockedArgs[1] = {&dbLockShowLockedArg0}; +static const iocshFuncDef dbLockShowLockedFuncDef = + {"dbLockShowLocked",1,dbLockShowLockedArgs}; +static void dbLockShowLockedCallFunc(const iocshArgBuf *args) +{ dbLockShowLocked(args[0].ival);} + +/* scanOnceSetQueueSize */ +static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt}; +static const iocshArg * const scanOnceSetQueueSizeArgs[1] = + {&scanOnceSetQueueSizeArg0}; +static const iocshFuncDef scanOnceSetQueueSizeFuncDef = + {"scanOnceSetQueueSize",1,scanOnceSetQueueSizeArgs}; +static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) +{ + scanOnceSetQueueSize(args[0].ival); +} + +/* scanppl */ +static const iocshArg scanpplArg0 = { "rate",iocshArgDouble}; +static const iocshArg * const scanpplArgs[1] = {&scanpplArg0}; +static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs}; +static void scanpplCallFunc(const iocshArgBuf *args) +{ scanppl(args[0].dval);} + +/* scanpel */ +static const iocshArg scanpelArg0 = { "event number",iocshArgInt}; +static const iocshArg * const scanpelArgs[1] = {&scanpelArg0}; +static const iocshFuncDef scanpelFuncDef = {"scanpel",1,scanpelArgs}; +static void scanpelCallFunc(const iocshArgBuf *args) +{ scanpel(args[0].ival);} + +/* scanpiol */ +static const iocshFuncDef scanpiolFuncDef = {"scanpiol",0}; +static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();} + +/* callbackSetQueueSize */ +static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt}; +static const iocshArg * const callbackSetQueueSizeArgs[1] = + {&callbackSetQueueSizeArg0}; +static const iocshFuncDef callbackSetQueueSizeFuncDef = + {"callbackSetQueueSize",1,callbackSetQueueSizeArgs}; +static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) +{ + callbackSetQueueSize(args[0].ival); +} + + +void epicsShareAPI dbIocRegister(void) +{ + iocshRegister(&dbbFuncDef,dbbCallFunc); + iocshRegister(&dbdFuncDef,dbdCallFunc); + iocshRegister(&dbcFuncDef,dbcCallFunc); + iocshRegister(&dbsFuncDef,dbsCallFunc); + iocshRegister(&dbstatFuncDef,dbstatCallFunc); + iocshRegister(&dbpFuncDef,dbpCallFunc); + iocshRegister(&dbapFuncDef,dbapCallFunc); + + iocshRegister(&dbcarFuncDef,dbcarCallFunc); + iocshRegister(&dbelFuncDef,dbelCallFunc); + + iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc); + iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc); + + iocshRegister(&dbaFuncDef,dbaCallFunc); + iocshRegister(&dblFuncDef,dblCallFunc); + iocshRegister(&dbnrFuncDef,dbnrCallFunc); + iocshRegister(&dblaFuncDef,dblaCallFunc); + iocshRegister(&dbgrepFuncDef,dbgrepCallFunc); + iocshRegister(&dbgfFuncDef,dbgfCallFunc); + iocshRegister(&dbpfFuncDef,dbpfCallFunc); + iocshRegister(&dbprFuncDef,dbprCallFunc); + iocshRegister(&dbtrFuncDef,dbtrCallFunc); + iocshRegister(&dbtgfFuncDef,dbtgfCallFunc); + iocshRegister(&dbtpfFuncDef,dbtpfCallFunc); + iocshRegister(&dbiorFuncDef,dbiorCallFunc); + iocshRegister(&dbhcrFuncDef,dbhcrCallFunc); + iocshRegister(&gftFuncDef,gftCallFunc); + iocshRegister(&pftFuncDef,pftCallFunc); + iocshRegister(&dbtpnFuncDef,dbtpnCallFunc); + iocshRegister(&dbNotifyDumpFuncDef,dbNotifyDumpCallFunc); + iocshRegister(&tpnFuncDef,tpnCallFunc); + iocshRegister(&dblsrFuncDef,dblsrCallFunc); + iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc); + + iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc); + iocshRegister(&scanpplFuncDef,scanpplCallFunc); + iocshRegister(&scanpelFuncDef,scanpelCallFunc); + iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); + + iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); +} diff --git a/src/db/dbIocRegister.h b/src/db/dbIocRegister.h new file mode 100644 index 000000000..31e72227a --- /dev/null +++ b/src/db/dbIocRegister.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_dbIocRegister_H +#define INC_dbIocRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI dbIocRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_dbIocRegister_H */ diff --git a/src/db/dbLock.c b/src/db/dbLock.c new file mode 100644 index 000000000..e1cea1581 --- /dev/null +++ b/src/db/dbLock.c @@ -0,0 +1,627 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbLock.c */ +/* Author: Marty Kraimer Date: 12MAR96 */ + +/************** DISCUSSION OF DYNAMIC LINK MODIFICATION ********************** + +A routine attempting to modify a link must do the following: + +Call dbLockSetGblLock before modifying any link and dbLockSetGblUnlock after. +Call dbLockSetRecordLock for any record referenced during change. It MUST NOT UNLOCK +Call dbLockSetSplit before changing any link that is originally a DB_LINK +Call dbLockSetMerge if changed link becomes a DB_LINK. + +Since the purpose of lock sets is to prevent multiple thread from simultaneously +accessing records in set, dynamically changing lock sets presents a problem. + +Three problems arise: + +1) Two threads simultaneoulsy trying to change lock sets +2) Another thread has successfully issued a dbScanLock and currently owns it. +3) While lock set is being changed, a thread issues a dbScanLock. + +solution: + +1) globalLock is locked during the entire time a thread is modifying lock sets + +2) lockSetModifyLock is locked whenever any fields in lockSet are being accessed +or lockRecord.plockSet is being accessed. + +NOTE: + +dblsr may crash if executed while lock sets are being modified. +It is NOT a good idea to make it more robust by issuing dbLockSetGblLock +since this will delay all other threads. +*****************************************************************************/ + +#include +#include +#include +#include + +#include "epicsStdioRedirect.h" +#include "dbDefs.h" +#include "dbBase.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "epicsAssert.h" +#include "cantProceed.h" +#include "ellLib.h" +#include "dbBase.h" +#include "dbStaticLib.h" +#include "dbFldTypes.h" +#include "link.h" +#include "dbCommon.h" +#include "epicsPrint.h" +#include "errMdef.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbAccessDefs.h" +#include "dbLock.h" + + +static int dbLockIsInitialized = FALSE; + +typedef enum { + listTypeScanLock = 0, + listTypeRecordLock = 1, + listTypeFree = 2 +} listType; + +#define nlistType listTypeFree + 1 + +static ELLLIST lockSetList[nlistType]; +static epicsMutexId globalLock; +static epicsMutexId lockSetModifyLock; +static unsigned long id = 0; +static char *msstring[4]={"NMS","MS","MSI","MSS"}; + +typedef enum { + lockSetStateFree=0, lockSetStateScanLock, lockSetStateRecordLock +} lockSetState; + +typedef struct lockSet { + ELLNODE node; + ELLLIST lockRecordList; + epicsMutexId lock; + unsigned long id; + listType type; + lockSetState state; + epicsThreadId thread_id; + dbCommon *precord; + int nRecursion; + int nWaiting; + int trace; /*For field TPRO*/ +} lockSet; + +/* dbCommon.LSET is a plockRecord */ +typedef struct lockRecord { + ELLNODE node; + lockSet *plockSet; + dbCommon *precord; +} lockRecord; + +/*private routines */ +static void dbLockInitialize(void) +{ + int i; + + if(dbLockIsInitialized) return; + for(i=0; i< nlistType; i++) ellInit(&lockSetList[i]); + globalLock = epicsMutexMustCreate(); + lockSetModifyLock = epicsMutexMustCreate(); + dbLockIsInitialized = TRUE; +} + +static lockSet * allocLockSet( + lockRecord *plockRecord, listType type, + lockSetState state, epicsThreadId thread_id) +{ + lockSet *plockSet; + + assert(dbLockIsInitialized); + plockSet = (lockSet *)ellFirst(&lockSetList[listTypeFree]); + if(plockSet) { + ellDelete(&lockSetList[listTypeFree],&plockSet->node); + } else { + plockSet = dbCalloc(1,sizeof(lockSet)); + plockSet->lock = epicsMutexMustCreate(); + } + ellInit(&plockSet->lockRecordList); + plockRecord->plockSet = plockSet; + id++; + plockSet->id = id; + plockSet->type = type; + plockSet->state = state; + plockSet->thread_id = thread_id; + plockSet->precord = 0; + plockSet->nRecursion = 0; + plockSet->nWaiting = 0; + ellAdd(&plockSet->lockRecordList,&plockRecord->node); + ellAdd(&lockSetList[type],&plockSet->node); + return(plockSet); +} + +unsigned long epicsShareAPI dbLockGetLockId(dbCommon *precord) +{ + lockRecord *plockRecord = precord->lset; + lockSet *plockSet; + long id = 0; + + assert(plockRecord); + epicsMutexMustLock(lockSetModifyLock); + plockSet = plockRecord->plockSet; + if(plockSet) id = plockSet->id; + epicsMutexUnlock(lockSetModifyLock); + return(id); +} + +void epicsShareAPI dbLockSetGblLock(void) +{ + assert(dbLockIsInitialized); + epicsMutexMustLock(globalLock); +} + +void epicsShareAPI dbLockSetGblUnlock(void) +{ + lockSet *plockSet; + lockSet *pnext; + epicsMutexMustLock(lockSetModifyLock); + plockSet = (lockSet *)ellFirst(&lockSetList[listTypeRecordLock]); + while(plockSet) { + pnext = (lockSet *)ellNext(&plockSet->node); + ellDelete(&lockSetList[listTypeRecordLock],&plockSet->node); + plockSet->type = listTypeScanLock; + plockSet->state = lockSetStateFree; + plockSet->thread_id = 0; + plockSet->precord = 0; + plockSet->nRecursion = 0; + plockSet->nWaiting = 0; + ellAdd(&lockSetList[listTypeScanLock],&plockSet->node); + plockSet = pnext; + } + epicsMutexUnlock(lockSetModifyLock); + epicsMutexUnlock(globalLock); + return; +} + +void epicsShareAPI dbLockSetRecordLock(dbCommon *precord) +{ + lockRecord *plockRecord = precord->lset; + lockSet *plockSet; + + /*Must make sure that no other thread has lock*/ + assert(plockRecord); + epicsMutexMustLock(lockSetModifyLock); + plockSet = plockRecord->plockSet; + assert(plockSet); + if(plockSet->type==listTypeRecordLock) { + epicsMutexUnlock(lockSetModifyLock); + return; + } + assert(plockSet->thread_id!=epicsThreadGetIdSelf()); + plockSet->state = lockSetStateRecordLock; + /*Wait until owner finishes and all waiting get to change state*/ + while(1) { + epicsMutexUnlock(lockSetModifyLock); + epicsMutexMustLock(plockSet->lock); + epicsMutexUnlock(plockSet->lock); + epicsMutexMustLock(lockSetModifyLock); + if(plockSet->nWaiting == 0 && plockSet->nRecursion==0) break; + epicsThreadSleep(.1); + } + assert(plockSet->nWaiting == 0 && plockSet->nRecursion==0); + assert(plockSet->type==listTypeScanLock); + assert(plockSet->state==lockSetStateRecordLock); + ellDelete(&lockSetList[plockSet->type],&plockSet->node); + ellAdd(&lockSetList[listTypeRecordLock],&plockSet->node); + plockSet->type = listTypeRecordLock; + plockSet->thread_id = epicsThreadGetIdSelf(); + plockSet->precord = 0; + epicsMutexUnlock(lockSetModifyLock); +} + +void epicsShareAPI dbScanLock(dbCommon *precord) +{ + lockRecord *plockRecord = precord->lset; + lockSet *plockSet; + epicsMutexLockStatus status; + epicsThreadId idSelf = epicsThreadGetIdSelf(); + + /* + * If this assertion is failing it is likely because iocInit + * has not completed. It must complete before normal record + * processing is possible. Consider using an initHook to + * detect when this occurs. + */ + assert(dbLockIsInitialized); + while(1) { + epicsMutexMustLock(lockSetModifyLock); + plockSet = plockRecord->plockSet; + if(!plockSet) goto getGlobalLock; + switch(plockSet->state) { + case lockSetStateFree: + status = epicsMutexTryLock(plockSet->lock); + assert(status==epicsMutexLockOK); + plockSet->nRecursion = 1; + plockSet->thread_id = idSelf; + plockSet->precord = precord; + plockSet->state = lockSetStateScanLock; + epicsMutexUnlock(lockSetModifyLock); + return; + case lockSetStateScanLock: + if(plockSet->thread_id!=idSelf) { + plockSet->nWaiting +=1; + epicsMutexUnlock(lockSetModifyLock); + epicsMutexMustLock(plockSet->lock); + epicsMutexMustLock(lockSetModifyLock); + plockSet->nWaiting -=1; + if(plockSet->state==lockSetStateRecordLock) { + epicsMutexUnlock(plockSet->lock); + goto getGlobalLock; + } + assert(plockSet->state==lockSetStateScanLock); + plockSet->nRecursion = 1; + plockSet->thread_id = idSelf; + plockSet->precord = precord; + } else { + plockSet->nRecursion += 1; + } + epicsMutexUnlock(lockSetModifyLock); + return; + case lockSetStateRecordLock: + /*Only recursive locking is permitted*/ + if((plockSet->nRecursion==0) || (plockSet->thread_id!=idSelf)) + goto getGlobalLock; + plockSet->nRecursion += 1; + epicsMutexUnlock(lockSetModifyLock); + return; + default: + cantProceed("dbScanLock. Bad case choice"); + } +getGlobalLock: + epicsMutexUnlock(lockSetModifyLock); + epicsMutexMustLock(globalLock); + epicsMutexUnlock(globalLock); + } +} + +void epicsShareAPI dbScanUnlock(dbCommon *precord) +{ + lockRecord *plockRecord = precord->lset; + lockSet *plockSet; + + assert(plockRecord); + epicsMutexMustLock(lockSetModifyLock); + plockSet = plockRecord->plockSet; + assert(plockSet); + assert(epicsThreadGetIdSelf()==plockSet->thread_id); + assert(plockSet->nRecursion>=1); + plockSet->nRecursion -= 1; + if(plockSet->nRecursion==0) { + plockSet->thread_id = 0; + plockSet->precord = 0; + if((plockSet->state == lockSetStateScanLock) + && (plockSet->nWaiting==0)) plockSet->state = lockSetStateFree; + epicsMutexUnlock(plockSet->lock); + } + epicsMutexUnlock(lockSetModifyLock); + return; +} + +void epicsShareAPI dbLockInitRecords(dbBase *pdbbase) +{ + int link; + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + dbRecordNode *pdbRecordNode; + dbCommon *precord; + DBLINK *plink; + int nrecords=0; + lockRecord *plockRecord; + + dbLockInitialize(); + /*Allocate and initialize a lockRecord for each record instance*/ + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + nrecords += ellCount(&pdbRecordType->recList) + - pdbRecordType->no_aliases; + } + /*Allocate all of them at once */ + plockRecord = dbCalloc(nrecords,sizeof(lockRecord)); + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList); + pdbRecordNode; + pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { + precord = pdbRecordNode->precord; + if (!precord->name[0] || + pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + continue; + plockRecord->precord = precord; + precord->lset = plockRecord; + plockRecord++; + } + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList); + pdbRecordNode; + pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { + precord = pdbRecordNode->precord; + if (!precord->name[0] || + pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + continue; + plockRecord = precord->lset; + if(!plockRecord->plockSet) + allocLockSet(plockRecord,listTypeScanLock,lockSetStateFree,0); + for(link=0; linkno_links; link++) { + DBADDR *pdbAddr; + + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[link]]; + plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + if(plink->type != DB_LINK) continue; + pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); + dbLockSetMerge(precord,pdbAddr->precord); + } + } + } +} + +void epicsShareAPI dbLockSetMerge(dbCommon *pfirst,dbCommon *psecond) +{ + lockRecord *p1lockRecord = pfirst->lset; + lockRecord *p2lockRecord = psecond->lset; + lockSet *p1lockSet; + lockSet *p2lockSet; + lockRecord *plockRecord; + lockRecord *pnext; + + epicsMutexMustLock(lockSetModifyLock); + if(pfirst==psecond) goto all_done; + p1lockSet = p1lockRecord->plockSet; + p2lockSet = p2lockRecord->plockSet; + assert(p1lockSet || p2lockSet); + if(p1lockSet == p2lockSet) goto all_done; + if(!p1lockSet) { + p1lockRecord->plockSet = p2lockSet; + ellAdd(&p2lockSet->lockRecordList,&p1lockRecord->node); + goto all_done; + } + if(!p2lockSet) { + p2lockRecord->plockSet = p1lockSet; + ellAdd(&p1lockSet->lockRecordList,&p2lockRecord->node); + goto all_done; + } + /*Move entire second list to first*/ + assert(p1lockSet->type == p2lockSet->type); + plockRecord = (lockRecord *)ellFirst(&p2lockSet->lockRecordList); + while(plockRecord) { + pnext = (lockRecord *)ellNext(&plockRecord->node); + ellDelete(&p2lockSet->lockRecordList,&plockRecord->node); + plockRecord->plockSet = p1lockSet; + ellAdd(&p1lockSet->lockRecordList,&plockRecord->node); + plockRecord = pnext; + } + ellDelete(&lockSetList[p2lockSet->type],&p2lockSet->node); + p2lockSet->type = listTypeFree; + ellAdd(&lockSetList[listTypeFree],&p2lockSet->node); +all_done: + epicsMutexUnlock(lockSetModifyLock); + return; +} + +void epicsShareAPI dbLockSetSplit(dbCommon *psource) +{ + lockSet *plockSet; + lockRecord *plockRecord; + lockRecord *pnext; + dbCommon *precord; + int link; + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + DBLINK *plink; + int indlockRecord,nlockRecords; + lockRecord **paplockRecord; + epicsThreadId idself = epicsThreadGetIdSelf(); + + + plockRecord = psource->lset; + assert(plockRecord); + plockSet = plockRecord->plockSet; + assert(plockSet); + assert(plockSet->state==lockSetStateRecordLock); + assert(plockSet->type==listTypeRecordLock); + /*First remove all records from lock set and store in paplockRecord*/ + nlockRecords = ellCount(&plockSet->lockRecordList); + paplockRecord = dbCalloc(nlockRecords,sizeof(lockRecord*)); + epicsMutexMustLock(lockSetModifyLock); + plockRecord = (lockRecord *)ellFirst(&plockSet->lockRecordList); + for(indlockRecord=0; indlockRecordnode); + ellDelete(&plockSet->lockRecordList,&plockRecord->node); + plockRecord->plockSet = 0; + paplockRecord[indlockRecord] = plockRecord; + plockRecord = pnext; + } + ellDelete(&lockSetList[plockSet->type],&plockSet->node); + plockSet->state = lockSetStateFree; + plockSet->type = listTypeFree; + ellAdd(&lockSetList[listTypeFree],&plockSet->node); + epicsMutexUnlock(lockSetModifyLock); + /*Now recompute lock sets */ + for(indlockRecord=0; indlockRecordplockSet) { + allocLockSet(plockRecord,listTypeRecordLock, + lockSetStateRecordLock,idself); + } + precord = plockRecord->precord; + epicsMutexUnlock(lockSetModifyLock); + pdbRecordType = precord->rdes; + for(link=0; linkno_links; link++) { + DBADDR *pdbAddr; + + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[link]]; + plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + if(plink->type != DB_LINK) continue; + pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); + dbLockSetMerge(precord,pdbAddr->precord); + } + } + free(paplockRecord); +} + +long epicsShareAPI dblsr(char *recordname,int level) +{ + int link; + DBENTRY dbentry; + DBENTRY *pdbentry=&dbentry; + long status; + dbCommon *precord; + lockSet *plockSet; + lockRecord *plockRecord; + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + DBLINK *plink; + + printf("globalLock %p\n",globalLock); + printf("lockSetModifyLock %p\n",lockSetModifyLock); + if (recordname && ((*recordname == '\0') || !strcmp(recordname,"*"))) + recordname = NULL; + if(recordname) { + dbInitEntry(pdbbase,pdbentry); + status = dbFindRecord(pdbentry,recordname); + if(status) { + printf("Record not found\n"); + dbFinishEntry(pdbentry); + return(0); + } + precord = pdbentry->precnode->precord; + dbFinishEntry(pdbentry); + plockRecord = precord->lset; + if(!plockRecord) return(0); + plockSet = plockRecord->plockSet; + } else { + plockSet = (lockSet *)ellFirst(&lockSetList[listTypeScanLock]); + } + for( ; plockSet; plockSet = (lockSet *)ellNext(&plockSet->node)) { + printf("Lock Set %lu %d members epicsMutexId %p", + plockSet->id,ellCount(&plockSet->lockRecordList),plockSet->lock); + if(epicsMutexTryLock(plockSet->lock)==epicsMutexLockOK) { + epicsMutexUnlock(plockSet->lock); + printf(" Not Locked\n"); + } else { + printf(" thread %p",plockSet->thread_id); + if(! plockSet->precord || !plockSet->precord->name) + printf(" NULL record or record name\n"); + else + printf(" record %s\n",plockSet->precord->name); + } + if(level==0) { if(recordname) break; continue; } + for(plockRecord = (lockRecord *)ellFirst(&plockSet->lockRecordList); + plockRecord; plockRecord = (lockRecord *)ellNext(&plockRecord->node)) { + precord = plockRecord->precord; + pdbRecordType = precord->rdes; + printf("%s\n",precord->name); + if(level<=1) continue; + for(link=0; (linkno_links) ; link++) { + DBADDR *pdbAddr; + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->link_ind[link]]; + plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + if(plink->type != DB_LINK) continue; + pdbAddr = (DBADDR *)(plink->value.pv_link.pvt); + printf("\t%s",pdbFldDes->name); + if(pdbFldDes->field_type==DBF_INLINK) { + printf("\t INLINK"); + } else if(pdbFldDes->field_type==DBF_OUTLINK) { + printf("\tOUTLINK"); + } else if(pdbFldDes->field_type==DBF_FWDLINK) { + printf("\tFWDLINK"); + } + printf(" %s %s", + ((plink->value.pv_link.pvlMask&pvlOptPP)?" PP":"NPP"), + msstring[plink->value.pv_link.pvlMask&pvlOptMsMode]); + printf(" %s\n",pdbAddr->precord->name); + } + } + if(recordname) break; + } + return(0); +} + +long epicsShareAPI dbLockShowLocked(int level) +{ + int indListType; + lockSet *plockSet; + epicsMutexLockStatus status; + epicsMutexLockStatus lockSetModifyLockStatus = epicsMutexLockOK; + int itry; + + printf("listTypeScanLock %d listTypeRecordLock %d listTypeFree %d\n", + ellCount(&lockSetList[0]), + ellCount(&lockSetList[1]), + ellCount(&lockSetList[2])); + for(itry=0; itry<100; itry++) { + lockSetModifyLockStatus = epicsMutexTryLock(lockSetModifyLock); + if(lockSetModifyLockStatus==epicsMutexLockOK) break; + epicsThreadSleep(.05); + } + if(lockSetModifyLockStatus!=epicsMutexLockOK) { + printf("Could not lock lockSetModifyLock\n"); + epicsMutexShow(lockSetModifyLock,level); + } + status = epicsMutexTryLock(globalLock); + if(status==epicsMutexLockOK) { + epicsMutexUnlock(globalLock); + } else { + printf("globalLock is locked\n"); + epicsMutexShow(globalLock,level); + } + /*Even if failure on lockSetModifyLock will continue */ + for(indListType=0; indListType <= 1; ++indListType) { + plockSet = (lockSet *)ellFirst(&lockSetList[indListType]); + if(plockSet) { + if(indListType==0) printf("listTypeScanLock\n"); + else printf("listTypeRecordLock\n"); + } + while(plockSet) { + epicsMutexLockStatus status; + + status = epicsMutexTryLock(plockSet->lock); + if(status==epicsMutexLockOK) epicsMutexUnlock(plockSet->lock); + if(status!=epicsMutexLockOK || indListType==1) { + if(plockSet->precord) + printf("%s ",plockSet->precord->name); + printf("state %d thread_id %p nRecursion %d nWaiting %d\n", + plockSet->state,plockSet->thread_id, + plockSet->nRecursion,plockSet->nWaiting); + epicsMutexShow(plockSet->lock,level); + } + plockSet = (lockSet *)ellNext(&plockSet->node); + } + } + if(lockSetModifyLockStatus==epicsMutexLockOK) + epicsMutexUnlock(lockSetModifyLock); + return(0); +} + +int * epicsShareAPI dbLockSetAddrTrace(dbCommon *precord) +{ + lockRecord *plockRecord = precord->lset; + lockSet *plockSet = plockRecord->plockSet; + + return(&plockSet->trace); +} diff --git a/src/db/dbLock.h b/src/db/dbLock.h new file mode 100644 index 000000000..5e82925ef --- /dev/null +++ b/src/db/dbLock.h @@ -0,0 +1,53 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbLock.h */ +/* Author: Marty Kraimer Date: 12MAR96 */ + +#ifndef INCdbLockh +#define INCdbLockh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct dbCommon; +struct dbBase; + +epicsShareFunc void epicsShareAPI dbScanLock(struct dbCommon *precord); +epicsShareFunc void epicsShareAPI dbScanUnlock(struct dbCommon *precord); +epicsShareFunc unsigned long epicsShareAPI dbLockGetLockId( + struct dbCommon *precord); + +epicsShareFunc void epicsShareAPI dbLockInitRecords(struct dbBase *pdbbase); +epicsShareFunc void epicsShareAPI dbLockSetMerge( + struct dbCommon *pfirst,struct dbCommon *psecond); +epicsShareFunc void epicsShareAPI dbLockSetSplit(struct dbCommon *psource); +/*The following are for code that modifies lock sets*/ +epicsShareFunc void epicsShareAPI dbLockSetGblLock(void); +epicsShareFunc void epicsShareAPI dbLockSetGblUnlock(void); +epicsShareFunc void epicsShareAPI dbLockSetRecordLock(struct dbCommon *precord); + +/* Lock Set Report */ +epicsShareFunc long epicsShareAPI dblsr(char *recordname,int level); +/* If recordname NULL then all records*/ +/* level = (0,1,2) (lock set state, + recordname, +DB links) */ + +epicsShareFunc long epicsShareAPI dbLockShowLocked(int level); + +/*KLUDGE to support field TPRO*/ +epicsShareFunc int * epicsShareAPI dbLockSetAddrTrace(struct dbCommon *precord); + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbLockh*/ diff --git a/src/db/dbNotify.c b/src/db/dbNotify.c new file mode 100644 index 000000000..a5190f49b --- /dev/null +++ b/src/db/dbNotify.c @@ -0,0 +1,573 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbNotify.c */ +/* base/src/db Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Marty Kraimer + * Date: 03-30-95 + * Extracted from dbLink.c +*/ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "ellLib.h" +#include "epicsAssert.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "errlog.h" +#include "errMdef.h" +#include "dbBase.h" +#include "dbStaticLib.h" +#include "dbFldTypes.h" +#include "link.h" +#include "dbCommon.h" +#define epicsExportSharedSymbols +#include "callback.h" +#include "dbAddr.h" +#include "dbScan.h" +#include "dbLock.h" +#include "callback.h" +#include "dbAccessDefs.h" +#include "recGbl.h" +#include "dbNotify.h" +#include "epicsTime.h" +#include "cantProceed.h" + +/*putNotify.state values */ +typedef enum { + putNotifyNotActive, + putNotifyWaitForRestart, + putNotifyRestartCallbackRequested, + putNotifyRestartInProgress, + putNotifyPutInProgress, + putNotifyUserCallbackRequested, + putNotifyUserCallbackActive +}putNotifyState; + +/*structure attached to ppnr field of each record*/ +typedef struct putNotifyRecord { + ellCheckNode waitNode; + ELLLIST restartList; /*list of putNotifys to restart*/ + dbCommon *precord; +}putNotifyRecord; + +#define MAGIC 0xfedc0123 +typedef struct putNotifyPvt { + ELLNODE node; /*For free list*/ + long magic; + short state; + CALLBACK callback; + ELLLIST waitList; /*list of records for current putNotify*/ + short cancelWait; + short userCallbackWait; + epicsEventId cancelEvent; + epicsEventId userCallbackEvent; +}putNotifyPvt; + +/* putNotify groups can span locksets if links are dynamically modified*/ +/* Thus a global lock is taken while putNotify fields are accessed */ +typedef struct notifyGlobal { + epicsMutexId lock; + ELLLIST freeList; +}notifyGlobal; + +static notifyGlobal *pnotifyGlobal = 0; + +/*Local routines*/ +static void putNotifyInit(putNotify *ppn); +static void putNotifyCleanup(putNotify *ppn); +static void restartCheck(putNotifyRecord *ppnr); +static void callUser(dbCommon *precord,putNotify *ppn); +static void notifyCallback(CALLBACK *pcallback); +static void putNotifyCommon(putNotify *ppn,dbCommon *precord); + +#define ellSafeAdd(list,listnode) \ +{ \ + assert((listnode)->isOnList==0); \ + ellAdd((list),&((listnode)->node)); \ + (listnode)->isOnList=1; \ +} + +#define ellSafeDelete(list,listnode) \ +{ \ + assert((listnode)->isOnList); \ + ellDelete((list),&((listnode)->node)); \ + (listnode)->isOnList=0; \ +} + +static void putNotifyInit(putNotify *ppn) +{ + putNotifyPvt *pputNotifyPvt; + + pputNotifyPvt = (putNotifyPvt *)ellFirst(&pnotifyGlobal->freeList); + if(pputNotifyPvt) { + ellDelete(&pnotifyGlobal->freeList,&pputNotifyPvt->node); + } else { + pputNotifyPvt = dbCalloc(1,sizeof(putNotifyPvt)); + pputNotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty); + pputNotifyPvt->userCallbackEvent = epicsEventCreate(epicsEventEmpty); + pputNotifyPvt->magic = MAGIC; + pputNotifyPvt->state = putNotifyNotActive; + } + pputNotifyPvt->state = putNotifyNotActive; + callbackSetCallback(notifyCallback,&pputNotifyPvt->callback); + callbackSetUser(ppn,&pputNotifyPvt->callback); + callbackSetPriority(priorityLow,&pputNotifyPvt->callback); + ellInit(&pputNotifyPvt->waitList); + ppn->status = 0; + pputNotifyPvt->state = putNotifyNotActive; + pputNotifyPvt->cancelWait = pputNotifyPvt->userCallbackWait = 0; + ppn->pputNotifyPvt = pputNotifyPvt; +} + +static void putNotifyCleanup(putNotify *ppn) +{ + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + + pputNotifyPvt->state = putNotifyNotActive; + ellAdd(&pnotifyGlobal->freeList,&pputNotifyPvt->node); + ppn->pputNotifyPvt = 0; +} + +static void restartCheck(putNotifyRecord *ppnr) +{ + dbCommon *precord = ppnr->precord; + putNotify *pfirst; + putNotifyPvt *pputNotifyPvt; + + assert(precord->ppn); + pfirst = (putNotify *)ellFirst(&ppnr->restartList); + if(!pfirst) { + precord->ppn = 0; + return; + } + pputNotifyPvt = (putNotifyPvt *)pfirst->pputNotifyPvt; + assert(pputNotifyPvt->state==putNotifyWaitForRestart); + /* remove pfirst from restartList */ + ellSafeDelete(&ppnr->restartList,&pfirst->restartNode); + /*make pfirst owner of the record*/ + precord->ppn = pfirst; + /* request callback for pfirst */ + pputNotifyPvt->state = putNotifyRestartCallbackRequested; + callbackRequest(&pputNotifyPvt->callback); +} + +static void callUser(dbCommon *precord,putNotify *ppn) +{ + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + (*ppn->userCallback)(ppn); + epicsMutexMustLock(pnotifyGlobal->lock); + if(pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) { + errlogPrintf("%s putNotify: both cancelWait and userCallbackWait true." + "This is illegal\n",precord->name); + pputNotifyPvt->cancelWait = pputNotifyPvt->userCallbackWait = 0; + } + if(!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) { + putNotifyCleanup(ppn); + epicsMutexUnlock(pnotifyGlobal->lock); + return; + } + if(pputNotifyPvt->cancelWait) { + pputNotifyPvt->cancelWait = 0; + epicsEventSignal(pputNotifyPvt->cancelEvent); + epicsMutexUnlock(pnotifyGlobal->lock); + return; + } + assert(pputNotifyPvt->userCallbackWait); + pputNotifyPvt->userCallbackWait = 0; + epicsEventSignal(pputNotifyPvt->userCallbackEvent); + epicsMutexUnlock(pnotifyGlobal->lock); + return; +} + +static void putNotifyCommon(putNotify *ppn,dbCommon *precord) +{ + long status=0; + dbFldDes *pfldDes = ppn->paddr->pfldDes; + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + + if(precord->ppn && pputNotifyPvt->state!=putNotifyRestartCallbackRequested) + { /*another putNotify owns the record */ + pputNotifyPvt->state = putNotifyWaitForRestart; + ellSafeAdd(&precord->ppnr->restartList,&ppn->restartNode); + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + return; + } else if(precord->ppn){ + assert(precord->ppn==ppn); + assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested); + } + if(precord->pact) { + precord->ppn = ppn; + ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); + pputNotifyPvt->state = putNotifyRestartInProgress; + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + return; + } + status=dbPut(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest); + ppn->status = (status==0) ? putNotifyOK : putNotifyError; + /* Check to see if dbProcess should not be called */ + if(!status /*dont process if dbPut returned error */ + &&((ppn->paddr->pfield==(void *)&precord->proc) /*If PROC call dbProcess*/ + || (pfldDes->process_passive && precord->scan==0))) { + precord->ppn = ppn; + ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); + pputNotifyPvt->state = putNotifyPutInProgress; + epicsMutexUnlock(pnotifyGlobal->lock); + dbProcess(precord); + dbScanUnlock(precord); + return; + } + if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { + restartCheck(precord->ppnr); + } + pputNotifyPvt->state = putNotifyUserCallbackActive; + assert(precord->ppn!=ppn); + callUser(precord,ppn); +} + +static void notifyCallback(CALLBACK *pcallback) +{ + putNotify *ppn=NULL; + dbCommon *precord; + putNotifyPvt *pputNotifyPvt; + + callbackGetUser(ppn,pcallback); + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + precord = ppn->paddr->precord; + dbScanLock(precord); + epicsMutexMustLock(pnotifyGlobal->lock); + assert(precord->ppnr); + assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested + || pputNotifyPvt->state==putNotifyUserCallbackRequested); + assert(ellCount(&pputNotifyPvt->waitList)==0); + if(pputNotifyPvt->cancelWait) { + if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { + restartCheck(precord->ppnr); + } + epicsEventSignal(pputNotifyPvt->cancelEvent); + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + return; + } + if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { + putNotifyCommon(ppn,precord); + return; + } + /* All done. Clean up and call userCallback */ + pputNotifyPvt->state = putNotifyUserCallbackActive; + assert(precord->ppn!=ppn); + callUser(precord,ppn); +} + +void epicsShareAPI dbPutNotifyInit(void) +{ + if(pnotifyGlobal) return; + pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal)); + pnotifyGlobal->lock = epicsMutexMustCreate(); + ellInit(&pnotifyGlobal->freeList); +} + +void epicsShareAPI dbPutNotify(putNotify *ppn) +{ + dbCommon *precord = ppn->paddr->precord; + short dbfType = ppn->paddr->field_type; + long status=0; + putNotifyPvt *pputNotifyPvt; + + assert(precord); + /*check for putField disabled*/ + if(precord->disp) { + if((void *)(&precord->disp) != ppn->paddr->pfield) { + ppn->status = putNotifyPutDisabled; + (*ppn->userCallback)(ppn); + return; + } + } + /* Must handle DBF_XXXLINKs as special case. + * Only dbPutField will change link fields. + * Also the record is not processed as a result + */ + if(dbfType>=DBF_INLINK && dbfType<=DBF_FWDLINK) { + status=dbPutField(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest); + ppn->status = (status==0) ? putNotifyOK : putNotifyError; + (*ppn->userCallback)(ppn); + return; + } + dbScanLock(precord); + epicsMutexMustLock(pnotifyGlobal->lock); + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + if(pputNotifyPvt && (pputNotifyPvt->magic!=MAGIC)) { + printf("dbPutNotify:pputNotifyPvt was not initialized\n"); + pputNotifyPvt = 0; + } + if(pputNotifyPvt) { + assert(pputNotifyPvt->state==putNotifyUserCallbackActive); + pputNotifyPvt->userCallbackWait = 1; + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + epicsEventWait(pputNotifyPvt->userCallbackEvent); + dbScanLock(precord); + epicsMutexMustLock(pnotifyGlobal->lock); + putNotifyCleanup(ppn); + } + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + assert(!pputNotifyPvt); + putNotifyInit(ppn); + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + if(!precord->ppnr) {/* make sure record has a putNotifyRecord*/ + precord->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); + precord->ppnr->precord = precord; + ellInit(&precord->ppnr->restartList); + } + putNotifyCommon(ppn,precord); +} + +void epicsShareAPI dbNotifyCancel(putNotify *ppn) +{ + dbCommon *precord = ppn->paddr->precord; + putNotifyState state; + putNotifyPvt *pputNotifyPvt; + + assert(precord); + dbScanLock(precord); + epicsMutexMustLock(pnotifyGlobal->lock); + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + if(!pputNotifyPvt || pputNotifyPvt->state==putNotifyNotActive) { + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + return; + } + state = pputNotifyPvt->state; + /*If callback is scheduled or active wait for it to complete*/ + if(state==putNotifyUserCallbackRequested + || state==putNotifyRestartCallbackRequested + || state==putNotifyUserCallbackActive) { + pputNotifyPvt->cancelWait = 1; + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); + epicsEventWait(pputNotifyPvt->cancelEvent); + epicsMutexMustLock(pnotifyGlobal->lock); + putNotifyCleanup(ppn); + epicsMutexUnlock(pnotifyGlobal->lock); + return; + } + switch(state) { + case putNotifyNotActive: break; + case putNotifyWaitForRestart: + assert(precord->ppn); + assert(precord->ppn!=ppn); + ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode); + break; + case putNotifyRestartInProgress: + case putNotifyPutInProgress: + { /*Take all records out of wait list */ + putNotifyRecord *ppnrWait; + + while((ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList))){ + ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode); + restartCheck(ppnrWait); + } + } + if(precord->ppn==ppn) restartCheck(precord->ppnr); + break; + default: + printf("dbNotify: illegal state for notifyCallback\n"); + } + pputNotifyPvt->state = putNotifyNotActive; + putNotifyCleanup(ppn); + epicsMutexUnlock(pnotifyGlobal->lock); + dbScanUnlock(precord); +} + +void epicsShareAPI dbNotifyCompletion(dbCommon *precord) +{ + putNotify *ppn = precord->ppn; + putNotifyPvt *pputNotifyPvt; + + epicsMutexMustLock(pnotifyGlobal->lock); + assert(ppn); + assert(precord->ppnr); + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + if(pputNotifyPvt->state!=putNotifyRestartInProgress + && pputNotifyPvt->state!=putNotifyPutInProgress) { + epicsMutexUnlock(pnotifyGlobal->lock); + return; + } + ellSafeDelete(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); + if((ellCount(&pputNotifyPvt->waitList)!=0)) { + restartCheck(precord->ppnr); + } else if(pputNotifyPvt->state == putNotifyPutInProgress) { + pputNotifyPvt->state = putNotifyUserCallbackRequested; + restartCheck(precord->ppnr); + callbackRequest(&pputNotifyPvt->callback); + } else if(pputNotifyPvt->state == putNotifyRestartInProgress) { + pputNotifyPvt->state = putNotifyRestartCallbackRequested; + callbackRequest(&pputNotifyPvt->callback); + } else { + cantProceed("dbNotifyCompletion illegal state"); + } + epicsMutexUnlock(pnotifyGlobal->lock); +} + +void epicsShareAPI dbNotifyAdd(dbCommon *pfrom, dbCommon *pto) +{ + putNotify *ppn = pfrom->ppn; + putNotifyPvt *pputNotifyPvt; + + if(pto->pact) return; /*if active it will not be processed*/ + epicsMutexMustLock(pnotifyGlobal->lock); + if(!pto->ppnr) {/* make sure record has a putNotifyRecord*/ + pto->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); + pto->ppnr->precord = pto; + ellInit(&pto->ppnr->restartList); + } + assert(ppn); + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + if(!(pto->ppn) + && (pputNotifyPvt->state==putNotifyPutInProgress) + && (pto!=ppn->paddr->precord)) { + putNotifyPvt *pputNotifyPvt; + pto->ppn = pfrom->ppn; + pputNotifyPvt = (putNotifyPvt *)pfrom->ppn->pputNotifyPvt; + ellSafeAdd(&pputNotifyPvt->waitList,&pto->ppnr->waitNode); + } + epicsMutexUnlock(pnotifyGlobal->lock); +} + +typedef struct tpnInfo { + epicsEventId callbackDone; + putNotify *ppn; +}tpnInfo; + +static void dbtpnCallback(putNotify *ppn) +{ + putNotifyStatus status = ppn->status; + tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + + if(status==0) + printf("dbtpnCallback: success record=%s\n",ppn->paddr->precord->name); + else + printf("%s dbtpnCallback putNotify.status %d\n",ppn->paddr->precord->name,(int)status); + epicsEventSignal(ptpnInfo->callbackDone); +} + +static void tpnThread(void *pvt) +{ + tpnInfo *ptpnInfo = (tpnInfo *)pvt; + putNotify *ppn = (putNotify *)ptpnInfo->ppn; + + dbPutNotify(ppn); + epicsEventWait(ptpnInfo->callbackDone); + dbNotifyCancel(ppn); + epicsEventDestroy(ptpnInfo->callbackDone); + free((void *)ppn->paddr); + free(ppn); + free(ptpnInfo); +} + +long epicsShareAPI dbtpn(char *pname,char *pvalue) +{ + long status; + tpnInfo *ptpnInfo; + DBADDR *pdbaddr=NULL; + putNotify *ppn=NULL; + char *psavevalue; + int len; + + len = strlen(pvalue); + /*allocate space for value immediately following DBADDR*/ + pdbaddr = dbCalloc(1,sizeof(DBADDR) + len+1); + psavevalue = (char *)(pdbaddr + 1); + strcpy(psavevalue,pvalue); + status = dbNameToAddr(pname,pdbaddr); + if(status) { + errMessage(status, "dbtpn: dbNameToAddr"); + free((void *)pdbaddr); + return(-1); + } + ppn = dbCalloc(1,sizeof(putNotify)); + ppn->paddr = pdbaddr; + ppn->pbuffer = psavevalue; + ppn->nRequest = 1; + ppn->dbrType = DBR_STRING; + ppn->userCallback = dbtpnCallback; + ptpnInfo = dbCalloc(1,sizeof(tpnInfo)); + ptpnInfo->ppn = ppn; + ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); + ppn->usrPvt = ptpnInfo; + epicsThreadCreate("dbtpn",epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackMedium), + tpnThread,ptpnInfo); + return(0); +} + +int epicsShareAPI dbNotifyDump(void) +{ + epicsMutexLockStatus lockStatus; + dbRecordType *pdbRecordType; + dbRecordNode *pdbRecordNode; + dbCommon *precord; + putNotify *ppn; + putNotify *ppnRestart; + putNotifyRecord *ppnrWait; + int itry; + + + for(itry=0; itry<100; itry++) { + lockStatus = epicsMutexTryLock(pnotifyGlobal->lock); + if(lockStatus==epicsMutexLockOK) break; + epicsThreadSleep(.05); + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList); + pdbRecordNode; + pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { + putNotifyPvt *pputNotifyPvt; + precord = pdbRecordNode->precord; + if (!precord->name[0] || + pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + continue; + if(!precord->ppn) continue; + if(!precord->ppnr) continue; + if(precord->ppn->paddr->precord != precord) continue; + ppn = precord->ppn; + pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + printf("%s state %d ppn %p\n waitList\n", + precord->name,pputNotifyPvt->state,(void*)ppn); + ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList); + while(ppnrWait) { + printf(" %s pact %d\n", + ppnrWait->precord->name,ppnrWait->precord->pact); + ppnrWait = (putNotifyRecord *)ellNext(&ppnrWait->waitNode.node); + } + printf(" restartList\n"); + ppnRestart = (putNotify *)ellFirst(&precord->ppnr->restartList); + while(ppnRestart) { + printf(" %p\n", (void *)ppnRestart); + ppnRestart = (putNotify *)ellNext(&ppnRestart->restartNode.node); + } + } + } + if(lockStatus==epicsMutexLockOK) epicsMutexUnlock(pnotifyGlobal->lock); + return(0); +} diff --git a/src/db/dbNotify.h b/src/db/dbNotify.h new file mode 100644 index 000000000..5c57c0d90 --- /dev/null +++ b/src/db/dbNotify.h @@ -0,0 +1,122 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbNotify.h */ + +#ifndef INCdbNotifyh +#define INCdbNotifyh + +#include "shareLib.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "callback.h" + +#ifdef __cplusplus + /* for brain dead C++ compilers */ + struct dbCommon; + struct putNotify; + extern "C" { +#endif + +typedef struct ellCheckNode{ + ELLNODE node; + int isOnList; +}ellCheckNode; + +typedef enum { + putNotifyOK, + putNotifyCanceled, + putNotifyError, + putNotifyPutDisabled +}putNotifyStatus; + +typedef struct putNotify{ + ellCheckNode restartNode; + /*The following members MUST be set by user*/ + void (*userCallback)(struct putNotify *); + struct dbAddr *paddr; /*dbAddr set by dbNameToAddr*/ + void *pbuffer; /*address of data*/ + long nRequest; /*number of elements to be written*/ + short dbrType; /*database request type*/ + void *usrPvt; /*for private use of user*/ + /*The following is status of request. Set by dbNotify */ + putNotifyStatus status; + void *pputNotifyPvt; /*for private use of putNotify*/ +}putNotify; + +/* dbPutNotify and dbNotifyCancel are the routines called by user*/ +/* The user is normally channel access client or server */ +epicsShareFunc void epicsShareAPI dbPutNotify(putNotify *pputNotify); +epicsShareFunc void epicsShareAPI dbNotifyCancel(putNotify *pputNotify); + +/*dbPutNotifyMapType convience function for old database access*/ +epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype); + +/* dbPutNotifyInit called by iocInit */ +epicsShareFunc void epicsShareAPI dbPutNotifyInit(void); + +/*dbNotifyAdd called by dbScanPassive and dbScanLink*/ +epicsShareFunc void epicsShareAPI dbNotifyAdd( + struct dbCommon *pfrom,struct dbCommon *pto); +/*dbNotifyCompletion called by recGblFwdLink or dbAccess*/ +epicsShareFunc void epicsShareAPI dbNotifyCompletion(struct dbCommon *precord); + +/* dbtpn is test routine for put notify */ +epicsShareFunc long epicsShareAPI dbtpn(char *recordname,char *value); + +/* dbNotifyDump is an INVASIVE debug utility. Dont use this needlessly*/ +epicsShareFunc int epicsShareAPI dbNotifyDump(void); + +/* This module provides code to handle put notify. If a put causes a record to + * be processed, then a user supplied callback is called when that record + * and all records processed because of that record complete processing. + * For asynchronous records completion means completion of the asyn phase. + * + * User code calls putNotifyInit, putNotifyCleanup, + * dbPutNotify, and dbNotifyCancel. + * + * The use must allocate storage for "struct putNotify" + * The user MUST set pputNotifyPvt=0 before the first call to dbPutNotify + * and should never modify it again. + * + * After dbPutNotify is called it may not called for the same putNotify + * until the putCallback is complete. The use can call dbNotifyCancel + * to cancel the operation. + * + * The user callback is called when the operation is completed. + * + * The other global routines (dbNotifyAdd and dbNotifyCompletion) are called by: + * + * dbAccess.c + * dbScanPassive and dbScanLink + * call dbNotifyAdd just before calling dbProcess + * dbProcess + * Calls dbNotifyCompletion if dbProcess does not call process + * Unless pact is already true. + * recGbl + * recGblFwdLink calls dbNotifyCompletion + */ + +/* Two fields in dbCommon are used for put notify. + * ppn pointer to putNotify + * If a record is part of a put notify group, + * This field is the address of the associated putNotify. + * As soon as a record completes processing the field is set NULL + * ppnr pointer to putNotifyRecord + * Address of a putNotifyRecord for 1) list node for records + * put notify is waiting to complete, and 2) a list of records + * to restart. + * + * See the Application Developer's Guide for implementation rules + */ +#ifdef __cplusplus +} +#endif + +#endif /*INCdbNotifyh*/ diff --git a/src/db/dbPutNotifyBlocker.cpp b/src/db/dbPutNotifyBlocker.cpp new file mode 100644 index 000000000..5dbee30bf --- /dev/null +++ b/src/db/dbPutNotifyBlocker.cpp @@ -0,0 +1,234 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author: + * Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include +#include + +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsTime.h" +#include "tsFreeList.h" +#include "errMdef.h" +#include "errlog.h" + +#include "caerr.h" // this needs to be eliminated +#include "db_access.h" // this needs to be eliminated + +#define epicsExportSharedSymbols +#include "dbCAC.h" +#include "dbChannelIO.h" +#include "dbPutNotifyBlocker.h" + +dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : + mutex ( mutexIn ), pNotify ( 0 ), + maxValueSize ( sizeof ( this->dbrScalarValue ) ) +{ + memset ( & this->pn, '\0', sizeof ( this->pn ) ); + memset ( & this->dbrScalarValue, '\0', sizeof ( this->dbrScalarValue ) ); + this->pn.pbuffer = & this->dbrScalarValue; +} + +dbPutNotifyBlocker::~dbPutNotifyBlocker () +{ +} + +void dbPutNotifyBlocker::destructor ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->cancel ( guard ); + if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { + char * pBuf = static_cast < char * > ( this->pn.pbuffer ); + delete [] pBuf; + } + this->~dbPutNotifyBlocker (); +} + +void dbPutNotifyBlocker::cancel ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->pn.paddr ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + dbNotifyCancel ( &this->pn ); + } + this->pNotify = 0; + this->block.signal (); +} + +void dbPutNotifyBlocker::expandValueBuf ( + epicsGuard < epicsMutex > & guard, unsigned long newSize ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->maxValueSize < newSize ) { + if ( this->maxValueSize > sizeof ( this->dbrScalarValue ) ) { + char * pBuf = static_cast < char * > ( this->pn.pbuffer ); + delete [] pBuf; + this->maxValueSize = sizeof ( this->dbrScalarValue ); + this->pn.pbuffer = & this->dbrScalarValue; + } + this->pn.pbuffer = new char [newSize]; + this->maxValueSize = newSize; + } +} + +extern "C" void putNotifyCompletion ( putNotify *ppn ) +{ + dbPutNotifyBlocker * pBlocker = static_cast < dbPutNotifyBlocker * > ( ppn->usrPvt ); + { + epicsGuard < epicsMutex > guard ( pBlocker->mutex ); + if ( pBlocker->pNotify ) { + if ( pBlocker->pn.status != putNotifyOK) { + pBlocker->pNotify->exception ( + guard, ECA_PUTFAIL, "put notify unsuccessful", + static_cast (pBlocker->pn.dbrType), + static_cast (pBlocker->pn.nRequest) ); + } + else { + pBlocker->pNotify->completion ( guard ); + } + } + else { + errlogPrintf ( "put notify completion with nill pNotify?\n" ); + } + pBlocker->pNotify = 0; + } + pBlocker->block.signal (); +} + +void dbPutNotifyBlocker::initiatePutNotify ( + epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, + struct dbAddr & addr, unsigned type, unsigned long count, + const void * pValue ) +{ + guard. assertIdenticalMutex ( this->mutex ); + epicsTime begin; + bool beginTimeInit = false; + while ( true ) { + if ( this->pNotify == 0 ) { + this->pNotify = & notify; + break; + } + if ( beginTimeInit ) { + if ( epicsTime::getCurrent () - begin > 30.0 ) { + throw cacChannel::requestTimedOut (); + } + } + else { + begin = epicsTime::getCurrent (); + beginTimeInit = true; + } + { + epicsGuardRelease < epicsMutex > autoRelease ( guard ); + this->block.wait ( 1.0 ); + } + } + + if ( count > LONG_MAX ) { + throw cacChannel::outOfBounds(); + } + + if ( type > SHRT_MAX ) { + throw cacChannel::badType(); + } + + int status = dbPutNotifyMapType ( + &this->pn, static_cast ( type ) ); + if ( status ) { + this->pNotify = 0; + throw cacChannel::badType(); + } + + this->pn.nRequest = static_cast < unsigned > ( count ); + this->pn.paddr = &addr; + this->pn.userCallback = putNotifyCompletion; + this->pn.usrPvt = this; + + unsigned long size = dbr_size_n ( type, count ); + this->expandValueBuf ( guard, size ); + memcpy ( this->pn.pbuffer, pValue, size ); + + { + epicsGuardRelease < epicsMutex > autoRelease ( guard ); + ::dbPutNotify ( &this->pn ); + } +} + +void dbPutNotifyBlocker::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->show ( guard, level ); +} + +void dbPutNotifyBlocker::show ( + epicsGuard < epicsMutex > &, unsigned level ) const +{ + printf ( "put notify blocker at %p\n", + static_cast ( this ) ); + if ( level > 0u ) { + this->block.show ( level - 1u ); + } +} + +dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () +{ + return 0; +} + +void * dbPutNotifyBlocker::operator new ( size_t size, + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +void * dbPutNotifyBlocker::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +#ifdef CXX_PLACEMENT_DELETE +void dbPutNotifyBlocker::operator delete ( void *pCadaver, + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +void dbPutNotifyBlocker::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} diff --git a/src/db/dbPutNotifyBlocker.h b/src/db/dbPutNotifyBlocker.h new file mode 100644 index 000000000..a2864b0ff --- /dev/null +++ b/src/db/dbPutNotifyBlocker.h @@ -0,0 +1,90 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef dbPutNotifyBlockerh +#define dbPutNotifyBlockerh + +#ifdef epicsExportSharedSymbols +#define dbPutNotifyBlockerh_restore_epicsExportSharedSymbols +#undef epicsExportSharedSymbols +#endif + +#include "tsFreeList.h" +#include "compilerDependencies.h" + +#ifdef dbPutNotifyBlockerh_restore_epicsExportSharedSymbols +#define epicsExportSharedSymbols +#endif + +class dbPutNotifyBlocker : public dbBaseIO { +public: + dbPutNotifyBlocker ( epicsMutex & ); + void destructor ( epicsGuard < epicsMutex > & ); + void initiatePutNotify ( epicsGuard < epicsMutex > &, + cacWriteNotify &, struct dbAddr &, + unsigned type, unsigned long count, const void * pValue ); + void cancel ( epicsGuard < epicsMutex > & ); + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; + void show ( unsigned level ) const; + void * operator new ( size_t size, + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & ); + epicsPlacementDeleteOperator (( void *, + tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & )) +private: + putNotify pn; + // + // Include a union of all scalar types + // including fixed length strings so + // that in many cases we can avoid + // allocating another buffer + // + union { + dbr_string_t strval; + dbr_short_t shrtval; + dbr_short_t intval; + dbr_float_t fltval; + dbr_enum_t enmval; + dbr_char_t charval; + dbr_long_t longval; + dbr_double_t doubleval; + } dbrScalarValue; + epicsEvent block; + epicsMutex & mutex; + cacWriteNotify * pNotify; + unsigned long maxValueSize; + dbSubscriptionIO * isSubscription (); + void expandValueBuf ( + epicsGuard < epicsMutex > &, unsigned long newSize ); + friend void putNotifyCompletion ( putNotify * ppn ); + dbPutNotifyBlocker ( const dbPutNotifyBlocker & ); + dbPutNotifyBlocker & operator = ( const dbPutNotifyBlocker & ); + virtual ~dbPutNotifyBlocker (); + void * operator new ( size_t size ); + void operator delete ( void * ); +}; + +#endif // ifndef dbPutNotifyBlockerh + diff --git a/src/db/dbScan.c b/src/db/dbScan.c new file mode 100644 index 000000000..5a372d6a9 --- /dev/null +++ b/src/db/dbScan.c @@ -0,0 +1,765 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* dbScan.c */ +/* tasks and subroutines to scan the database */ +/* + * Original Author: Bob Dalesio + * Current Author: Marty Kraimer + * Date: 07/18/91 + */ + +#include +#include +#include +#include +#include +#include + +#include "epicsStdioRedirect.h" +#include "dbDefs.h" +#include "ellLib.h" +#include "taskwd.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsInterrupt.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "cantProceed.h" +#include "epicsRingPointer.h" +#include "epicsPrint.h" +#include "dbBase.h" +#include "dbStaticLib.h" +#include "dbFldTypes.h" +#include "link.h" +#include "devSup.h" +#include "dbCommon.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "callback.h" +#include "dbAccessDefs.h" +#include "dbLock.h" +#include "recGbl.h" +#include "dbScan.h" + + +/* Task Control */ +enum ctl {ctlRun, ctlPause, ctlExit}; + +/* Task Startup/Shutdown Synchronization */ +static epicsEventId startStopEvent; + +volatile enum ctl scanCtl; + +/* SCAN ONCE */ + +static int onceQueueSize = 1000; +static epicsEventId onceSem; +static epicsRingPointerId onceQ; +static epicsThreadId onceTaskId; +static void *exitOnce; + + +/* All other scan types */ +typedef struct scan_list{ + epicsMutexId lock; + ELLLIST list; + short modified;/*has list been modified?*/ +} scan_list; +/*scan_elements are allocated and the address stored in dbCommon.spvt*/ +typedef struct scan_element{ + ELLNODE node; + scan_list *pscan_list; + struct dbCommon *precord; +} scan_element; + + +/* PERIODIC */ + +typedef struct periodic_scan_list { + scan_list scan_list; + double period; + volatile enum ctl scanCtl; + epicsEventId loopEvent; +} periodic_scan_list; + +static int nPeriodic = 0; +static periodic_scan_list **papPeriodic; /* pointer to array of pointers */ +static epicsThreadId *periodicTaskId; /* array of thread ids */ + + +static char *priorityName[NUM_CALLBACK_PRIORITIES] = { + "Low", "Medium", "High" +}; + + +/* EVENT */ + +#define MAX_EVENTS 256 +typedef struct event_scan_list { + CALLBACK callback; + scan_list scan_list; +} event_scan_list; +static event_scan_list *pevent_list[NUM_CALLBACK_PRIORITIES][MAX_EVENTS]; + + +/* IO_EVENT*/ + +typedef struct io_scan_list { + CALLBACK callback; + scan_list scan_list; + struct io_scan_list *next; +} io_scan_list; + +static io_scan_list *iosl_head[NUM_CALLBACK_PRIORITIES] = { + NULL, NULL, NULL +}; + + +/* Private routines */ +static void onceTask(void *); +static void initOnce(void); +static void periodicTask(void *arg); +static void initPeriodic(void); +static void spawnPeriodic(int ind); +static void initEvent(void); +static void eventCallback(CALLBACK *pcallback); +static void ioeventCallback(CALLBACK *pcallback); +static void printList(scan_list *psl, char *message); +static void scanList(scan_list *psl); +static void buildScanLists(void); +static void addToList(struct dbCommon *precord, scan_list *psl); +static void deleteFromList(struct dbCommon *precord, scan_list *psl); + +static void scanShutdown(void *arg) +{ + int i; + + interruptAccept = FALSE; + + for (i = 0; i < nPeriodic; i++) { + papPeriodic[i]->scanCtl = ctlExit; + epicsEventSignal(papPeriodic[i]->loopEvent); + epicsEventWait(startStopEvent); + } + + scanOnce((dbCommon *)&exitOnce); + epicsEventWait(startStopEvent); +} + +long scanInit(void) +{ + int i; + + startStopEvent = epicsEventMustCreate(epicsEventEmpty); + scanCtl = ctlPause; + + initOnce(); + initPeriodic(); + initEvent(); + buildScanLists(); + for (i = 0; i < nPeriodic; i++) + spawnPeriodic(i); + + epicsAtExit(scanShutdown, NULL); + return 0; +} + +void scanRun(void) +{ + int i; + + interruptAccept = TRUE; + scanCtl = ctlRun; + + for (i = 0; i < nPeriodic; i++) + papPeriodic[i]->scanCtl = ctlRun; +} + +void scanPause(void) +{ + int i; + + for (i = nPeriodic - 1; i >= 0; --i) + papPeriodic[i]->scanCtl = ctlPause; + + scanCtl = ctlPause; + interruptAccept = FALSE; +} + +void scanAdd(struct dbCommon *precord) +{ + int scan; + + /* get the list on which this record belongs */ + scan = precord->scan; + if (scan == menuScanPassive) return; + if (scan < 0 || scan >= nPeriodic + SCAN_1ST_PERIODIC) { + recGblRecordError(-1, (void *)precord, + "scanAdd detected illegal SCAN value"); + } else if (scan == menuScanEvent) { + int evnt; + int prio; + event_scan_list *pesl; + + evnt = precord->evnt; + if (evnt < 0 || evnt >= MAX_EVENTS) { + recGblRecordError(S_db_badField, (void *)precord, + "scanAdd detected illegal EVNT value"); + precord->scan = menuScanPassive; + return; + } + prio = precord->prio; + if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { + recGblRecordError(-1, (void *)precord, + "scanAdd: illegal prio field"); + precord->scan = menuScanPassive; + return; + } + pesl = pevent_list[prio][evnt]; + if (pesl == NULL) { + pesl = dbCalloc(1, sizeof(event_scan_list)); + pevent_list[prio][evnt] = pesl; + pesl->scan_list.lock = epicsMutexMustCreate(); + callbackSetCallback(eventCallback, &pesl->callback); + callbackSetPriority(prio, &pesl->callback); + callbackSetUser(pesl, &pesl->callback); + ellInit(&pesl->scan_list.list); + } + addToList(precord, &pesl->scan_list); + } else if (scan == menuScanI_O_Intr) { + io_scan_list *piosl = NULL; + int prio; + DEVSUPFUN get_ioint_info; + + if (precord->dset == NULL){ + recGblRecordError(-1, (void *)precord, + "scanAdd: I/O Intr not valid (no DSET) "); + precord->scan = menuScanPassive; + return; + } + get_ioint_info = precord->dset->get_ioint_info; + if (get_ioint_info == NULL) { + recGblRecordError(-1, (void *)precord, + "scanAdd: I/O Intr not valid (no get_ioint_info)"); + precord->scan = menuScanPassive; + return; + } + if (get_ioint_info(0, precord, &piosl)) { + precord->scan = menuScanPassive; + return; + } + if (piosl == NULL) { + recGblRecordError(-1, (void *)precord, + "scanAdd: I/O Intr not valid"); + precord->scan = menuScanPassive; + return; + } + prio = precord->prio; + if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { + recGblRecordError(-1, (void *)precord, + "scanAdd: illegal prio field"); + precord->scan = menuScanPassive; + return; + } + piosl += prio; /* get piosl for correct priority*/ + addToList(precord, &piosl->scan_list); + } else if (scan >= SCAN_1ST_PERIODIC) { + addToList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list); + } + return; +} + +void scanDelete(struct dbCommon *precord) +{ + short scan; + + /* get the list on which this record belongs */ + scan = precord->scan; + if (scan == menuScanPassive) return; + if (scan < 0 || scan >= nPeriodic + SCAN_1ST_PERIODIC) { + recGblRecordError(-1, (void *)precord, + "scanDelete detected illegal SCAN value"); + } else if (scan == menuScanEvent) { + int evnt; + int prio; + event_scan_list *pesl; + scan_list *psl = 0; + + evnt = precord->evnt; + if (evnt < 0 || evnt >= MAX_EVENTS) { + recGblRecordError(S_db_badField, (void *)precord, + "scanAdd detected illegal EVNT value"); + precord->scan = menuScanPassive; + return; + } + prio = precord->prio; + if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { + recGblRecordError(-1, (void *)precord, + "scanAdd: illegal prio field"); + precord->scan = menuScanPassive; + return; + } + pesl = pevent_list[prio][evnt]; + if (pesl) psl = &pesl->scan_list; + if (!pesl || !psl) + recGblRecordError(-1, (void *)precord, + "scanDelete for bad evnt"); + else + deleteFromList(precord, psl); + } else if (scan == menuScanI_O_Intr) { + io_scan_list *piosl=NULL; + int prio; + DEVSUPFUN get_ioint_info; + + if (precord->dset==NULL) { + recGblRecordError(-1, (void *)precord, + "scanDelete: I/O Intr not valid (no DSET)"); + return; + } + get_ioint_info=precord->dset->get_ioint_info; + if (get_ioint_info == NULL) { + recGblRecordError(-1, (void *)precord, + "scanDelete: I/O Intr not valid (no get_ioint_info)"); + return; + } + if (get_ioint_info(1, precord, &piosl)) return; + if (piosl == NULL) { + recGblRecordError(-1, (void *)precord, + "scanDelete: I/O Intr not valid"); + return; + } + prio = precord->prio; + if (prio < 0 || prio >= NUM_CALLBACK_PRIORITIES) { + recGblRecordError(-1, (void *)precord, + "scanDelete: get_ioint_info returned illegal priority"); + return; + } + piosl += prio; /*get piosl for correct priority*/ + deleteFromList(precord, &piosl->scan_list); + } else if (scan >= SCAN_1ST_PERIODIC) { + deleteFromList(precord, &papPeriodic[scan - SCAN_1ST_PERIODIC]->scan_list); + } + return; +} + +double scanPeriod(int scan) { + scan -= SCAN_1ST_PERIODIC; + if (scan < 0 || scan >= nPeriodic) + return 0.0; + return papPeriodic[scan]->period; +} + +int scanppl(double period) /* print periodic list */ +{ + periodic_scan_list *ppsl; + char message[80]; + int i; + + for (i = 0; i < nPeriodic; i++) { + ppsl = papPeriodic[i]; + if (ppsl == NULL) continue; + if (period > 0.0 && (fabs(period - ppsl->period) >.05)) continue; + sprintf(message, "Scan Period = %g seconds ", ppsl->period); + printList(&ppsl->scan_list, message); + } + return 0; +} + +int scanpel(int event_number) /* print event list */ +{ + char message[80]; + int prio, evnt; + event_scan_list *pesl; + + for (evnt = 0; evnt < MAX_EVENTS; evnt++) { + if (event_number && evntevent_number) break; + for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + pesl = pevent_list[prio][evnt]; + if (!pesl) continue; + if (ellCount(&pesl->scan_list.list) == 0) continue; + sprintf(message, "Event %d Priority %s", evnt, priorityName[prio]); + printList(&pesl->scan_list, message); + } + } + return 0; +} + +int scanpiol(void) /* print io_event list */ +{ + io_scan_list *piosl; + int prio; + char message[80]; + + for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + piosl = iosl_head[prio]; + if (piosl == NULL) continue; + sprintf(message, "IO Event: Priority %s", priorityName[prio]); + while(piosl != NULL) { + printList(&piosl->scan_list, message); + piosl = piosl->next; + } + } + return 0; +} + +static void eventCallback(CALLBACK *pcallback) +{ + event_scan_list *pesl; + + callbackGetUser(pesl, pcallback); + scanList(&pesl->scan_list); +} + +static void initEvent(void) +{ + int evnt, prio; + + for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + for (evnt = 0; evnt < MAX_EVENTS; evnt++) { + pevent_list[prio][evnt] = NULL; + } + } +} + +void post_event(int event) +{ + int prio; + event_scan_list *pesl; + + if (scanCtl != ctlRun) return; + if (event < 0 || event >= MAX_EVENTS) { + errMessage(-1, "illegal event passed to post_event"); + return; + } + for (prio=0; prioscan_list.list) >0) + callbackRequest((void *)pesl); + } +} + +void scanIoInit(IOSCANPVT *ppioscanpvt) +{ + int prio; + + /* Allocate an array of io_scan_lists, one for each priority. */ + /* IOSCANPVT will hold the address of this array of structures */ + *ppioscanpvt = dbCalloc(NUM_CALLBACK_PRIORITIES, sizeof(io_scan_list)); + for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + io_scan_list *piosl = &(*ppioscanpvt)[prio]; + callbackSetCallback(ioeventCallback, &piosl->callback); + callbackSetPriority(prio, &piosl->callback); + callbackSetUser(piosl, &piosl->callback); + ellInit(&piosl->scan_list.list); + piosl->scan_list.lock = epicsMutexMustCreate(); + piosl->next = iosl_head[prio]; + iosl_head[prio] = piosl; + } +} + + +void scanIoRequest(IOSCANPVT pioscanpvt) +{ + int prio; + + if (scanCtl != ctlRun) return; + for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + io_scan_list *piosl = &pioscanpvt[prio]; + if (ellCount(&piosl->scan_list.list) > 0) + callbackRequest(&piosl->callback); + } +} + +void scanOnce(struct dbCommon *precord) +{ + static int newOverflow = TRUE; + int lockKey; + int pushOK; + + lockKey = epicsInterruptLock(); + pushOK = epicsRingPointerPush(onceQ, precord); + epicsInterruptUnlock(lockKey); + + if (!pushOK) { + if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); + newOverflow = FALSE; + } else { + newOverflow = TRUE; + } + epicsEventSignal(onceSem); +} + +static void onceTask(void *arg) +{ + taskwdInsert(0, NULL, NULL); + epicsEventSignal(startStopEvent); + + while (TRUE) { + void *precord; + + epicsEventMustWait(onceSem); + while ((precord = epicsRingPointerPop(onceQ))) { + if (precord == &exitOnce) goto shutdown; + dbScanLock(precord); + dbProcess(precord); + dbScanUnlock(precord); + } + } + +shutdown: + taskwdRemove(0); + epicsEventSignal(startStopEvent); +} + +int scanOnceSetQueueSize(int size) +{ + onceQueueSize = size; + return 0; +} + +static void initOnce(void) +{ + if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) { + cantProceed("initOnce: Ring buffer create failed\n"); + } + onceSem = epicsEventMustCreate(epicsEventEmpty); + onceTaskId = epicsThreadCreate("scanOnce", epicsThreadPriorityScanHigh, + epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0); + + epicsEventWait(startStopEvent); +} + +static void periodicTask(void *arg) +{ + periodic_scan_list *ppsl = (periodic_scan_list *)arg; + + epicsTimeStamp start_time, end_time; + double delay; + + taskwdInsert(0, NULL, NULL); + epicsEventSignal(startStopEvent); + + while (ppsl->scanCtl != ctlExit) { + epicsTimeGetCurrent(&start_time); + if (ppsl->scanCtl == ctlRun) scanList(&ppsl->scan_list); + epicsTimeGetCurrent(&end_time); + delay = ppsl->period - epicsTimeDiffInSeconds(&end_time, &start_time); + if (delay <= 0.0) delay = 0.1; + epicsEventWaitWithTimeout(ppsl->loopEvent, delay); + } + + taskwdRemove(0); + epicsEventSignal(startStopEvent); +} + + +static void initPeriodic(void) +{ + dbMenu *pmenu; + periodic_scan_list *ppsl; + int i; + + pmenu = dbFindMenu(pdbbase, "menuScan"); + if (!pmenu) { + epicsPrintf("initPeriodic: menuScan not present\n"); + return; + } + nPeriodic = pmenu->nChoice - SCAN_1ST_PERIODIC; + papPeriodic = dbCalloc(nPeriodic, sizeof(periodic_scan_list*)); + periodicTaskId = dbCalloc(nPeriodic, sizeof(void *)); + for (i = 0; i < nPeriodic; i++) { + ppsl = dbCalloc(1, sizeof(periodic_scan_list)); + + ppsl->scan_list.lock = epicsMutexMustCreate(); + ellInit(&ppsl->scan_list.list); + epicsScanDouble(pmenu->papChoiceValue[i + SCAN_1ST_PERIODIC], + &ppsl->period); + ppsl->scanCtl = ctlPause; + ppsl->loopEvent = epicsEventMustCreate(epicsEventEmpty); + + papPeriodic[i] = ppsl; + } +} + +static void spawnPeriodic(int ind) +{ + periodic_scan_list *ppsl; + char taskName[20]; + + ppsl = papPeriodic[ind]; + sprintf(taskName, "scan%g", ppsl->period); + periodicTaskId[ind] = epicsThreadCreate( + taskName, epicsThreadPriorityScanLow + ind, + epicsThreadGetStackSize(epicsThreadStackBig), + periodicTask, (void *)ppsl); + + epicsEventWait(startStopEvent); +} + +static void ioeventCallback(CALLBACK *pcallback) +{ + io_scan_list *piosl; + + callbackGetUser(piosl, pcallback); + scanList(&piosl->scan_list); +} + +static void printList(scan_list *psl, char *message) +{ + scan_element *pse; + + epicsMutexMustLock(psl->lock); + pse = (scan_element *)ellFirst(&psl->list); + epicsMutexUnlock(psl->lock); + if (pse == NULL) return; + printf("%s\n", message); + while (pse != NULL) { + printf(" %-28s\n", pse->precord->name); + epicsMutexMustLock(psl->lock); + if (pse->pscan_list != psl) { + epicsMutexUnlock(psl->lock); + printf("Scan list changed while processing."); + return; + } + pse = (scan_element *)ellNext(&pse->node); + epicsMutexUnlock(psl->lock); + } +} + +static void scanList(scan_list *psl) +{ + /* When reading this code remember that the call to dbProcess can result + * in the SCAN field being changed in an arbitrary number of records. + */ + + scan_element *pse; + scan_element *prev = NULL; + scan_element *next = NULL; + + epicsMutexMustLock(psl->lock); + psl->modified = FALSE; + pse = (scan_element *)ellFirst(&psl->list); + if (pse) next = (scan_element *)ellNext(&pse->node); + epicsMutexUnlock(psl->lock); + + while (pse) { + struct dbCommon *precord = pse->precord; + + dbScanLock(precord); + dbProcess(precord); + dbScanUnlock(precord); + + epicsMutexMustLock(psl->lock); + if (!psl->modified) { + prev = pse; + pse = (scan_element *)ellNext(&pse->node); + if (pse) next = (scan_element *)ellNext(&pse->node); + } else if (pse->pscan_list == psl) { + /*This scan element is still in same scan list*/ + prev = pse; + pse = (scan_element *)ellNext(&pse->node); + if (pse) next = (scan_element *)ellNext(&pse->node); + psl->modified = FALSE; + } else if (prev && prev->pscan_list == psl) { + /*Previous scan element is still in same scan list*/ + pse = (scan_element *)ellNext(&prev->node); + if (pse) { + prev = (scan_element *)ellPrevious(&pse->node); + next = (scan_element *)ellNext(&pse->node); + } + psl->modified = FALSE; + } else if (next && next->pscan_list == psl) { + /*Next scan element is still in same scan list*/ + pse = next; + prev = (scan_element *)ellPrevious(&pse->node); + next = (scan_element *)ellNext(&pse->node); + psl->modified = FALSE; + } else { + /*Too many changes. Just wait till next period*/ + epicsMutexUnlock(psl->lock); + return; + } + epicsMutexUnlock(psl->lock); + } +} + +static void buildScanLists(void) +{ + dbRecordType *pdbRecordType; + + /*Look for first record*/ + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + dbRecordNode *pdbRecordNode; + for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); + pdbRecordNode; + pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { + dbCommon *precord = pdbRecordNode->precord; + if (!precord->name[0] || + pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + continue; + scanAdd(precord); + } + } +} + +static void addToList(struct dbCommon *precord, scan_list *psl) +{ + scan_element *pse, *ptemp; + + epicsMutexMustLock(psl->lock); + pse = precord->spvt; + if (pse == NULL) { + pse = dbCalloc(1, sizeof(scan_element)); + precord->spvt = pse; + pse->precord = precord; + } + pse->pscan_list = psl; + ptemp = (scan_element *)ellFirst(&psl->list); + while (ptemp) { + if (ptemp->precord->phas > precord->phas) { + ellInsert(&psl->list, ellPrevious(&ptemp->node), &pse->node); + break; + } + ptemp = (scan_element *)ellNext(&ptemp->node); + } + if (ptemp == NULL) ellAdd(&psl->list, (void *)pse); + psl->modified = TRUE; + epicsMutexUnlock(psl->lock); +} + +static void deleteFromList(struct dbCommon *precord, scan_list *psl) +{ + scan_element *pse; + + epicsMutexMustLock(psl->lock); + pse = precord->spvt; + if (pse == NULL) { + epicsMutexUnlock(psl->lock); + errlogPrintf("dbScan: Tried to delete record from wrong scan list!\n" + "\t%s.SPVT = NULL, but psl = %p\n", + precord->name, (void *)psl); + return; + } + if (pse->pscan_list != psl) { + epicsMutexUnlock(psl->lock); + errlogPrintf("dbScan: Tried to delete record from wrong scan list!\n" + "\t%s.SPVT->pscan_list = %p but psl = %p\n", + precord->name, (void *)pse, (void *)psl); + return; + } + pse->pscan_list = NULL; + ellDelete(&psl->list, (void *)pse); + psl->modified = TRUE; + epicsMutexUnlock(psl->lock); +} diff --git a/src/db/dbScan.h b/src/db/dbScan.h new file mode 100644 index 000000000..e046235ca --- /dev/null +++ b/src/db/dbScan.h @@ -0,0 +1,69 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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: Marty Kraimer + * Date: 07-17-91 + */ + +#ifndef INCdbScanH +#define INCdbScanH + +#include + +#include "menuScan.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SCAN_PASSIVE menuScanPassive +#define SCAN_EVENT menuScanEvent +#define SCAN_IO_EVENT menuScanI_O_Intr +#define SCAN_1ST_PERIODIC (menuScanI_O_Intr + 1) + +#define MAX_PHASE SHRT_MAX +#define MIN_PHASE SHRT_MIN + +/*definitions for I/O Interrupt Scanning */ +struct io_scan_list; + +typedef struct io_scan_list *IOSCANPVT; + +struct dbCommon; + +epicsShareFunc long scanInit(void); +epicsShareFunc void scanRun(void); +epicsShareFunc void scanPause(void); + +epicsShareFunc void post_event(int event); +epicsShareFunc void scanAdd(struct dbCommon *); +epicsShareFunc void scanDelete(struct dbCommon *); +epicsShareFunc double scanPeriod(int scan); +epicsShareFunc void scanOnce(struct dbCommon *); +epicsShareFunc int scanOnceSetQueueSize(int size); + +/*print periodic lists*/ +epicsShareFunc int scanppl(double rate); + +/*print event lists*/ +epicsShareFunc int scanpel(int event_number); + +/*print io_event list*/ +epicsShareFunc int scanpiol(void); + +epicsShareFunc void scanIoInit(IOSCANPVT *); +epicsShareFunc void scanIoRequest(IOSCANPVT); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/db/dbSubscriptionIO.cpp b/src/db/dbSubscriptionIO.cpp new file mode 100644 index 000000000..e4396ec86 --- /dev/null +++ b/src/db/dbSubscriptionIO.cpp @@ -0,0 +1,169 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include + +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "tsFreeList.h" + +#include "db_access.h" // need to eliminate this +#include "cadef.h" // this can be eliminated when the callbacks use the new interface +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "dbCAC.h" +#include "dbChannelIO.h" +#include "db_access_routines.h" + +dbSubscriptionIO::dbSubscriptionIO ( + epicsGuard < epicsMutex > & guard, epicsMutex & mutexIn, + dbContext &, dbChannelIO & chanIO, + dbAddr & addr, cacStateNotify & notifyIn, unsigned typeIn, + unsigned long countIn, unsigned maskIn, dbEventCtx ctx ) : + mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), + chan ( chanIO ), es ( 0 ), type ( typeIn ), id ( 0u ) +{ + guard.assertIdenticalMutex ( this->mutex ); + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->es = db_add_event ( ctx, & addr, + dbSubscriptionEventCallback, (void *) this, maskIn ); + if ( this->es == 0 ) { + throw std::bad_alloc(); + } + db_post_single_event ( this->es ); + db_event_enable ( this->es ); + } +} + +dbSubscriptionIO::~dbSubscriptionIO () +{ +} + +void dbSubscriptionIO::destructor ( epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->~dbSubscriptionIO (); +} + +void dbSubscriptionIO::unsubscribe ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + if ( this->es ) { + dbEventSubscription tmp = this->es; + this->es = 0; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + db_cancel_event ( tmp ); + } + } +} + +void dbSubscriptionIO::channelDeleteException ( + epicsGuard < epicsMutex > & guard ) +{ + guard.assertIdenticalMutex ( this->mutex ); + this->notify.exception ( guard, ECA_CHANDESTROY, + this->chan.pName(guard), this->type, this->count ); +} + +void * dbSubscriptionIO::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void dbSubscriptionIO::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void * dbSubscriptionIO::operator new ( size_t size, + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +void dbSubscriptionIO::operator delete ( void * pCadaver, + tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr * /* paddr */, + int /* eventsRemaining */, struct db_field_log *pfl ) +{ + dbSubscriptionIO * pIO = static_cast < dbSubscriptionIO * > ( pPrivate ); + pIO->chan.callStateNotify ( pIO->type, pIO->count, pfl, pIO->notify ); +} + +void dbSubscriptionIO::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + this->show ( guard, level ); +} + +void dbSubscriptionIO::show ( + epicsGuard < epicsMutex > & guard, unsigned level ) const +{ + guard.assertIdenticalMutex ( this->mutex ); + + printf ( "Data base subscription IO at %p\n", + static_cast ( this ) ); + if ( level > 0u ) { + short tmpType; + if ( this->type < SHRT_MAX ) { + tmpType = static_cast < short > ( this->type ); + printf ( "\ttype %s, count %lu, channel at %p\n", + dbf_type_to_text ( tmpType ), this->count, + static_cast ( &this->chan ) ); + } + else { + printf ( "strange type !, count %lu, channel at %p\n", + this->count, static_cast ( &this->chan ) ); + } + } +} + +dbSubscriptionIO * dbSubscriptionIO::isSubscription () +{ + return this; +} + + diff --git a/src/db/dbTest.c b/src/db/dbTest.c new file mode 100644 index 000000000..53ec55fe9 --- /dev/null +++ b/src/db/dbTest.c @@ -0,0 +1,1232 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ +/* database access test subroutines */ + +#include +#include +#include + +#include "epicsStdlib.h" +#include "epicsStdio.h" +#include "epicsString.h" +#include "dbDefs.h" +#include "errlog.h" +#include "ellLib.h" +#include "epicsMutex.h" +#include "epicsStdioRedirect.h" +#include "ellLib.h" +#include "dbBase.h" +#include "dbStaticLib.h" +#include "link.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "devSup.h" +#include "drvSup.h" +#include "dbCommon.h" +#include "special.h" +#include "db_field_log.h" +#include "epicsString.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbLock.h" +#include "dbAccessDefs.h" +#include "recGbl.h" +#include "dbEvent.h" +#include "callback.h" +#include "dbTest.h" + +#define MAXLINE 80 +struct msgBuff { /* line output structure */ + char out_buff[MAXLINE + 1]; + char *pNext; + char *pLast; + char *pNexTab; + char message[128]; +}; +typedef struct msgBuff TAB_BUFFER; + +#ifndef MIN +# define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#endif +#ifndef MAX +# define MAX(x,y) (((x) < (y)) ? (x) : (y)) +#endif + +/* Local Routines */ +static long nameToAddr(const char *pname, DBADDR *paddr); +static void printDbAddr(DBADDR *paddr); +static void printBuffer( + long status,short dbr_type,void *pbuffer,long reqOptions, + long retOptions,long no_elements,TAB_BUFFER *pMsgBuff,int tab_size); +static int dbpr_report( + const char *pname,DBADDR *paddr,int interest_level, + TAB_BUFFER *pMsgBuff,int tab_size); +static void dbpr_msgOut(TAB_BUFFER *pMsgBuff,int tab_size); +static void dbpr_init_msg(TAB_BUFFER *pMsgBuff,int tab_size); +static void dbpr_insert_msg(TAB_BUFFER *pMsgBuff,int len,int tab_size); +static void dbpr_msg_flush(TAB_BUFFER *pMsgBuff,int tab_size); + +static char *dbf[DBF_NTYPES]={ + "STRING","CHAR","UCHAR","SHORT","USHORT","LONG","ULONG", + "FLOAT","DOUBLE","ENUM","MENU","DEVICE", + "INLINK","OUTLINK","FWDLINK","NOACCESS"}; + +static char *dbr[DBR_ENUM+2]={ + "STRING","CHAR","UCHAR","SHORT","USHORT","LONG","ULONG", + "FLOAT","DOUBLE","ENUM","NOACCESS"}; + +long epicsShareAPI dba(const char*pname) +{ + DBADDR addr; + + if (!pname || !*pname) { + printf("Usage: dba \"pv name\"\n"); + return 1; + } + if (nameToAddr(pname, &addr)) return -1; + printDbAddr(&addr); + return 0; +} + +long epicsShareAPI dbl(const char *precordTypename, const char *fields) +{ + DBENTRY dbentry; + DBENTRY *pdbentry=&dbentry; + long status; + int nfields = 0; + int ifield; + char *fieldnames = 0; + char **papfields = 0; + + if (!pdbbase) { + printf("No database loaded\n"); + return 0; + } + if (precordTypename && + ((*precordTypename == '\0') || !strcmp(precordTypename,"*"))) + precordTypename = NULL; + if (fields && (*fields == '\0')) + fields = NULL; + if (fields) { + char *pnext; + + fieldnames = epicsStrDup(fields); + nfields = 1; + pnext = fieldnames; + while (*pnext && (pnext = strchr(pnext,' '))) { + nfields++; + while (*pnext == ' ') pnext++; + } + papfields = dbCalloc(nfields,sizeof(char *)); + pnext = fieldnames; + for (ifield = 0; ifield < nfields; ifield++) { + papfields[ifield] = pnext; + if (ifield < nfields - 1) { + pnext = strchr(pnext, ' '); + *pnext++ = 0; + while (*pnext == ' ') pnext++; + } + } + } + dbInitEntry(pdbbase, pdbentry); + if (!precordTypename) + status = dbFirstRecordType(pdbentry); + else + status = dbFindRecordType(pdbentry,precordTypename); + if (status) { + printf("No record type\n"); + } + while (!status) { + status = dbFirstRecord(pdbentry); + while (!status) { + printf("%s", dbGetRecordName(pdbentry)); + for (ifield = 0; ifield < nfields; ifield++) { + char *pvalue; + status = dbFindField(pdbentry, papfields[ifield]); + if (status) { + if (!strcmp(papfields[ifield], "recordType")) { + pvalue = dbGetRecordTypeName(pdbentry); + } else { + printf(", "); + continue; + } + } else { + pvalue = dbGetString(pdbentry); + } + printf(", \"%s\"", pvalue ? pvalue : ""); + } + printf("\n"); + status = dbNextRecord(pdbentry); + } + if (precordTypename) break; + status = dbNextRecordType(pdbentry); + } + if (nfields > 0) { + free((void *)papfields); + free((void *)fieldnames); + } + dbFinishEntry(pdbentry); + return 0; +} + +long epicsShareAPI dbnr(int verbose) +{ + DBENTRY dbentry; + DBENTRY *pdbentry=&dbentry; + long status; + int nrecords; + int naliases; + int trecords = 0; + int taliases = 0; + + if (!pdbbase) { + printf("No database loaded\n"); + return 0; + } + dbInitEntry(pdbbase, pdbentry); + status = dbFirstRecordType(pdbentry); + if (status) { + printf("No record types loaded\n"); + return 0; + } + printf("Records Aliases Record Type\n"); + while (!status) { + naliases = dbGetNAliases(pdbentry); + taliases += naliases; + nrecords = dbGetNRecords(pdbentry) - naliases; + trecords += nrecords; + if (verbose || nrecords) + printf(" %5d %5d %s\n", + nrecords, naliases, dbGetRecordTypeName(pdbentry)); + status = dbNextRecordType(pdbentry); + } + dbFinishEntry(pdbentry); + printf("Total %d records, %d aliases\n", trecords, taliases); + return 0; +} + +long epicsShareAPI dbla(const char *pmask) +{ + DBENTRY dbentry; + DBENTRY *pdbentry = &dbentry; + long status; + + if (!pdbbase) { + printf("No database loaded\n"); + return 0; + } + dbInitEntry(pdbbase, pdbentry); + status = dbFirstRecordType(pdbentry); + while (!status) { + for (status = dbFirstRecord(pdbentry); !status; + status = dbNextRecord(pdbentry)) { + char *palias; + + if (!dbIsAlias(pdbentry)) continue; + palias = dbGetRecordName(pdbentry); + if (pmask && *pmask && !epicsStrGlobMatch(palias, pmask)) continue; + dbFindField(pdbentry, "NAME"); + printf("%s -> %s\n", palias, dbGetString(pdbentry)); + } + status = dbNextRecordType(pdbentry); + } + dbFinishEntry(pdbentry); + return 0; +} + +long epicsShareAPI dbgrep(const char *pmask) +{ + DBENTRY dbentry; + DBENTRY *pdbentry = &dbentry; + long status; + + if (!pmask || !*pmask) { + printf("Usage: dbgrep \"pattern\"\n"); + return 1; + } + if (!pdbbase) { + printf("No database loaded\n"); + return 0; + } + dbInitEntry(pdbbase, pdbentry); + status = dbFirstRecordType(pdbentry); + while (!status) { + status = dbFirstRecord(pdbentry); + while (!status) { + char *pname = dbGetRecordName(pdbentry); + if (epicsStrGlobMatch(pname, pmask)) puts(pname); + status = dbNextRecord(pdbentry); + } + status = dbNextRecordType(pdbentry); + } + dbFinishEntry(pdbentry); + return 0; +} + +long epicsShareAPI dbgf(const char *pname) +{ + /* declare buffer long just to ensure correct alignment */ + long buffer[100]; + long *pbuffer=&buffer[0]; + DBADDR addr; + long status; + long options = 0; + long no_elements; + static TAB_BUFFER msg_Buff; + int tab_size = 10; + + if (!pname || !*pname) { + printf("Usage: dbgf \"pv name\"\n"); + return 1; + } + if (nameToAddr(pname, &addr)) return -1; + no_elements = MIN(addr.no_elements, sizeof(buffer)/addr.field_size); + if (addr.dbr_field_type == DBR_ENUM) { + status = dbGetField(&addr, DBR_STRING, pbuffer, + &options, &no_elements, NULL); + printBuffer(status, DBR_STRING, pbuffer, 0L, 0L, + no_elements, &msg_Buff, tab_size); + } else { + status = dbGetField(&addr, addr.dbr_field_type, pbuffer, + &options, &no_elements, NULL); + printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L, + no_elements, &msg_Buff, tab_size); + } + msg_Buff.message[0] = '\0'; + dbpr_msgOut(&msg_Buff, tab_size); + return 0; +} + +long epicsShareAPI dbpf(const char *pname,const char *pvalue) +{ + DBADDR addr; + long status = 0; + + if (!pname || !*pname || !pvalue) { + printf("Usage: dbpf \"pv name\", \"value\"\n"); + return 1; + } + if (nameToAddr(pname, &addr)) return -1; + /* For enumerated types must allow for ENUM rather than string*/ + /* If entire field is digits then use DBR_ENUM else DBR_STRING*/ + if (addr.dbr_field_type == DBR_ENUM && !*pvalue && + strspn(pvalue,"0123456789") == strlen(pvalue)) { + unsigned short value; + + sscanf(pvalue, "%hu", &value); + status = dbPutField(&addr, DBR_ENUM, &value, 1L); + } else if (addr.dbr_field_type == DBR_CHAR && + addr.no_elements > 1) { + status = dbPutField(&addr, DBR_CHAR, pvalue, strlen(pvalue) + 1); + } else { + status = dbPutField(&addr, DBR_STRING, pvalue, 1L); + } + if (status) { + errMessage(status,"- dbPutField error\n"); + return status; + } + status = dbgf(pname); + return status; +} + +long epicsShareAPI dbpr(const char *pname,int interest_level) +{ + static TAB_BUFFER msg_Buff; + TAB_BUFFER *pMsgBuff = &msg_Buff; + DBADDR addr; + char *pmsg; + int tab_size = 20; + + if (!pname || !*pname) { + printf("Usage: dbpr \"pv name\", level\n"); + return 1; + } + if (nameToAddr(pname, &addr)) return -1; + pmsg = pMsgBuff->message; + + if (dbpr_report(pname, &addr, interest_level, pMsgBuff, tab_size)) + return 1; + pmsg[0] = '\0'; + dbpr_msgOut(pMsgBuff, tab_size); + return 0; +} + +long epicsShareAPI dbtr(const char *pname) +{ + DBADDR addr; + long status; + struct dbCommon *precord; + + if (!pname || !*pname) { + printf("Usage: dbtr \"pv name\"\n"); + return 1; + } + if (nameToAddr(pname, &addr)) return -1; + precord = (struct dbCommon*)addr.precord; + if (precord->pact) { + printf("record active\n"); + return 1; + } + dbScanLock(precord); + status = dbProcess(precord); + dbScanUnlock(precord); + if (status) + recGblRecordError(status, precord, "dbtr(dbProcess)"); + dbpr(pname, 3); + return 0; +} + +long epicsShareAPI dbtgf(const char *pname) +{ + /* declare buffer long just to ensure correct alignment */ + long buffer[400]; + long *pbuffer = &buffer[0]; + DBADDR addr; + long status; + long req_options, ret_options, no_elements; + short dbr_type; + static TAB_BUFFER msg_Buff; + TAB_BUFFER *pMsgBuff = &msg_Buff; + char *pmsg = pMsgBuff->message; + int tab_size = 10; + + if (pname==0 || *pname==0) { + printf("Usage: dbtgf \"pv name\"\n"); + return 1; + } + if (nameToAddr(pname, &addr)) return -1; + + /* try all options first */ + req_options = 0xffffffff; + ret_options = req_options; + no_elements = 0; + status = dbGetField(&addr, addr.dbr_field_type, pbuffer, + &ret_options, &no_elements, NULL); + printBuffer(status, addr.dbr_field_type, pbuffer, + req_options, ret_options, no_elements, pMsgBuff, tab_size); + + /* Now try all request types */ + ret_options=0; + + dbr_type = DBR_STRING; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE)); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_CHAR; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_UCHAR; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_SHORT; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_USHORT; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_LONG; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_ULONG; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_FLOAT; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_DOUBLE; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + dbr_type = DBR_ENUM; + no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16))); + status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL); + printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size); + + pmsg[0] = '\0'; + dbpr_msgOut(pMsgBuff, tab_size); + return(0); +} + +long epicsShareAPI dbtpf(const char *pname, const char *pvalue) +{ + /* declare buffer long just to ensure correct alignment */ + long buffer[100]; + long *pbuffer = buffer; + DBADDR addr; + long status = 0; + long options, no_elements; + char *pend; + long val_long; + int validLong; + unsigned long val_ulong; + int validULong; + int valid = 1; + int put_type; + epicsInt8 val_i8; + epicsUInt8 val_u8; + epicsInt16 val_i16; + epicsUInt16 val_u16; + epicsInt32 val_i32; + epicsUInt32 val_u32; + epicsFloat32 fvalue; + epicsFloat64 dvalue; + static TAB_BUFFER msg_Buff; + TAB_BUFFER *pMsgBuff = &msg_Buff; + char *pmsg = pMsgBuff->message; + int tab_size = 10; + + if (!pname || !*pname || !pvalue) { + printf("Usage: dbtpf \"pv name\", \"value\"\n"); + return 1; + } + if (nameToAddr(pname, &addr)) return -1; + + val_long = strtol(pvalue, &pend, 10); + validLong = (*pend == 0); + + val_ulong = strtoul(pvalue, &pend, 10); + validULong = (*pend == 0); + + for (put_type = DBR_STRING; put_type <= DBF_ENUM; put_type++) { + switch (put_type) { + case DBR_STRING: + status = dbPutField(&addr, put_type, pvalue, 1L); + break; + case DBR_CHAR: + if ((valid = validLong)) { + val_i8 = (epicsInt8)val_long; + status = dbPutField(&addr, put_type, &val_i8, 1L); + } + break; + case DBR_UCHAR: + if ((valid = validULong)) { + val_u8 = (epicsUInt8)val_ulong; + status = dbPutField(&addr, put_type, &val_u8, 1L); + } + break; + case DBR_SHORT: + if ((valid = validLong)) { + val_i16 = val_long; + status = dbPutField(&addr, put_type, &val_i16,1L); + } + break; + case DBR_USHORT: + if ((valid = validULong)) { + val_u16 = val_ulong; + status = dbPutField(&addr, put_type, &val_u16, 1L); + } + break; + case DBR_LONG: + if ((valid = validLong)) { + val_i32 = val_long; + status = dbPutField(&addr, put_type,&val_i32,1L); + } + break; + case DBR_ULONG: + if ((valid = validULong)) { + val_u32 = val_ulong; + status = dbPutField(&addr, put_type, &val_u32, 1L); + } + break; + case DBR_FLOAT: + if ((valid = epicsScanFloat(pvalue, &fvalue) == 1)) + status = dbPutField(&addr, put_type, &fvalue, 1L); + break; + case DBR_DOUBLE: + if ((valid = epicsScanDouble(pvalue, &dvalue) == 1)) + status = dbPutField(&addr, put_type, &dvalue, 1L); + break; + case DBR_ENUM: + if ((valid = validULong)) { + val_u16 = val_ulong; + status = dbPutField(&addr, put_type, &val_u16, 1L); + } + break; + } + if (valid) { + if (status) { + printf("Put as DBR_%s Failed.\n", dbr[put_type]); + } else { + printf("Put as DBR_%-6s Ok, result as ", dbr[put_type]); + no_elements = MIN(addr.no_elements, + ((sizeof(buffer))/addr.field_size)); + options = 0; + status = dbGetField(&addr, addr.dbr_field_type, pbuffer, + &options, &no_elements, NULL); + printBuffer(status, addr.dbr_field_type, pbuffer, 0L, 0L, + no_elements, pMsgBuff, tab_size); + } + } + } + + pmsg[0] = '\0'; + dbpr_msgOut(pMsgBuff, tab_size); + return(0); +} + +long epicsShareAPI dbior(const char *pdrvName,int interest_level) +{ + char *pname; + drvSup *pdrvSup; + struct drvet *pdrvet; + dbRecordType *pdbRecordType; + devSup *pdevSup; + struct dset *pdset; + + if (!pdbbase) { + printf("No database loaded\n"); + return 0; + } + if (pdrvName && ((*pdrvName == '\0') || !strcmp(pdrvName,"*"))) + pdrvName = NULL; + for (pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); + pdrvSup; + pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { + pname = pdrvSup->name; + if(pdrvName!=NULL && *pdrvName!='\0' && (strcmp(pdrvName,pname)!=0)) continue; + pdrvet = pdrvSup->pdrvet ; + if(pdrvet==NULL) { + printf("No driver entry table is present for %s\n",pname); + continue; + } + if(pdrvet->report==NULL) + printf("Driver: %s No report available\n",pname); + else { + printf("Driver: %s\n",pname); + (*pdrvet->report)(interest_level); + } + } + /* now check devSup reports */ + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + for (pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); + pdevSup; + pdevSup = (devSup *)ellNext(&pdevSup->node)) { + if(!(pdset = pdevSup->pdset)) continue; + if(!(pname = pdevSup->name)) continue; + if(pdrvName!=NULL && *pdrvName!='\0' && (strcmp(pdrvName,pname)!=0)) continue; + if(pdset->report!=NULL) { + printf("Device Support: %s\n",pname); + (*pdset->report)(interest_level); + } + } + } + return 0; +} + +int epicsShareAPI dbhcr(void) +{ + if (!pdbbase) { + printf("No database loaded\n"); + return 0; + } + dbReportDeviceConfig(pdbbase, stdout); + return 0; +} + +static long nameToAddr(const char *pname, DBADDR *paddr) +{ + long status = dbNameToAddr(pname, paddr); + if (status) { + printf("Record '%s' not found\n", pname); + } + return status; +} + +static void printDbAddr(DBADDR *paddr) +{ + short field_type; + short dbr_field_type; + dbFldDes *pdbFldDes = paddr->pfldDes; + + printf("Record Address: %p",(void *)paddr->precord); + printf(" Field Address: %p",paddr->pfield); + printf(" Field Description: %p\n",(void *)pdbFldDes); + printf(" No Elements: %ld\n",paddr->no_elements); + printf(" Record Type: %s\n",pdbFldDes->pdbRecordType->name); + printf(" FieldType: DBF_"); + field_type = paddr->field_type; + if(field_type<0 || field_type>DBR_NOACCESS) + printf(" Illegal = %d\n",field_type); + else + printf("%s\n",dbf[field_type]); + printf(" Field Type: %d\n",paddr->field_type); + printf(" Field Size: %d\n",paddr->field_size); + printf(" Special: %d\n",paddr->special); + printf("DBR Field Type: DBR_"); + dbr_field_type = paddr->dbr_field_type; + if(dbr_field_type==DBR_NOACCESS)dbr_field_type=DBR_ENUM + 1; + if(dbr_field_type<0 || dbr_field_type>(DBR_ENUM+1)) + printf(" Illegal = %d\n",dbr_field_type); + else + printf("%s\n",dbr[dbr_field_type]); +} + + +static void printBuffer( + long status, short dbr_type, void *pbuffer, long reqOptions, + long retOptions, long no_elements, TAB_BUFFER *pMsgBuff, int tab_size) +{ + epicsInt32 val_i32; + epicsUInt32 val_u32; + char *pmsg = pMsgBuff->message; + int i, len; + + if (reqOptions & DBR_STATUS) { + if (retOptions & DBR_STATUS) { + struct dbr_status *pdbr_status = (void *)pbuffer; + + printf("status = %u, severity = %u\n", + pdbr_status->status, + pdbr_status->severity); + } else + printf("status and severity not returned\n"); + pbuffer = (char *)pbuffer + dbr_status_size; + } + if (reqOptions & DBR_UNITS) { + if (retOptions & DBR_UNITS) { + struct dbr_units *pdbr_units = (void *)pbuffer; + + printf("units = \"%s\"\n", + pdbr_units->units); + }else{ + printf("units not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_units_size; + } + if (reqOptions & DBR_PRECISION) { + if (retOptions & DBR_PRECISION){ + struct dbr_precision *pdbr_precision = (void *)pbuffer; + + printf("precision = %ld\n", + pdbr_precision->precision.dp); + }else{ + printf("precision not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_precision_size; + } + if (reqOptions & DBR_TIME) { + if (retOptions & DBR_TIME) { + struct dbr_time *pdbr_time = (void *)pbuffer; + char time_buf[40]; + epicsTimeToStrftime(time_buf, 40, "%Y-%m-%d %H:%M:%S.%09f", + &pdbr_time->time); + printf("time = %s\n", time_buf); + }else{ + printf("time not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_time_size; + } + if (reqOptions & DBR_ENUM_STRS) { + if (retOptions & DBR_ENUM_STRS) { + struct dbr_enumStrs *pdbr_enumStrs = (void *)pbuffer; + + printf("no_strs = %u:\n", + pdbr_enumStrs->no_str); + for (i = 0; i < pdbr_enumStrs->no_str; i++) + printf("\t\"%s\"\n", pdbr_enumStrs->strs[i]); + } else + printf("enum strings not returned\n"); + pbuffer = (char *)pbuffer + dbr_enumStrs_size; + } + if (reqOptions & DBR_GR_LONG) { + if (retOptions & DBR_GR_LONG) { + struct dbr_grLong *pdbr_grLong = (void *)pbuffer; + + printf("grLong: %d .. %d\n", + pdbr_grLong->lower_disp_limit, + pdbr_grLong->upper_disp_limit); + }else{ + printf("DBRgrLong not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_grLong_size; + } + if (reqOptions & DBR_GR_DOUBLE) { + if (retOptions & DBR_GR_DOUBLE) { + struct dbr_grDouble *pdbr_grDouble = (void *)pbuffer; + + printf("grDouble: %g .. %g\n", + pdbr_grDouble->lower_disp_limit, + pdbr_grDouble->upper_disp_limit); + }else{ + printf("DBRgrDouble not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_grDouble_size; + } + if (reqOptions & DBR_CTRL_LONG) { + if (retOptions & DBR_CTRL_LONG){ + struct dbr_ctrlLong *pdbr_ctrlLong = (void *)pbuffer; + + printf("ctrlLong: %d .. %d\n", + pdbr_ctrlLong->lower_ctrl_limit, + pdbr_ctrlLong->upper_ctrl_limit); + }else{ + printf("DBRctrlLong not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_ctrlLong_size; + } + if (reqOptions & DBR_CTRL_DOUBLE) { + if (retOptions & DBR_CTRL_DOUBLE) { + struct dbr_ctrlDouble *pdbr_ctrlDouble = (void *)pbuffer; + + printf("ctrlDouble: %g .. %g\n", + pdbr_ctrlDouble->lower_ctrl_limit, + pdbr_ctrlDouble->upper_ctrl_limit); + }else{ + printf("DBRctrlDouble not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_ctrlDouble_size; + } + if (reqOptions & DBR_AL_LONG) { + if (retOptions & DBR_AL_LONG) { + struct dbr_alLong *pdbr_alLong = (void *)pbuffer; + + printf("alLong: %d < %d .. %d < %d\n", + pdbr_alLong->lower_alarm_limit, + pdbr_alLong->lower_warning_limit, + pdbr_alLong->upper_warning_limit, + pdbr_alLong->upper_alarm_limit); + }else{ + printf("DBRalLong not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_alLong_size; + } + if (reqOptions & DBR_AL_DOUBLE) { + if (retOptions & DBR_AL_DOUBLE) { + struct dbr_alDouble *pdbr_alDouble = (void *)pbuffer; + + printf("alDouble: %g < %g .. %g < %g\n", + pdbr_alDouble->lower_alarm_limit, + pdbr_alDouble->lower_warning_limit, + pdbr_alDouble->upper_warning_limit, + pdbr_alDouble->upper_alarm_limit); + }else{ + printf("DBRalDouble not returned\n"); + } + pbuffer = (char *)pbuffer + dbr_alDouble_size; + } + /* Now print values */ + if (no_elements == 0) return; + switch (dbr_type) { + case (DBR_STRING): + if (no_elements == 1) + sprintf(pmsg, "DBR_STRING: "); + else + sprintf(pmsg, "DBR_STRING[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, "DBR_STRING: failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for(i=0; i 0) { + sprintf(pmsg, " \"%s\"", (char *)pbuffer); + dbpr_msgOut(pMsgBuff, tab_size); + } + pbuffer = (char *)pbuffer + MAX_STRING_SIZE; + } + break; + case (DBR_CHAR): + if (no_elements == 1) + sprintf(pmsg, "DBR_CHAR: "); + else + sprintf(pmsg, "DBR_CHAR[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + if (no_elements == 1) { + val_i32 = *(epicsInt8 *) pbuffer; + sprintf(pmsg, "%-9d 0x%-9x", val_i32, val_i32); + dbpr_msgOut(pMsgBuff, tab_size); + } else { + for (i = 0; i < no_elements; i+= MAXLINE - 5) { + sprintf(pmsg, " \"%.*s\"", MAXLINE - 5, (char *)pbuffer + i); + if (i + MAXLINE - 5 < no_elements) strcat(pmsg, " +"); + dbpr_msgOut(pMsgBuff, tab_size); + } + } + break; + case (DBR_UCHAR): + if (no_elements == 1) + sprintf(pmsg, "DBR_UCHAR: "); + else + sprintf(pmsg, "DBR_UCHAR[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + val_u32 = *(epicsUInt8 *) pbuffer; + sprintf(pmsg, "%-9u 0x%-9x", val_u32, val_u32); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsUInt8); + } + break; + case (DBR_SHORT): + if (no_elements == 1) + sprintf(pmsg, "DBR_SHORT: "); + else + sprintf(pmsg, "DBR_SHORT[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + val_i32 = *(epicsInt16 *) pbuffer; + sprintf(pmsg, "%-9d 0x%-9x", val_i32, val_i32); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsInt16); + } + break; + case (DBR_USHORT): + if (no_elements == 1) + sprintf(pmsg, "DBR_USHORT: "); + else + sprintf(pmsg, "DBR_USHORT[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + val_u32 = *(epicsUInt16 *) pbuffer; + sprintf(pmsg, "%-9u 0x%-9x", val_u32, val_u32); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsUInt16); + } + break; + case (DBR_LONG): + if (no_elements == 1) + sprintf(pmsg, "DBR_LONG: "); + else + sprintf(pmsg, "DBR_LONG[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + val_i32 = *(epicsInt32 *) pbuffer; + sprintf(pmsg, "%-9d 0x%-9x", val_i32, val_i32); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsInt32); + } + break; + case (DBR_ULONG): + if (no_elements == 1) + sprintf(pmsg, "DBR_ULONG: "); + else + sprintf(pmsg, "DBR_ULONG[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + val_u32 = *(epicsUInt32 *) pbuffer; + sprintf(pmsg, "%-9u 0x%-9x", val_u32, val_u32); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsUInt32); + } + break; + case (DBR_FLOAT): + if (no_elements == 1) + sprintf(pmsg, "DBR_FLOAT: "); + else + sprintf(pmsg, "DBR_FLOAT[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + sprintf(pmsg, "%-13.6g", *((epicsFloat32 *) pbuffer)); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsFloat32); + } + break; + case (DBR_DOUBLE): + if (no_elements == 1) + sprintf(pmsg, "DBR_DOUBLE: "); + else + sprintf(pmsg, "DBR_DOUBLE[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + sprintf(pmsg, "%-13.6g", *((epicsFloat64 *) pbuffer)); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsFloat64); + } + break; + case (DBR_ENUM): + if (no_elements == 1) + sprintf(pmsg, "DBR_ENUM: "); + else + sprintf(pmsg, "DBR_ENUM[%ld]: ", no_elements); + dbpr_msgOut(pMsgBuff, tab_size); + if (status != 0) { + sprintf(pmsg, " failed."); + dbpr_msgOut(pMsgBuff, tab_size); + break; + } + for (i = 0; i < no_elements; i++) { + sprintf(pmsg, "%-9u", *((epicsEnum16 *) pbuffer)); + dbpr_msgOut(pMsgBuff, tab_size); + pbuffer = (char *)pbuffer + sizeof(epicsEnum16); + } + break; + default: + printf(" illegal request type."); + break; + } + dbpr_msg_flush(pMsgBuff, tab_size); + return; +} + +static int dbpr_report( + const char *pname,DBADDR *paddr,int interest_level, + TAB_BUFFER *pMsgBuff,int tab_size) +{ + + char *pmsg; + dbFldDes *pdbFldDes = paddr->pfldDes; + dbRecordType *pdbRecordType = pdbFldDes->pdbRecordType; + short n2; + void *pfield; + char *pfield_name; + char *pfield_value; + DBENTRY dbentry; + DBENTRY *pdbentry = &dbentry; + long status; + + dbInitEntry(pdbbase,pdbentry); + status = dbFindRecord(pdbentry,pname); + if(status) { + errMessage(status,pname); + return(-1); + } + pmsg = pMsgBuff->message; + for (n2 = 0; n2 <= pdbRecordType->no_fields - 1; n2++) { + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->sortFldInd[n2]]; + pfield_name = pdbFldDes->name; + pfield = ((char *)paddr->precord) + pdbFldDes->offset; + if (pdbFldDes->interest > interest_level ) + continue; + switch (pdbFldDes->field_type) { + case DBF_STRING: + case DBF_USHORT: + case DBF_ENUM: + case DBF_FLOAT: + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_LONG: + case DBF_ULONG: + case DBF_DOUBLE: + case DBF_MENU: + case DBF_DEVICE: + status = dbFindField(pdbentry,pfield_name); + pfield_value = dbGetString(pdbentry); + sprintf(pmsg, "%s: %s", pfield_name, + (pfield_value ? pfield_value : "")); + dbpr_msgOut(pMsgBuff, tab_size); + break; + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: { + DBLINK *plink = (DBLINK *)pfield; + int ind; + + status = dbFindField(pdbentry,pfield_name); + for(ind=0; indtype) break; + } + if(ind>=LINK_NTYPES) { + sprintf(pmsg,"%s: Illegal Link Type", pfield_name); + } else { + sprintf(pmsg,"%s:%s %s", pfield_name, + pamaplinkType[ind].strvalue,dbGetString(pdbentry)); + } + dbpr_msgOut(pMsgBuff, tab_size); + } + break; + case DBF_NOACCESS: + if (pfield == (void *)&paddr->precord->time) { + /* Special for the TIME field, make it human-readable */ + char time_buf[40]; + epicsTimeToStrftime(time_buf, 40, "%Y-%m-%d %H:%M:%S.%09f", + &paddr->precord->time); + sprintf(pmsg, "%s: %s", pfield_name, time_buf); + dbpr_msgOut(pMsgBuff, tab_size); + } else if (pdbFldDes->size == sizeof(void *) && + strchr(pdbFldDes->extra, '*')) { + /* Special for pointers, needed on little-endian CPUs */ + sprintf(pmsg, "%s: %p", pfield_name, *(void **)pfield); + dbpr_msgOut(pMsgBuff, tab_size); + } else { /* just print field as hex bytes */ + unsigned char *pchar = (unsigned char *)pfield; + char temp_buf[61]; + char *ptemp_buf = &temp_buf[0]; + short n = pdbFldDes->size; + short i; + unsigned int value; + + if (n > sizeof(temp_buf)/3) n = sizeof(temp_buf)/3; + for (i=0; imessage; + static int last_tabsize; + + if (!((tab_size == 10) || (tab_size == 20))) { + printf("tab_size not 10 or 20 - dbpr_msgOut()\n"); + return; + } + /* init if first time */ + if (!(pMsgBuff->pNext)) + dbpr_init_msg(pMsgBuff, tab_size); + if (tab_size != last_tabsize) + pMsgBuff->pNexTab = pMsgBuff->out_buff + tab_size; + + last_tabsize = tab_size; + /* flush output if NULL string command */ + if (*pmsg == 0) { + dbpr_msg_flush(pMsgBuff, tab_size); + return; + } + /* truncate if too long */ + len = strlen(pmsg); + if (len > MAXLINE) { + err = 1; + len = MAXLINE; + } + + dbpr_insert_msg(pMsgBuff, len, tab_size); + + /* warn if msg gt 80 */ + if (err == 1) { + len = strlen(pmsg); + sprintf(pmsg, + "dbpr_msgOut: ERROR - msg length=%d limit=%d ", + len, + MAXLINE); + dbpr_insert_msg(pMsgBuff, len, tab_size); + return; + } + return; +} + +static void dbpr_init_msg(TAB_BUFFER *pMsgBuff,int tab_size) +{ + pMsgBuff->pNext = pMsgBuff->out_buff; + pMsgBuff->pLast = pMsgBuff->out_buff + MAXLINE; + pMsgBuff->pNexTab = pMsgBuff->out_buff + tab_size; + return; +} + +static void dbpr_insert_msg(TAB_BUFFER *pMsgBuff,int len,int tab_size) +{ + int current_len; + int n; + int tot_line; + char *pmsg = pMsgBuff->message; + current_len = strlen(pMsgBuff->out_buff); + tot_line = current_len + len; + + /* flush buffer if overflow would occor */ + if (tot_line > MAXLINE) + dbpr_msg_flush(pMsgBuff, tab_size); + + /* append message to buffer */ + n = 0; + while ((*pmsg) && (n < len)) { + *pMsgBuff->pNext++ = *pmsg++; + + /* position to next tab stop */ + if (*(pMsgBuff->pNexTab - 1) != '\0') + pMsgBuff->pNexTab = pMsgBuff->pNexTab + tab_size; + n++; + } + + /* fill spaces to next tab stop */ + while (*(pMsgBuff->pNexTab - 1) != ' ' + && pMsgBuff->pNext < pMsgBuff->pLast) { + *pMsgBuff->pNext++ = ' '; + } + return; + +} + +static void dbpr_msg_flush(TAB_BUFFER *pMsgBuff,int tab_size) +{ + /* skip print if buffer empty */ + if (pMsgBuff->pNext != pMsgBuff->out_buff) + printf("%s\n", pMsgBuff->out_buff); + memset(pMsgBuff->out_buff,'\0', (int) MAXLINE + 1); + pMsgBuff->pNext = pMsgBuff->out_buff; + pMsgBuff->pNexTab = pMsgBuff->out_buff + tab_size; + return; +} diff --git a/src/db/dbTest.h b/src/db/dbTest.h new file mode 100644 index 000000000..231403a62 --- /dev/null +++ b/src/db/dbTest.h @@ -0,0 +1,52 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_dbTest_H +#define INC_dbTest_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*dbAddr info */ +epicsShareFunc long epicsShareAPI dba(const char *pname); +/*list records*/ +epicsShareFunc long epicsShareAPI dbl( + const char *precordTypename,const char *fields); +/*list number of records of each type*/ +epicsShareFunc long epicsShareAPI dbnr(int verbose); +/* list aliases */ +epicsShareFunc long epicsShareAPI dbla(const char *pmask); +/*list records with mask*/ +epicsShareFunc long epicsShareAPI dbgrep(const char *pmask); +/*get field value*/ +epicsShareFunc long epicsShareAPI dbgf(const char *pname); +/*put field value*/ +epicsShareFunc long epicsShareAPI dbpf(const char *pname,const char *pvalue); +/*print record*/ +epicsShareFunc long epicsShareAPI dbpr(const char *pname,int interest_level); +/*test record*/ +epicsShareFunc long epicsShareAPI dbtr(const char *pname); +/*test get field*/ +epicsShareFunc long epicsShareAPI dbtgf(const char *pname); +/*test put field*/ +epicsShareFunc long epicsShareAPI dbtpf(const char *pname,const char *pvalue); +/*I/O report */ +epicsShareFunc long epicsShareAPI dbior( + const char *pdrvName,int interest_level); +/*Hardware Configuration Report*/ +epicsShareFunc int epicsShareAPI dbhcr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_dbTest_H */ diff --git a/src/db/db_access.c b/src/db/db_access.c new file mode 100644 index 000000000..a22cffc52 --- /dev/null +++ b/src/db/db_access.c @@ -0,0 +1,1032 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Revision-Id: anj@aps.anl.gov-20101027182656-gkrwdp20hdmf08nj */ + +/* Interface between old database access and new + * + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include +#include +#include +#include + +#include "epicsConvert.h" +#include "dbDefs.h" +#include "errlog.h" +#include "ellLib.h" +#include "epicsTime.h" +#include "dbStaticLib.h" +#include "dbBase.h" +#include "dbCommon.h" +#include "errMdef.h" +#include "recSup.h" +#include "alarm.h" +#define db_accessHFORdb_accessC +#include "db_access.h" +#undef db_accessHFORdb_accessC +#define epicsExportSharedSymbols +#include "dbNotify.h" +#include "dbAccessDefs.h" +#include "dbEvent.h" +#include "db_access_routines.h" + +#ifndef NULL +#define NULL 0 +#endif + + +#define oldDBF_STRING 0 +#define oldDBF_INT 1 +#define oldDBF_SHORT 1 +#define oldDBF_FLOAT 2 +#define oldDBF_ENUM 3 +#define oldDBF_CHAR 4 +#define oldDBF_LONG 5 +#define oldDBF_DOUBLE 6 + +/* data request buffer types */ +#define oldDBR_STRING oldDBF_STRING +#define oldDBR_INT oldDBF_INT +#define oldDBR_SHORT oldDBF_INT +#define oldDBR_FLOAT oldDBF_FLOAT +#define oldDBR_ENUM oldDBF_ENUM +#define oldDBR_CHAR oldDBF_CHAR +#define oldDBR_LONG oldDBF_LONG +#define oldDBR_DOUBLE oldDBF_DOUBLE +#define oldDBR_STS_STRING 7 +#define oldDBR_STS_INT 8 +#define oldDBR_STS_SHORT 8 +#define oldDBR_STS_FLOAT 9 +#define oldDBR_STS_ENUM 10 +#define oldDBR_STS_CHAR 11 +#define oldDBR_STS_LONG 12 +#define oldDBR_STS_DOUBLE 13 +#define oldDBR_TIME_STRING 14 +#define oldDBR_TIME_INT 15 +#define oldDBR_TIME_SHORT 15 +#define oldDBR_TIME_FLOAT 16 +#define oldDBR_TIME_ENUM 17 +#define oldDBR_TIME_CHAR 18 +#define oldDBR_TIME_LONG 19 +#define oldDBR_TIME_DOUBLE 20 +#define oldDBR_GR_STRING 21 +#define oldDBR_GR_INT 22 +#define oldDBR_GR_SHORT 22 +#define oldDBR_GR_FLOAT 23 +#define oldDBR_GR_ENUM 24 +#define oldDBR_GR_CHAR 25 +#define oldDBR_GR_LONG 26 +#define oldDBR_GR_DOUBLE 27 +#define oldDBR_CTRL_STRING 28 +#define oldDBR_CTRL_INT 29 +#define oldDBR_CTRL_SHORT 29 +#define oldDBR_CTRL_FLOAT 30 +#define oldDBR_CTRL_ENUM 31 +#define oldDBR_CTRL_CHAR 32 +#define oldDBR_CTRL_LONG 33 +#define oldDBR_CTRL_DOUBLE 34 +#define oldDBR_PUT_ACKT oldDBR_CTRL_DOUBLE + 1 +#define oldDBR_PUT_ACKS oldDBR_PUT_ACKT + 1 +#define oldDBR_STSACK_STRING oldDBR_PUT_ACKS + 1 +#define oldDBR_CLASS_NAME oldDBR_STSACK_STRING + 1 + +/*Following is defined in db_convert.h*/ +extern unsigned short dbDBRnewToDBRold[DBR_ENUM+1]; + +typedef char DBSTRING[MAX_STRING_SIZE]; + + +#ifndef MAX_STRING_SIZE +#define MAX_STRING_SIZE 40 +#endif + +/* + * DB_PROCESS + * + * process database records + */ +void db_process(struct dbAddr *paddr) +{ + long status = dbProcess(paddr->precord); + + if (status) errMessage(status, "db_process failed"); +} + +/* + * DB_NAME_TO_ADDR + */ +int epicsShareAPI db_name_to_addr(const char *pname, struct dbAddr *paddr) +{ + long status; + short ftype; + + status = dbNameToAddr(pname, paddr); + if (!status) { + ftype = paddr->dbr_field_type; + if (INVALID_DB_REQ(ftype)) { + errlogPrintf("%s dbNameToAddr failed\n", pname); + return -2; + } + paddr->dbr_field_type = dbDBRnewToDBRold[ftype]; + return 0; + } + else + return -1; +} + +int epicsShareAPI db_get_field(struct dbAddr *paddr, + int buffer_type, void *pbuffer, int no_elements, void *pfl) +{ + long nRequest = no_elements; + int result = db_get_field_and_count( + paddr, buffer_type, pbuffer, &nRequest, pfl); + if (nRequest < no_elements) { + /* The database request returned fewer elements than requested, so + * fill the remainder of the buffer with zeros. + */ + int val_size = dbr_value_size[buffer_type]; + int offset = dbr_size[buffer_type] + (nRequest - 1) * val_size; + int nbytes = (no_elements - nRequest) * val_size; + + memset((char *)pbuffer + offset, 0, nbytes); + } + return result; +} + +/* Performs the work of the public db_get_field API, but also returns the number + * of elements actually copied to the buffer. The caller is responsible for + * zeroing the remaining part of the buffer. */ +int epicsShareAPI db_get_field_and_count( + struct dbAddr *paddr, int buffer_type, + void *pbuffer, long *nRequest, void *pfl) +{ + long status; + long options; + long i; + long zero = 0; + + /* The order of the DBR* elements in the "newSt" structures below is + * very important and must correspond to the order of processing + * in the dbAccess.c dbGet() and getOptions() routines. + */ + + switch(buffer_type) { + case(oldDBR_STRING): + status = dbGetField(paddr, DBR_STRING, pbuffer, &zero, nRequest, pfl); + break; +/* case(oldDBR_INT): */ + case(oldDBR_SHORT): + status = dbGetField(paddr, DBR_SHORT, pbuffer, &zero, nRequest, pfl); + break; + case(oldDBR_FLOAT): + status = dbGetField(paddr, DBR_FLOAT, pbuffer, &zero, nRequest, pfl); + break; + case(oldDBR_ENUM): + status = dbGetField(paddr, DBR_ENUM, pbuffer, &zero, nRequest, pfl); + break; + case(oldDBR_CHAR): + status = dbGetField(paddr, DBR_CHAR, pbuffer, &zero, nRequest, pfl); + break; + case(oldDBR_LONG): + status = dbGetField(paddr, DBR_LONG, pbuffer, &zero, nRequest, pfl); + break; + case(oldDBR_DOUBLE): + status = dbGetField(paddr, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl); + break; + + case(oldDBR_STS_STRING): + case(oldDBR_GR_STRING): + case(oldDBR_CTRL_STRING): + { + struct dbr_sts_string *pold = (struct dbr_sts_string *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + status = dbGetField(paddr, DBR_STRING, pold->value, &zero, + nRequest, pfl); + } + break; +/* case(oldDBR_STS_INT): */ + case(oldDBR_STS_SHORT): + { + struct dbr_sts_int *pold = (struct dbr_sts_int *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + status = dbGetField(paddr, DBR_SHORT, &pold->value, &zero, + nRequest, pfl); + } + break; + case(oldDBR_STS_FLOAT): + { + struct dbr_sts_float *pold = (struct dbr_sts_float *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + status = dbGetField(paddr, DBR_FLOAT, &pold->value, &zero, + nRequest, pfl); + } + break; + case(oldDBR_STS_ENUM): + { + struct dbr_sts_enum *pold = (struct dbr_sts_enum *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + status = dbGetField(paddr, DBR_ENUM, &pold->value, &zero, + nRequest, pfl); + } + break; + case(oldDBR_STS_CHAR): + { + struct dbr_sts_char *pold = (struct dbr_sts_char *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + status = dbGetField(paddr, DBR_UCHAR, &pold->value, &zero, + nRequest, pfl); + } + break; + case(oldDBR_STS_LONG): + { + struct dbr_sts_long *pold = (struct dbr_sts_long *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + status = dbGetField(paddr, DBR_LONG, &pold->value, &zero, + nRequest, pfl); + } + break; + case(oldDBR_STS_DOUBLE): + { + struct dbr_sts_double *pold = (struct dbr_sts_double *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + options = 0; + status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + nRequest, pfl); + } + break; + + case(oldDBR_TIME_STRING): + { + struct dbr_time_string *pold = (struct dbr_time_string *)pbuffer; + struct { + DBRstatus + DBRtime + } newSt; + + options = DBR_STATUS | DBR_TIME; + status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->stamp = newSt.time; /* structure copy */ + options = 0; + status = dbGetField(paddr, DBR_STRING, pold->value, &options, + nRequest, pfl); + } + break; +/* case(oldDBR_TIME_INT): */ + case(oldDBR_TIME_SHORT): + { + struct dbr_time_short *pold = (struct dbr_time_short *)pbuffer; + struct { + DBRstatus + DBRtime + } newSt; + + options = DBR_STATUS | DBR_TIME; + status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->stamp = newSt.time; /* structure copy */ + options = 0; + status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_TIME_FLOAT): + { + struct dbr_time_float *pold = (struct dbr_time_float *)pbuffer; + struct { + DBRstatus + DBRtime + } newSt; + + options = DBR_STATUS | DBR_TIME; + status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->stamp = newSt.time; /* structure copy */ + options = 0; + status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_TIME_ENUM): + { + struct dbr_time_enum *pold = (struct dbr_time_enum *)pbuffer; + struct { + DBRstatus + DBRtime + } newSt; + + options = DBR_STATUS | DBR_TIME; + status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->stamp = newSt.time; /* structure copy */ + options = 0; + status = dbGetField(paddr, DBR_ENUM, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_TIME_CHAR): + { + struct dbr_time_char *pold = (struct dbr_time_char *)pbuffer; + struct { + DBRstatus + DBRtime + } newSt; + + options = DBR_STATUS | DBR_TIME; + status = dbGetField(paddr, DBR_CHAR, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->stamp = newSt.time; /* structure copy */ + options = 0; + status = dbGetField(paddr, DBR_CHAR, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_TIME_LONG): + { + struct dbr_time_long *pold = (struct dbr_time_long *)pbuffer; + struct { + DBRstatus + DBRtime + } newSt; + + options = DBR_STATUS | DBR_TIME; + status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->stamp = newSt.time; /* structure copy */ + options = 0; + status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_TIME_DOUBLE): + { + struct dbr_time_double *pold = (struct dbr_time_double *)pbuffer; + struct { + DBRstatus + DBRtime + } newSt; + + options = DBR_STATUS | DBR_TIME; + status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->stamp = newSt.time; /* structure copy */ + options = 0; + status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + nRequest, pfl); + } + break; + +/* case(oldDBR_GR_STRING): NOT IMPLEMENTED - use DBR_STS_STRING */ +/* case(oldDBR_GR_INT): */ + case(oldDBR_GR_SHORT): + { + struct dbr_gr_int *pold = (struct dbr_gr_int *)pbuffer; + struct { + DBRstatus + DBRunits + DBRgrLong + DBRalLong + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; + status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + options = 0; + status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_GR_FLOAT): + { + struct dbr_gr_float *pold = (struct dbr_gr_float *)pbuffer; + struct { + DBRstatus + DBRunits + DBRprecision + DBRgrDouble + DBRalDouble + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | + DBR_AL_DOUBLE; + status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->precision = newSt.precision.dp; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = epicsConvertDoubleToFloat(newSt.upper_disp_limit); + pold->lower_disp_limit = epicsConvertDoubleToFloat(newSt.lower_disp_limit); + pold->upper_alarm_limit = epicsConvertDoubleToFloat(newSt.upper_alarm_limit); + pold->lower_alarm_limit = epicsConvertDoubleToFloat(newSt.lower_alarm_limit); + pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit); + pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit); + options = 0; + status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + nRequest, pfl); + } + break; +/* case(oldDBR_GR_ENUM): see oldDBR_CTRL_ENUM */ + case(oldDBR_GR_CHAR): + { + struct dbr_gr_char *pold = (struct dbr_gr_char *)pbuffer; + struct { + DBRstatus + DBRunits + DBRgrLong + DBRalLong + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; + status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + options = 0; + status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_GR_LONG): + { + struct dbr_gr_long *pold = (struct dbr_gr_long *)pbuffer; + struct { + DBRstatus + DBRunits + DBRgrLong + DBRalLong + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; + status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + options = 0; + status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_GR_DOUBLE): + { + struct dbr_gr_double *pold = (struct dbr_gr_double *)pbuffer; + struct { + DBRstatus + DBRunits + DBRprecision + DBRgrDouble + DBRalDouble + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | + DBR_AL_DOUBLE; + status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->precision = newSt.precision.dp; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + options = 0; + status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + nRequest, pfl); + } + break; + +/* case(oldDBR_CTRL_INT): */ + case(oldDBR_CTRL_SHORT): + { + struct dbr_ctrl_int *pold = (struct dbr_ctrl_int *)pbuffer; + struct { + DBRstatus + DBRunits + DBRgrLong + DBRctrlLong + DBRalLong + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | + DBR_AL_LONG; + status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + pold->upper_ctrl_limit = newSt.upper_ctrl_limit; + pold->lower_ctrl_limit = newSt.lower_ctrl_limit; + options = 0; + status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_CTRL_FLOAT): + { + struct dbr_ctrl_float *pold = (struct dbr_ctrl_float *)pbuffer; + struct { + DBRstatus + DBRunits + DBRprecision + DBRgrDouble + DBRctrlDouble + DBRalDouble + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | + DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; + status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->precision = newSt.precision.dp; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = epicsConvertDoubleToFloat(newSt.upper_disp_limit); + pold->lower_disp_limit = epicsConvertDoubleToFloat(newSt.lower_disp_limit); + pold->upper_alarm_limit = epicsConvertDoubleToFloat(newSt.upper_alarm_limit); + pold->lower_alarm_limit = epicsConvertDoubleToFloat(newSt.lower_alarm_limit); + pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit); + pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit); + pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit); + pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit); + options = 0; + status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_GR_ENUM): + case(oldDBR_CTRL_ENUM): + { + struct dbr_ctrl_enum *pold = (struct dbr_ctrl_enum *)pbuffer; + struct { + DBRstatus + DBRenumStrs + } newSt; + short no_str; + + memset(pold, '\0', sizeof(struct dbr_ctrl_enum)); + /* first get status and severity */ + options = DBR_STATUS | DBR_ENUM_STRS; + status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + no_str = newSt.no_str; + if (no_str>16) no_str=16; + pold->no_str = no_str; + for (i = 0; i < no_str; i++) + strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i])); + /*now get values*/ + options = 0; + status = dbGetField(paddr, DBR_ENUM, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_CTRL_CHAR): + { + struct dbr_ctrl_char *pold = (struct dbr_ctrl_char *)pbuffer; + struct { + DBRstatus + DBRunits + DBRgrLong + DBRctrlLong + DBRalLong + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | + DBR_AL_LONG; + status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + pold->upper_ctrl_limit = newSt.upper_ctrl_limit; + pold->lower_ctrl_limit = newSt.lower_ctrl_limit; + options = 0; + status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_CTRL_LONG): + { + struct dbr_ctrl_long *pold = (struct dbr_ctrl_long *)pbuffer; + struct { + DBRstatus + DBRunits + DBRgrLong + DBRctrlLong + DBRalLong + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | + DBR_AL_LONG; + status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + pold->upper_ctrl_limit = newSt.upper_ctrl_limit; + pold->lower_ctrl_limit = newSt.lower_ctrl_limit; + options = 0; + status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + nRequest, pfl); + } + break; + case(oldDBR_CTRL_DOUBLE): + { + struct dbr_ctrl_double *pold = (struct dbr_ctrl_double *)pbuffer; + struct { + DBRstatus + DBRunits + DBRprecision + DBRgrDouble + DBRctrlDouble + DBRalDouble + } newSt; + + options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | + DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; + status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->precision = newSt.precision.dp; + strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); + pold->units[MAX_UNITS_SIZE-1] = '\0'; + pold->upper_disp_limit = newSt.upper_disp_limit; + pold->lower_disp_limit = newSt.lower_disp_limit; + pold->upper_alarm_limit = newSt.upper_alarm_limit; + pold->upper_warning_limit = newSt.upper_warning_limit; + pold->lower_warning_limit = newSt.lower_warning_limit; + pold->lower_alarm_limit = newSt.lower_alarm_limit; + pold->upper_ctrl_limit = newSt.upper_ctrl_limit; + pold->lower_ctrl_limit = newSt.lower_ctrl_limit; + options = 0; + status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + nRequest, pfl); + } + break; + + case(oldDBR_STSACK_STRING): + { + struct dbr_stsack_string *pold = (struct dbr_stsack_string *)pbuffer; + struct { + DBRstatus + } newSt; + + options = DBR_STATUS; + status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + pold->status = newSt.status; + pold->severity = newSt.severity; + pold->ackt = newSt.ackt; + pold->acks = newSt.acks; + options = 0; + status = dbGetField(paddr, DBR_STRING, pold->value, + &options, nRequest, pfl); + } + break; + + case(oldDBR_CLASS_NAME): + { + DBENTRY dbEntry; + char *name = 0; + char *pto = (char *)pbuffer; + + if (!pdbbase) { + status = S_db_notFound; + break; + } + dbInitEntry(pdbbase, &dbEntry); + status = dbFindRecord(&dbEntry, paddr->precord->name); + if (!status) name = dbGetRecordTypeName(&dbEntry); + dbFinishEntry(&dbEntry); + if (status) break; + if (!name) { + status = S_dbLib_recordTypeNotFound; + break; + } + pto[MAX_STRING_SIZE-1] = 0; + strncpy(pto, name, MAX_STRING_SIZE-1); + } + break; + default: + return -1; + } + if (status) return -1; + return 0; +} + +/* DB_PUT_FIELD put a field and convert it to the desired type */ + +int epicsShareAPI db_put_field(struct dbAddr *paddr, int src_type, + const void *psrc, int no_elements) +{ + long status; + + switch(src_type) { + case(oldDBR_STRING): + status = dbPutField(paddr, DBR_STRING, psrc, no_elements); + break; +/* case(oldDBR_INT): */ + case(oldDBR_SHORT): + status = dbPutField(paddr, DBR_SHORT, psrc, no_elements); + break; + case(oldDBR_FLOAT): + status = dbPutField(paddr, DBR_FLOAT, psrc, no_elements); + break; + case(oldDBR_ENUM): + status = dbPutField(paddr, DBR_ENUM, psrc, no_elements); + break; + case(oldDBR_CHAR): + status = dbPutField(paddr, DBR_UCHAR, psrc, no_elements); + break; + case(oldDBR_LONG): + status = dbPutField(paddr, DBR_LONG, psrc, no_elements); + break; + case(oldDBR_DOUBLE): + status = dbPutField(paddr, DBR_DOUBLE, psrc, no_elements); + break; + + case(oldDBR_STS_STRING): + status = dbPutField(paddr, DBR_STRING, + ((const struct dbr_sts_string *)psrc)->value, no_elements); + break; +/* case(oldDBR_STS_INT): */ + case(oldDBR_STS_SHORT): + status = dbPutField(paddr, DBR_SHORT, + &((const struct dbr_sts_short *)psrc)->value, no_elements); + break; + case(oldDBR_STS_FLOAT): + status = dbPutField(paddr, DBR_FLOAT, + &((const struct dbr_sts_float *)psrc)->value, no_elements); + break; + case(oldDBR_STS_ENUM): + status = dbPutField(paddr, DBR_ENUM, + &((const struct dbr_sts_enum *)psrc)->value, no_elements); + break; + case(oldDBR_STS_CHAR): + status = dbPutField(paddr, DBR_UCHAR, + &((const struct dbr_sts_char *)psrc)->value, no_elements); + break; + case(oldDBR_STS_LONG): + status = dbPutField(paddr, DBR_LONG, + &((const struct dbr_sts_long *)psrc)->value, no_elements); + break; + case(oldDBR_STS_DOUBLE): + status = dbPutField(paddr, DBR_DOUBLE, + &((const struct dbr_sts_double *)psrc)->value, no_elements); + break; + + case(oldDBR_TIME_STRING): + status = dbPutField(paddr, DBR_TIME, + ((const struct dbr_time_string *)psrc)->value, no_elements); + break; +/* case(oldDBR_TIME_INT): */ + case(oldDBR_TIME_SHORT): + status = dbPutField(paddr, DBR_SHORT, + &((const struct dbr_time_short *)psrc)->value, no_elements); + break; + case(oldDBR_TIME_FLOAT): + status = dbPutField(paddr, DBR_FLOAT, + &((const struct dbr_time_float *)psrc)->value, no_elements); + break; + case(oldDBR_TIME_ENUM): + status = dbPutField(paddr, DBR_ENUM, + &((const struct dbr_time_enum *)psrc)->value, no_elements); + break; + case(oldDBR_TIME_CHAR): + status = dbPutField(paddr, DBR_UCHAR, + &((const struct dbr_time_char *)psrc)->value, no_elements); + break; + case(oldDBR_TIME_LONG): + status = dbPutField(paddr, DBR_LONG, + &((const struct dbr_time_long *)psrc)->value, no_elements); + break; + case(oldDBR_TIME_DOUBLE): + status = dbPutField(paddr, DBR_DOUBLE, + &((const struct dbr_time_double *)psrc)->value, no_elements); + break; + + case(oldDBR_GR_STRING): + /* no struct dbr_gr_string - use dbr_sts_string instead */ + status = dbPutField(paddr, DBR_STRING, + ((const struct dbr_sts_string *)psrc)->value, no_elements); + break; +/* case(oldDBR_GR_INT): */ + case(oldDBR_GR_SHORT): + status = dbPutField(paddr, DBR_SHORT, + &((const struct dbr_gr_short *)psrc)->value, no_elements); + break; + case(oldDBR_GR_FLOAT): + status = dbPutField(paddr, DBR_FLOAT, + &((const struct dbr_gr_float *)psrc)->value, no_elements); + break; + case(oldDBR_GR_ENUM): + status = dbPutField(paddr, DBR_ENUM, + &((const struct dbr_gr_enum *)psrc)->value, no_elements); + break; + case(oldDBR_GR_CHAR): + status = dbPutField(paddr, DBR_UCHAR, + &((const struct dbr_gr_char *)psrc)->value, no_elements); + break; + case(oldDBR_GR_LONG): + status = dbPutField(paddr, DBR_LONG, + &((const struct dbr_gr_long *)psrc)->value, no_elements); + break; + case(oldDBR_GR_DOUBLE): + status = dbPutField(paddr, DBR_DOUBLE, + &((const struct dbr_gr_double *)psrc)->value, no_elements); + break; + + case(oldDBR_CTRL_STRING): + /* no struct dbr_ctrl_string - use dbr_sts_string instead */ + status = dbPutField(paddr, DBR_STRING, + ((const struct dbr_sts_string *)psrc)->value, no_elements); + break; +/* case(oldDBR_CTRL_INT): */ + case(oldDBR_CTRL_SHORT): + status = dbPutField(paddr, DBR_SHORT, + &((const struct dbr_ctrl_short *)psrc)->value, no_elements); + break; + case(oldDBR_CTRL_FLOAT): + status = dbPutField(paddr, DBR_FLOAT, + &((const struct dbr_ctrl_float *)psrc)->value, no_elements); + break; + case(oldDBR_CTRL_ENUM): + status = dbPutField(paddr, DBR_ENUM, + &((const struct dbr_ctrl_enum *)psrc)->value, no_elements); + break; + case(oldDBR_CTRL_CHAR): + status = dbPutField(paddr, DBR_UCHAR, + &((const struct dbr_ctrl_char *)psrc)->value, no_elements); + break; + case(oldDBR_CTRL_LONG): + status = dbPutField(paddr, DBR_LONG, + &((const struct dbr_ctrl_long *)psrc)->value, no_elements); + break; + case(oldDBR_CTRL_DOUBLE): + status = dbPutField(paddr, DBR_DOUBLE, + &((const struct dbr_ctrl_double *)psrc)->value, no_elements); + break; + + case(oldDBR_PUT_ACKT): + status = dbPutField(paddr, DBR_PUT_ACKT, psrc, no_elements); + break; + case(oldDBR_PUT_ACKS): + status = dbPutField(paddr, DBR_PUT_ACKS, psrc, no_elements); + break; + + default: + return -1; + } + if (status) return -1; + return 0; +} + + +epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype) +{ + switch(oldtype) { + case(oldDBR_STRING): + ppn->dbrType = DBR_STRING; + break; +/* case(oldDBR_INT): */ + case(oldDBR_SHORT): + ppn->dbrType = DBR_SHORT; + break; + case(oldDBR_FLOAT): + ppn->dbrType = DBR_FLOAT; + break; + case(oldDBR_ENUM): + ppn->dbrType = DBR_ENUM; + break; + case(oldDBR_CHAR): + ppn->dbrType = DBR_UCHAR; + break; + case(oldDBR_LONG): + ppn->dbrType = DBR_LONG; + break; + case(oldDBR_DOUBLE): + ppn->dbrType = DBR_DOUBLE; + break; + case(oldDBR_PUT_ACKT): + ppn->dbrType = DBR_PUT_ACKT; + break; + case(oldDBR_PUT_ACKS): + ppn->dbrType = DBR_PUT_ACKS; + break; + default: + return -1; + } + return 0; +} diff --git a/src/db/db_access_routines.h b/src/db/db_access_routines.h new file mode 100644 index 000000000..a1f445c6f --- /dev/null +++ b/src/db/db_access_routines.h @@ -0,0 +1,51 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* base/include/db_access_routines.h */ + +/* This defined routines for old database access. These were broken out of + db_access.h so that ca can be build independent of db. + src/ca contains db_access, which contains that data definitions +*/ + +#ifndef INCLdb_access_routinesh +#define INCLdb_access_routinesh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" +#include "dbAddr.h" + +epicsShareExtern struct dbBase *pdbbase; +epicsShareExtern volatile int interruptAccept; + + +/* + * old db access API + * (included here because these routines use dbAccess.h and their + * prototypes must also be included in db_access.h) + */ +epicsShareFunc int epicsShareAPI db_name_to_addr( + const char *pname, DBADDR *paddr); +epicsShareFunc int epicsShareAPI db_put_field( + DBADDR *paddr, int src_type,const void *psrc, int no_elements); +epicsShareFunc int epicsShareAPI db_get_field( + DBADDR *paddr, int dest_type,void *pdest, int no_elements, void *pfl); +epicsShareFunc int epicsShareAPI db_get_field_and_count( + struct dbAddr *paddr, int buffer_type, + void *pbuffer, long *nRequest, void *pfl); + + +#ifdef __cplusplus +} +#endif + +#endif /* INCLdb_access_routinesh */ diff --git a/src/db/db_convert.h b/src/db/db_convert.h new file mode 100644 index 000000000..57b178a23 --- /dev/null +++ b/src/db/db_convert.h @@ -0,0 +1,69 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* db_convert.h */ + +#ifndef INCLdb_converth +#define INCLdb_converth + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" +#include "dbAddr.h" + +epicsShareExtern struct dbBase *pdbbase; +epicsShareExtern volatile int interruptAccept; + +/*Definitions that allow old database access to use new conversion routines*/ +#define newDBF_DEVICE 11 +#define newDBR_ENUM 9 +epicsShareExtern long (*dbGetConvertRoutine[newDBF_DEVICE+1][newDBR_ENUM+1]) + (struct dbAddr *paddr, void *pbuffer,long nRequest, + long no_elements, long offset); +epicsShareExtern long (*dbPutConvertRoutine[newDBR_ENUM+1][newDBF_DEVICE+1]) + (struct dbAddr *paddr, const void *pbuffer,long nRequest, + long no_elements, long offset); +epicsShareExtern long (*dbFastGetConvertRoutine[newDBF_DEVICE+1][newDBR_ENUM+1])(); +epicsShareExtern long (*dbFastPutConvertRoutine[newDBR_ENUM+1][newDBF_DEVICE+1])(); + +/*Conversion between old and new DBR types*/ +epicsShareExtern unsigned short dbDBRoldToDBFnew[DBR_DOUBLE+1]; +epicsShareExtern unsigned short dbDBRnewToDBRold[newDBR_ENUM+1]; +#ifdef DB_CONVERT_GBLSOURCE +epicsShareDef unsigned short dbDBRoldToDBFnew[DBR_DOUBLE+1] = { + 0, /*DBR_STRING to DBF_STRING*/ + 3, /*DBR_INT to DBF_SHORT*/ + 7, /*DBR_FLOAT to DBF_FLOAT*/ + 9, /*DBR_ENUM to DBF_ENUM*/ + 1, /*DBR_CHAR to DBF_CHAR*/ + 5, /*DBR_LONG to DBF_LONG*/ + 8 /*DBR_DOUBLE to DBF_DOUBLE*/ +}; +epicsShareDef unsigned short dbDBRnewToDBRold[newDBR_ENUM+1] = { + 0, /*DBR_STRING to DBR_STRING*/ + 4, /*DBR_CHAR to DBR_CHAR*/ + 4, /*DBR_UCHAR to DBR_CHAR*/ + 1, /*DBR_SHORT to DBR_SHORT*/ + 5, /*DBR_USHORT to DBR_LONG*/ + 5, /*DBR_LONG to DBR_LONG*/ + 6, /*DBR_ULONG to DBR_DOUBLE*/ + 2, /*DBR_FLOAT to DBR_FLOAT*/ + 6, /*DBR_DOUBLE to DBR_DOUBLE*/ + 3, /*DBR_ENUM to DBR_ENUM*/ +}; +#endif /*DB_CONVERT_GBLSOURCE*/ + + +#ifdef __cplusplus +} +#endif + +#endif /* INCLdb_converth */ diff --git a/src/db/db_field_log.h b/src/db/db_field_log.h new file mode 100644 index 000000000..1bee8267b --- /dev/null +++ b/src/db/db_field_log.h @@ -0,0 +1,58 @@ +/*************************************************************************\ +* 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: Jeffrey O. Hill + * Date: 4-1-89 + */ +#ifndef INCLdb_field_logh +#define INCLdb_field_logh + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Simple native types (anything which is not a string or an array for + * now) logged by db_post_events() for reliable interprocess communication. + * (for other types they get whatever happens to be there when the lower + * priority task pending on the event queue wakes up). Strings would slow down + * events for more reasonable size values. DB fields of native type string + * will most likely change infrequently. + * + */ +union native_value{ + short dbf_int; + short dbf_short; + float dbf_float; + short dbf_enum; + char dbf_char; + long dbf_long; + double dbf_double; +#ifdef DB_EVENT_LOG_STRINGS + char dbf_string[MAXSTRINGSIZE]; +#endif +}; + +/* + * structure to log the state of a data base field at the time + * an event is triggered. + */ +typedef struct db_field_log { + unsigned short stat; /* Alarm Status */ + unsigned short sevr; /* Alarm Severity */ + epicsTimeStamp time; /* time stamp */ + union native_value field; /* field value */ +}db_field_log; + +#ifdef __cplusplus +} +#endif + +#endif /*INCLdb_field_logh*/ diff --git a/src/db/db_test.c b/src/db/db_test.c new file mode 100644 index 000000000..0ad5ad2e2 --- /dev/null +++ b/src/db/db_test.c @@ -0,0 +1,728 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* share/src/db @(#)db_test.c 1.10 2/3/94 */ +/* database access subroutines */ +/* + * Author: Bob Dalesio + * Date: 4/15/88 + */ +#include +#include +#include +#include + +#include "dbDefs.h" +#include "errlog.h" +#include "cadef.h" +#include "epicsStdioRedirect.h" +#define epicsExportSharedSymbols +#include "db_access_routines.h" +#include "dbNotify.h" +#include "db_test.h" +#include "dbCommon.h" + + +#define MAX_ELEMS 10 +int epicsShareAPI gft(char *pname) +{ + char tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE+sizeof(struct dbr_ctrl_double)]; + struct dbAddr addr; + struct dbAddr *paddr = &addr; + short number_elements; + int i; + int status; + + if (pname==0 || ((*pname < ' ') || (*pname > 'z'))) { + printf("\nusage \"pv name\"\n"); + return(1); + } + /* convert name to database address */ + status=db_name_to_addr(pname,&addr); + if(status) { + printf("db_name_to_addr failed\n"); + return(1); + } + printf(" Record Name: %s\n",pname); + printf("Record Address: 0x%p\n",(void *)addr.precord); + printf(" Field Type: %d\n",addr.dbr_field_type); + printf(" Field Address: 0x%p\n",addr.pfield); + printf(" Field Size: %d\n",addr.field_size); + printf(" No Elements: %ld\n",addr.no_elements); + number_elements = + ((addr.no_elements > MAX_ELEMS)?MAX_ELEMS:addr.no_elements); + + for(i=0; i<=LAST_BUFFER_TYPE; i++) { + if(addr.dbr_field_type==0) { + if( (i!=DBR_STRING) + && (i!=DBR_STS_STRING) + && (i!=DBR_TIME_STRING) + && (i!=DBR_GR_STRING) + && (i!=DBR_CTRL_STRING)) continue; + } + if(db_get_field(paddr,i,tgf_buffer,number_elements,NULL)<0) + printf("\t%s Failed\n",dbr_text[i]); + else + ca_dump_dbr (i,number_elements,tgf_buffer); + } + return(0); +} + +/* + * TPF + * Test put field + */ +int epicsShareAPI pft(char *pname,char *pvalue) +{ + struct dbAddr addr; + struct dbAddr *paddr = &addr; + char buffer[500]; + short shortvalue; + long longvalue; + float floatvalue; + unsigned char charvalue; + double doublevalue; + int status; + + if (pname==0 || pvalue==0 + || ((*pname < ' ') || (*pname > 'z')) + || ((*pvalue < ' ') || (*pvalue > 'z'))){ + printf("\nusage \"pv name\",\"value\"\n"); + return(1); + } + + /* convert name to database address */ + + /* convert name to database address */ + status=db_name_to_addr(pname,&addr); + if(status) { + printf("db_name_to_addr failed\n"); + return(1); + } + printf(" Record Name: %s\n",pname); + printf("Record Address: 0x%p\n",(void *)addr.precord); + printf(" Field Type: %d\n",addr.dbr_field_type); + printf(" Field Address: 0x%p\n",addr.pfield); + printf(" Field Size: %d\n",addr.field_size); + printf(" No Elements: %ld\n",addr.no_elements); + if (db_put_field(paddr,DBR_STRING,pvalue,1) < 0) printf("\n\t failed "); + if (db_get_field(paddr,DBR_STRING,buffer,1,NULL) < 0) printf("\n\tfailed"); + else ca_dump_dbr(DBR_STRING,1,buffer); + if(addr.dbr_field_type<=DBF_STRING || addr.dbr_field_type==DBF_ENUM) + return(0); + if(sscanf(pvalue,"%hd",&shortvalue)==1) { + if (db_put_field(paddr,DBR_SHORT,&shortvalue,1) < 0) + printf("\n\t SHORT failed "); + if (db_get_field(paddr,DBR_SHORT,buffer,1,NULL) < 0) + printf("\n\t SHORT GET failed"); + else ca_dump_dbr(DBR_SHORT,1,buffer); + } + if(sscanf(pvalue,"%ld",&longvalue)==1) { + if (db_put_field(paddr,DBR_LONG,&longvalue,1) < 0) + printf("\n\t LONG failed "); + if (db_get_field(paddr,DBR_LONG,buffer,1,NULL) < 0) + printf("\n\t LONG GET failed"); + else ca_dump_dbr(DBR_LONG,1,buffer); + } + if(epicsScanFloat(pvalue, &floatvalue)==1) { + if (db_put_field(paddr,DBR_FLOAT,&floatvalue,1) < 0) + printf("\n\t FLOAT failed "); + if (db_get_field(paddr,DBR_FLOAT,buffer,1,NULL) < 0) + printf("\n\t FLOAT GET failed"); + else ca_dump_dbr(DBR_FLOAT,1,buffer); + } + if(epicsScanFloat(pvalue, &floatvalue)==1) { + doublevalue=floatvalue; + if (db_put_field(paddr,DBR_DOUBLE,&doublevalue,1) < 0) + printf("\n\t DOUBLE failed "); + if (db_get_field(paddr,DBR_DOUBLE,buffer,1,NULL) < 0) + printf("\n\t DOUBLE GET failed"); + else ca_dump_dbr(DBR_DOUBLE,1,buffer); + } + if(sscanf(pvalue,"%hd",&shortvalue)==1) { + charvalue=(unsigned char)shortvalue; + if (db_put_field(paddr,DBR_CHAR,&charvalue,1) < 0) + printf("\n\t CHAR failed "); + if (db_get_field(paddr,DBR_CHAR,buffer,1,NULL) < 0) + printf("\n\t CHAR GET failed"); + else ca_dump_dbr(DBR_CHAR,1,buffer); + } + if(sscanf(pvalue,"%hd",&shortvalue)==1) { + if (db_put_field(paddr,DBR_ENUM,&shortvalue,1) < 0) + printf("\n\t ENUM failed "); + if (db_get_field(paddr,DBR_ENUM,buffer,1,NULL) < 0) + printf("\n\t ENUM GET failed"); + else ca_dump_dbr(DBR_ENUM,1,buffer); + } + printf("\n"); + return(0); +} + +#if 0 +/* + * PRINT_RETURNED + * + * print out the values in a database access interface structure + */ +static void print_returned(type,count,pbuffer) + short type; + char *pbuffer; + short count; +{ + short i; + + printf("%s\t",dbr_text[type]); + switch(type){ + case (DBR_STRING): + for(i=0; istatus,pvalue->severity); + printf("\tValue: %s",pvalue->value); + break; + } + case (DBR_STS_ENUM): + case (DBR_STS_SHORT): + { + struct dbr_sts_short *pvalue + = (struct dbr_sts_short *)pbuffer; + short *pshort = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case (DBR_STS_FLOAT): + { + struct dbr_sts_float *pvalue + = (struct dbr_sts_float *)pbuffer; + float *pfloat = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case (DBR_STS_CHAR): + { + struct dbr_sts_char *pvalue + = (struct dbr_sts_char *)pbuffer; + unsigned char *pchar = &pvalue->value; + short value; + + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + value=*pchar; + printf("%d ",value); + } + break; + } + case (DBR_STS_LONG): + { + struct dbr_sts_long *pvalue + = (struct dbr_sts_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case (DBR_STS_DOUBLE): + { + struct dbr_sts_double *pvalue + = (struct dbr_sts_double *)pbuffer; + double *pdouble = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",(float)(*pdouble)); + } + break; + } + case (DBR_TIME_STRING): + { + struct dbr_time_string *pvalue + = (struct dbr_time_string *) pbuffer; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %u %u", + pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); + printf("\tValue: "); + printf("%s",pvalue->value); + break; + } + case (DBR_TIME_SHORT): + case (DBR_TIME_ENUM): + { + struct dbr_time_short *pvalue + = (struct dbr_time_short *)pbuffer; + short *pshort = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %u %u", + pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case (DBR_TIME_FLOAT): + { + struct dbr_time_float *pvalue + = (struct dbr_time_float *)pbuffer; + float *pfloat = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %u %u", + pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case (DBR_TIME_CHAR): + { + struct dbr_time_char *pvalue + = (struct dbr_time_char *)pbuffer; + unsigned char *pchar = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %u %u", + pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",(short)(*pchar)); + } + break; + } + case (DBR_TIME_LONG): + { + struct dbr_time_long *pvalue + = (struct dbr_time_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %u %u", + pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case (DBR_TIME_DOUBLE): + { + struct dbr_time_double *pvalue + = (struct dbr_time_double *)pbuffer; + double *pdouble = &pvalue->value; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf("\tTimeStamp: %u %u", + pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",(float)(*pdouble)); + } + break; + } + case (DBR_GR_SHORT): + { + struct dbr_gr_short *pvalue + = (struct dbr_gr_short *)pbuffer; + short *pshort = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case (DBR_GR_FLOAT): + { + struct dbr_gr_float *pvalue + = (struct dbr_gr_float *)pbuffer; + float *pfloat = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case (DBR_GR_ENUM): + case (DBR_CTRL_ENUM): + { + struct dbr_gr_enum *pvalue + = (struct dbr_gr_enum *)pbuffer; + printf("%2d %2d",pvalue->status, + pvalue->severity); + printf("\tValue: %d",pvalue->value); + if(pvalue->no_str>0) { + printf("\n\t%3d",pvalue->no_str); + for (i = 0; i < pvalue->no_str; i++) + printf("\n\t%.26s",pvalue->strs[i]); + } + break; + } + case (DBR_GR_CHAR): + { + struct dbr_gr_char *pvalue + = (struct dbr_gr_char *)pbuffer; + unsigned char *pchar = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",(short)(*pchar)); + } + break; + } + case (DBR_GR_LONG): + { + struct dbr_gr_long *pvalue + = (struct dbr_gr_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case (DBR_GR_DOUBLE): + { + struct dbr_gr_double *pvalue + = (struct dbr_gr_double *)pbuffer; + double *pdouble = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + (float)(pvalue->upper_disp_limit), + (float)(pvalue->lower_disp_limit), + (float)(pvalue->upper_alarm_limit), + (float)(pvalue->upper_warning_limit), + (float)(pvalue->lower_warning_limit), + (float)(pvalue->lower_alarm_limit)); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",(float)(*pdouble)); + } + break; + } + case (DBR_CTRL_SHORT): + { + struct dbr_ctrl_short *pvalue + = (struct dbr_ctrl_short *)pbuffer; + short *pshort = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8d %8d", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pshort++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*pshort); + } + break; + } + case (DBR_CTRL_FLOAT): + { + struct dbr_ctrl_float *pvalue + = (struct dbr_ctrl_float *)pbuffer; + float *pfloat = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8.3f %8.3f", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pfloat++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.4f ",*pfloat); + } + break; + } + case (DBR_CTRL_CHAR): + { + struct dbr_ctrl_char *pvalue + = (struct dbr_ctrl_char *)pbuffer; + unsigned char *pchar = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8d %8d", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pchar++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%4d ",(short)(*pchar)); + } + break; + } + case (DBR_CTRL_LONG): + { + struct dbr_ctrl_long *pvalue + = (struct dbr_ctrl_long *)pbuffer; + dbr_long_t *plong = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf("\n\t%8d %8d %8d %8d %8d %8d", + pvalue->upper_disp_limit,pvalue->lower_disp_limit, + pvalue->upper_alarm_limit,pvalue->upper_warning_limit, + pvalue->lower_warning_limit,pvalue->lower_alarm_limit); + printf(" %8d %8d", + pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,plong++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%d ",*plong); + } + break; + } + case (DBR_CTRL_DOUBLE): + { + struct dbr_ctrl_double *pvalue + = (struct dbr_ctrl_double *)pbuffer; + double *pdouble = &pvalue->value; + printf("%2d %2d %.8s",pvalue->status,pvalue->severity, + pvalue->units); + printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", + pvalue->precision, + (float)(pvalue->upper_disp_limit), + (float)(pvalue->lower_disp_limit), + (float)(pvalue->upper_alarm_limit), + (float)(pvalue->upper_warning_limit), + (float)(pvalue->lower_warning_limit), + (float)(pvalue->lower_alarm_limit)); + printf(" %8.3f %8.3f", + (float)(pvalue->upper_ctrl_limit), + (float)(pvalue->lower_ctrl_limit)); + if(count==1) printf("\tValue: "); + for (i = 0; i < count; i++,pdouble++){ + if(count!=1 && (i%10 == 0)) printf("\n"); + printf("%6.6f ",(float)(*pdouble)); + } + break; + } + case (DBR_STSACK_STRING): + { + struct dbr_stsack_string *pvalue + = (struct dbr_stsack_string *)pbuffer; + printf("%2d %2d",pvalue->status,pvalue->severity); + printf(" %2d %2d",pvalue->ackt,pvalue->acks); + printf(" %s",pvalue->value); + break; + } + case (DBR_CLASS_NAME): + { + printf(" %s",(char *)pbuffer); + break; + } + } + printf("\n"); +} +#endif + +typedef struct tpnInfo { + epicsEventId callbackDone; + putNotify *ppn; +}tpnInfo; + +static void tpnCallback(putNotify *ppn) +{ + struct dbAddr *pdbaddr = (struct dbAddr *)ppn->paddr; + tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + putNotifyStatus status = ppn->status; + char *pname = pdbaddr->precord->name; + + if(status==0) + printf("tpnCallback: success record=%s\n",pname); + else + printf("%s tpnCallback status = %d\n",pname,status); + epicsEventSignal(ptpnInfo->callbackDone); +} + +static void tpnThread(void *pvt) +{ + tpnInfo *ptpnInfo = (tpnInfo *)pvt; + putNotify *ppn = (putNotify *)ptpnInfo->ppn; + + dbPutNotify(ppn); + epicsEventWait(ptpnInfo->callbackDone); + dbNotifyCancel(ppn); + epicsEventDestroy(ptpnInfo->callbackDone); + free((void *)ppn->paddr); + free(ppn); + free(ptpnInfo); +} + + +int epicsShareAPI tpn(char *pname,char *pvalue) +{ + long status; + tpnInfo *ptpnInfo; + struct dbAddr *pdbaddr=NULL; + putNotify *ppn=NULL; + char *psavevalue; + int len; + + if (pname==0 || pvalue==0 + || ((*pname < ' ') || (*pname > 'z')) + || ((*pvalue < ' ') || (*pvalue > 'z'))){ + printf("\nusage \"pv name\",\"value\"\n"); + return(1); + } + len = strlen(pvalue); + /*allocate space for value immediately following DBADDR*/ + pdbaddr = calloc(1,sizeof(struct dbAddr) + len+1); + if(!pdbaddr) { + printf("calloc failed\n"); + return(-1); + } + psavevalue = (char *)(pdbaddr + 1); + strcpy(psavevalue,pvalue); + status = db_name_to_addr(pname,pdbaddr); + if(status) { + printf("db_name_to_addr failed\n"); + free((void *)pdbaddr); + return(-1); + } + ppn = calloc(1,sizeof(putNotify)); + if(!pdbaddr) { + printf("calloc failed\n"); + return(-1); + } + ppn->paddr = pdbaddr; + ppn->pbuffer = psavevalue; + ppn->nRequest = 1; + if(dbPutNotifyMapType(ppn,DBR_STRING)) { + printf("dbPutNotifyMapType failed\n"); + printf("calloc failed\n"); + return(-1); + } + ppn->userCallback = tpnCallback; + ptpnInfo = calloc(1,sizeof(tpnInfo)); + if(!ptpnInfo) { + printf("calloc failed\n"); + return(-1); + } + ptpnInfo->ppn = ppn; + ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); + ppn->usrPvt = ptpnInfo; + epicsThreadCreate("tpn",epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackMedium), + tpnThread,ptpnInfo); + return(0); +} diff --git a/src/db/db_test.h b/src/db/db_test.h new file mode 100644 index 000000000..52b00a979 --- /dev/null +++ b/src/db/db_test.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INCLdb_testh +#define INCLdb_testh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsShareAPI gft(char *pname); +epicsShareFunc int epicsShareAPI pft(char *pname,char *pvalue); +epicsShareFunc int epicsShareAPI tpn(char *pname,char *pvalue); +#ifdef __cplusplus +} +#endif + +#endif /* INCLdb_testh */ diff --git a/src/db/initHooks.c b/src/db/initHooks.c new file mode 100644 index 000000000..99a56a2a1 --- /dev/null +++ b/src/db/initHooks.c @@ -0,0 +1,130 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ +/* + * Authors: Benjamin Franksen (BESY) and Marty Kraimer + * Date: 06-01-91 + * major Revision: 07JuL97 + */ + +#include +#include +#include + +#include "dbDefs.h" +#include "ellLib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#define epicsExportSharedSymbols +#include "initHooks.h" + +typedef struct initHookLink { + ELLNODE node; + initHookFunction func; +} initHookLink; + +static ELLLIST functionList = ELLLIST_INIT; +static epicsMutexId listLock; + +/* + * Lazy initialization functions + */ +static void initHookOnce(void *arg) +{ + listLock = epicsMutexMustCreate(); +} + +static void initHookInit(void) +{ + static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; + epicsThreadOnce(&onceFlag, initHookOnce, NULL); +} + +/* + * To be called before iocInit reaches state desired. + */ +int initHookRegister(initHookFunction func) +{ + initHookLink *newHook; + + if (!func) return 0; + + initHookInit(); + + newHook = (initHookLink *)malloc(sizeof(initHookLink)); + if (!newHook) { + printf("Cannot malloc a new initHookLink\n"); + return -1; + } + newHook->func = func; + + epicsMutexMustLock(listLock); + ellAdd(&functionList, &newHook->node); + epicsMutexUnlock(listLock); + return 0; +} + +/* + * Called by iocInit at various points during initialization. + * This function must only be called by iocInit and relatives. + */ +void initHookAnnounce(initHookState state) +{ + initHookLink *hook; + + initHookInit(); + + epicsMutexMustLock(listLock); + hook = (initHookLink *)ellFirst(&functionList); + epicsMutexUnlock(listLock); + + while (hook != NULL) { + hook->func(state); + + epicsMutexMustLock(listLock); + hook = (initHookLink *)ellNext(&hook->node); + epicsMutexUnlock(listLock); + } +} + +/* + * Call any time you want to print out a state name. + */ +const char *initHookName(int state) +{ + const char *stateName[] = { + "initHookAtIocBuild", + "initHookAtBeginning", + "initHookAfterCallbackInit", + "initHookAfterCaLinkInit", + "initHookAfterInitDrvSup", + "initHookAfterInitRecSup", + "initHookAfterInitDevSup", + "initHookAfterInitDatabase", + "initHookAfterFinishDevSup", + "initHookAfterScanInit", + "initHookAfterInitialProcess", + "initHookAfterCaServerInit", + "initHookAfterIocBuilt", + "initHookAtIocRun", + "initHookAfterDatabaseRunning", + "initHookAfterCaServerRunning", + "initHookAfterIocRunning", + "initHookAtIocPause", + "initHookAfterCaServerPaused", + "initHookAfterDatabasePaused", + "initHookAfterIocPaused", + "initHookAfterInterruptAccept", + "initHookAtEnd" + }; + if (state < 0 || state > NELEMENTS(stateName)) { + return "Not an initHookState"; + } + return stateName[state]; +} diff --git a/src/db/initHooks.h b/src/db/initHooks.h new file mode 100644 index 000000000..048a44cfa --- /dev/null +++ b/src/db/initHooks.h @@ -0,0 +1,68 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ +/* + * Authors: Benjamin Franksen (BESY) and Marty Kraimer + * Date: 06-01-91 + * major Revision: 07JuL97 + */ + +#ifndef INC_initHooks_H +#define INC_initHooks_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* This enum must agree with the array of names defined in initHookName() */ +typedef enum { + initHookAtIocBuild = 0, /* Start of iocBuild/iocInit commands */ + initHookAtBeginning, + initHookAfterCallbackInit, + initHookAfterCaLinkInit, + initHookAfterInitDrvSup, + initHookAfterInitRecSup, + initHookAfterInitDevSup, + initHookAfterInitDatabase, + initHookAfterFinishDevSup, + initHookAfterScanInit, + initHookAfterInitialProcess, + initHookAfterCaServerInit, + initHookAfterIocBuilt, /* End of iocBuild command */ + + initHookAtIocRun, /* Start of iocRun command */ + initHookAfterDatabaseRunning, + initHookAfterCaServerRunning, + initHookAfterIocRunning, /* End of iocRun/iocInit commands */ + + initHookAtIocPause, /* Start of iocPause command */ + initHookAfterCaServerPaused, + initHookAfterDatabasePaused, + initHookAfterIocPaused, /* End of iocPause command */ + +/* Deprecated states, provided for backwards compatibility. + * These states are announced at the same point they were before, + * but will not be repeated if the IOC gets paused and restarted. + */ + initHookAfterInterruptAccept, /* After initHookAfterDatabaseRunning */ + initHookAtEnd, /* Before initHookAfterIocRunning */ +} initHookState; + +typedef void (*initHookFunction)(initHookState state); +epicsShareFunc int initHookRegister(initHookFunction func); +epicsShareFunc void initHookAnnounce(initHookState state); +epicsShareFunc const char *initHookName(int state); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_initHooks_H */ diff --git a/src/db/menuAlarmSevr.dbd b/src/db/menuAlarmSevr.dbd new file mode 100644 index 000000000..ae6661a9e --- /dev/null +++ b/src/db/menuAlarmSevr.dbd @@ -0,0 +1,15 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuAlarmSevr) { + choice(menuAlarmSevrNO_ALARM,"NO_ALARM") + choice(menuAlarmSevrMINOR,"MINOR") + choice(menuAlarmSevrMAJOR,"MAJOR") + choice(menuAlarmSevrINVALID,"INVALID") +} diff --git a/src/db/menuAlarmStat.dbd b/src/db/menuAlarmStat.dbd new file mode 100644 index 000000000..e8d7f1ced --- /dev/null +++ b/src/db/menuAlarmStat.dbd @@ -0,0 +1,33 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuAlarmStat) { + choice(menuAlarmStatNO_ALARM,"NO_ALARM") + choice(menuAlarmStatREAD,"READ") + choice(menuAlarmStatWRITE,"WRITE") + choice(menuAlarmStatHIHI,"HIHI") + choice(menuAlarmStatHIGH,"HIGH") + choice(menuAlarmStatLOLO,"LOLO") + choice(menuAlarmStatLOW,"LOW") + choice(menuAlarmStatSTATE,"STATE") + choice(menuAlarmStatCOS,"COS") + choice(menuAlarmStatCOMM,"COMM") + choice(menuAlarmStatTIMEOUT,"TIMEOUT") + choice(menuAlarmStatHWLIMIT,"HWLIMIT") + choice(menuAlarmStatCALC,"CALC") + choice(menuAlarmStatSCAN,"SCAN") + choice(menuAlarmStatLINK,"LINK") + choice(menuAlarmStatSOFT,"SOFT") + choice(menuAlarmStatBAD_SUB,"BAD_SUB") + choice(menuAlarmStatUDF,"UDF") + choice(menuAlarmStatDISABLE,"DISABLE") + choice(menuAlarmStatSIMM,"SIMM") + choice(menuAlarmStatREAD_ACCESS,"READ_ACCESS") + choice(menuAlarmStatWRITE_ACCESS,"WRITE_ACCESS") +} diff --git a/src/db/menuCompress.dbd b/src/db/menuCompress.dbd new file mode 100644 index 000000000..68b68e2a2 --- /dev/null +++ b/src/db/menuCompress.dbd @@ -0,0 +1,15 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuCompress) { + choice(menuCompressN_to_1_First_Value,"N to 1 First Value") + choice(menuCompressN_to_1_Low_Value,"N to 1 Low Value") + choice(menuCompressN_to_1_High_Value,"N to 1 High Value") + choice(menuCompressN_to_1_Average,"N to 1 Average") +} diff --git a/src/db/menuFtype.dbd b/src/db/menuFtype.dbd new file mode 100644 index 000000000..ff20e0ca4 --- /dev/null +++ b/src/db/menuFtype.dbd @@ -0,0 +1,21 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuFtype) { + choice(menuFtypeSTRING,"STRING") + choice(menuFtypeCHAR,"CHAR") + choice(menuFtypeUCHAR,"UCHAR") + choice(menuFtypeSHORT,"SHORT") + choice(menuFtypeUSHORT,"USHORT") + choice(menuFtypeLONG,"LONG") + choice(menuFtypeULONG,"ULONG") + choice(menuFtypeFLOAT,"FLOAT") + choice(menuFtypeDOUBLE,"DOUBLE") + choice(menuFtypeENUM,"ENUM") +} diff --git a/src/db/menuGlobal.dbd b/src/db/menuGlobal.dbd new file mode 100644 index 000000000..16a0bbc34 --- /dev/null +++ b/src/db/menuGlobal.dbd @@ -0,0 +1,19 @@ +#************************************************************************* +# Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +include "menuAlarmSevr.dbd" +include "menuAlarmStat.dbd" +include "menuCompress.dbd" +include "menuFtype.dbd" +include "menuIvoa.dbd" +include "menuOmsl.dbd" +include "menuPini.dbd" +include "menuPriority.dbd" +include "menuScan.dbd" +include "menuSimm.dbd" +include "menuYesNo.dbd" diff --git a/src/db/menuIvoa.dbd b/src/db/menuIvoa.dbd new file mode 100644 index 000000000..b1b3ce51f --- /dev/null +++ b/src/db/menuIvoa.dbd @@ -0,0 +1,14 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuIvoa) { + choice(menuIvoaContinue_normally,"Continue normally") + choice(menuIvoaDon_t_drive_outputs,"Don't drive outputs") + choice(menuIvoaSet_output_to_IVOV,"Set output to IVOV") +} diff --git a/src/db/menuOmsl.dbd b/src/db/menuOmsl.dbd new file mode 100644 index 000000000..3022437dc --- /dev/null +++ b/src/db/menuOmsl.dbd @@ -0,0 +1,13 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuOmsl) { + choice(menuOmslsupervisory,"supervisory") + choice(menuOmslclosed_loop,"closed_loop") +} diff --git a/src/db/menuPini.dbd b/src/db/menuPini.dbd new file mode 100644 index 000000000..96aca7298 --- /dev/null +++ b/src/db/menuPini.dbd @@ -0,0 +1,16 @@ +#************************************************************************* +# Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +menu(menuPini) { + choice(menuPiniNO,"NO") + choice(menuPiniYES,"YES") + choice(menuPiniRUN,"RUN") + choice(menuPiniRUNNING,"RUNNING") + choice(menuPiniPAUSE,"PAUSE") + choice(menuPiniPAUSED,"PAUSED") +} diff --git a/src/db/menuPriority.dbd b/src/db/menuPriority.dbd new file mode 100644 index 000000000..155caa5e7 --- /dev/null +++ b/src/db/menuPriority.dbd @@ -0,0 +1,14 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuPriority) { + choice(menuPriorityLOW,"LOW") + choice(menuPriorityMEDIUM,"MEDIUM") + choice(menuPriorityHIGH,"HIGH") +} diff --git a/src/db/menuScan.dbd b/src/db/menuScan.dbd new file mode 100644 index 000000000..17fedd59b --- /dev/null +++ b/src/db/menuScan.dbd @@ -0,0 +1,21 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +menu(menuScan) { + choice(menuScanPassive,"Passive") + choice(menuScanEvent,"Event") + choice(menuScanI_O_Intr,"I/O Intr") + # Periodic scans follow, ordered from slowest to fastest + choice(menuScan10_second,"10 second") + choice(menuScan5_second,"5 second") + choice(menuScan2_second,"2 second") + choice(menuScan1_second,"1 second") + choice(menuScan_5_second,".5 second") + choice(menuScan_2_second,".2 second") + choice(menuScan_1_second,".1 second") +} diff --git a/src/db/menuSimm.dbd b/src/db/menuSimm.dbd new file mode 100644 index 000000000..8021a9512 --- /dev/null +++ b/src/db/menuSimm.dbd @@ -0,0 +1,14 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuSimm) { + choice(menuSimmNO,"NO") + choice(menuSimmYES,"YES") + choice(menuSimmRAW,"RAW") +} diff --git a/src/db/menuYesNo.dbd b/src/db/menuYesNo.dbd new file mode 100644 index 000000000..2d09dd65a --- /dev/null +++ b/src/db/menuYesNo.dbd @@ -0,0 +1,13 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(menuYesNo) { + choice(menuYesNoNO,"NO") + choice(menuYesNoYES,"YES") +} diff --git a/src/db/recGbl.c b/src/db/recGbl.c new file mode 100644 index 000000000..276e105de --- /dev/null +++ b/src/db/recGbl.c @@ -0,0 +1,358 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* recGbl.c */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * Author: Marty Kraimer + * Date: 11-7-90 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsTime.h" +#include "epicsPrint.h" +#include "dbBase.h" +#include "dbFldTypes.h" +#include "link.h" +#include "dbAddr.h" +#include "db_field_log.h" +#include "errlog.h" +#include "devSup.h" +#include "dbCommon.h" +#include "caeventmask.h" +#define epicsExportSharedSymbols +#include "dbAccessDefs.h" +#include "dbNotify.h" +#include "dbCa.h" +#include "dbEvent.h" +#include "dbScan.h" +#include "recGbl.h" + + +/* Hook Routines */ + +RECGBL_ALARM_HOOK_ROUTINE recGblAlarmHook = NULL; + +/* local routines */ +static void getMaxRangeValues(short field_type, double *pupper_limit, + double *plower_limit); + + + +void epicsShareAPI recGblDbaddrError(long status, const struct dbAddr *paddr, + const char *pmessage) +{ + dbCommon *precord = 0; + dbFldDes *pdbFldDes = 0; + + if(paddr) { + pdbFldDes = paddr->pfldDes; + precord = paddr->precord; + } + errPrintf(status,0,0, + "PV: %s.%s " + "error detected in routine: %s\n", + (paddr ? precord->name : "Unknown"), + (pdbFldDes ? pdbFldDes->name : ""), + (pmessage ? pmessage : "Unknown")); + return; +} + +void epicsShareAPI recGblRecordError(long status, void *pdbc, + const char *pmessage) +{ + dbCommon *precord = pdbc; + + errPrintf(status,0,0, + "PV: %s %s\n", + (precord ? precord->name : "Unknown"), + (pmessage ? pmessage : "")); + return; +} + +void epicsShareAPI recGblRecSupError(long status, const struct dbAddr *paddr, + const char *pmessage, const char *psupport_name) +{ + dbCommon *precord = 0; + dbFldDes *pdbFldDes = 0; + dbRecordType *pdbRecordType = 0; + + if(paddr) { + precord = paddr->precord; + pdbFldDes = paddr->pfldDes; + if(pdbFldDes) pdbRecordType = pdbFldDes->pdbRecordType; + } + errPrintf(status,0,0, + "Record Support Routine (%s) " + "Record Type %s " + "PV %s.%s " + " %s\n", + (psupport_name ? psupport_name : "Unknown"), + (pdbRecordType ? pdbRecordType->name : "Unknown"), + (paddr ? precord->name : "Unknown"), + (pdbFldDes ? pdbFldDes->name : ""), + (pmessage ? pmessage : "")); + return; +} + +void epicsShareAPI recGblGetPrec(const struct dbAddr *paddr,long *precision) +{ + dbFldDes *pdbFldDes = paddr->pfldDes; + + switch(pdbFldDes->field_type){ + case(DBF_SHORT): + *precision = 0; + break; + case(DBF_USHORT): + *precision = 0; + break; + case(DBF_LONG): + *precision = 0; + break; + case(DBF_ULONG): + *precision = 0; + break; + case(DBF_FLOAT): + case(DBF_DOUBLE): + if(*precision<0 || *precision>15) *precision=15; + break; + default: + break; + } + return; +} + +void epicsShareAPI recGblGetGraphicDouble( + const struct dbAddr *paddr,struct dbr_grDouble *pgd) +{ + dbFldDes *pdbFldDes = paddr->pfldDes; + + getMaxRangeValues(pdbFldDes->field_type,&pgd->upper_disp_limit, + &pgd->lower_disp_limit); + + return; +} + +void epicsShareAPI recGblGetAlarmDouble( + const struct dbAddr *paddr,struct dbr_alDouble *pad) +{ + pad->upper_alarm_limit = 0; + pad->upper_warning_limit = 0; + pad->lower_warning_limit = 0; + pad->lower_alarm_limit = 0; + + return; +} + +void epicsShareAPI recGblGetControlDouble( + const struct dbAddr *paddr,struct dbr_ctrlDouble *pcd) +{ + dbFldDes *pdbFldDes=paddr->pfldDes; + + getMaxRangeValues(pdbFldDes->field_type,&pcd->upper_ctrl_limit, + &pcd->lower_ctrl_limit); + + return; +} + +int epicsShareAPI recGblInitConstantLink( + struct link *plink,short dbftype,void *pdest) +{ + if(plink->type != CONSTANT) return(FALSE); + if(!plink->value.constantStr) return(FALSE); + switch(dbftype) { + case DBF_STRING: + strcpy((char *)pdest,plink->value.constantStr); + break; + case DBF_CHAR : { + epicsInt16 value; + epicsInt8 *pvalue = (epicsInt8 *)pdest; + + sscanf(plink->value.constantStr,"%hi",&value); + *pvalue = value; + } + break; + case DBF_UCHAR : { + epicsUInt16 value; + epicsUInt8 *pvalue = (epicsUInt8 *)pdest; + + sscanf(plink->value.constantStr,"%hu",&value); + *pvalue = value; + } + break; + case DBF_SHORT : + sscanf(plink->value.constantStr,"%hi",(epicsInt16 *)pdest); + break; + case DBF_USHORT : + case DBF_ENUM : + case DBF_MENU : + case DBF_DEVICE : + sscanf(plink->value.constantStr,"%hu",(epicsUInt16 *)pdest); + break; + case DBF_LONG : + *(epicsInt32 *)pdest = strtol(plink->value.constantStr, NULL, 0); + break; + case DBF_ULONG : + *(epicsUInt32 *)pdest = strtoul(plink->value.constantStr, NULL, 10); + break; + case DBF_FLOAT : + epicsScanFloat(plink->value.constantStr, (epicsFloat32 *)pdest); + break; + case DBF_DOUBLE : + epicsScanDouble(plink->value.constantStr, (epicsFloat64 *)pdest); + break; + default: + epicsPrintf("Error in recGblInitConstantLink: Illegal DBF type\n"); + return(FALSE); + } + return(TRUE); +} + +unsigned short epicsShareAPI recGblResetAlarms(void *precord) +{ + dbCommon *pdbc = precord; + epicsEnum16 prev_stat = pdbc->stat; + epicsEnum16 prev_sevr = pdbc->sevr; + epicsEnum16 new_stat = pdbc->nsta; + epicsEnum16 new_sevr = pdbc->nsev; + epicsEnum16 val_mask = 0; + epicsEnum16 stat_mask = 0; + + pdbc->stat = new_stat; + pdbc->sevr = new_sevr; + pdbc->nsta = 0; + pdbc->nsev = 0; + + if (prev_sevr != new_sevr) { + stat_mask = DBE_ALARM; + db_post_events(pdbc, &pdbc->sevr, DBE_VALUE); + } + if (prev_stat != new_stat) { + stat_mask |= DBE_VALUE; + } + if (stat_mask) { + db_post_events(pdbc, &pdbc->stat, stat_mask); + val_mask = DBE_ALARM; + + if (!pdbc->ackt || new_sevr >= pdbc->acks) { + pdbc->acks = new_sevr; + db_post_events(pdbc, &pdbc->acks, DBE_VALUE); + } + + if (recGblAlarmHook) { + (*recGblAlarmHook)(pdbc, prev_sevr, prev_stat); + } + } + return val_mask; +} + +void epicsShareAPI recGblFwdLink(void *precord) +{ + dbCommon *pdbc = precord; + + dbScanFwdLink(&pdbc->flnk); + /*Handle dbPutFieldNotify record completions*/ + if(pdbc->ppn) dbNotifyCompletion(pdbc); + if(pdbc->rpro) { + /*If anyone requested reprocessing do it*/ + pdbc->rpro = FALSE; + scanOnce(pdbc); + } + /*In case putField caused put we are all done */ + pdbc->putf = FALSE; +} + +void epicsShareAPI recGblGetTimeStamp(void *pvoid) +{ + dbCommon* prec = (dbCommon*)pvoid; + struct link *plink = &prec->tsel; + + if (plink->type != CONSTANT) { + struct pv_link *ppv_link = &plink->value.pv_link; + + if (ppv_link->pvlMask & pvlOptTSELisTime) { + if (dbGetTimeStamp(plink, &prec->time)) + errlogPrintf("recGblGetTimeStamp: dbGetTimeStamp failed, %s.TSEL = %s\n", + prec->name, ppv_link->pvname); + return; + } + dbGetLink(&prec->tsel, DBR_SHORT, &prec->tse, 0, 0); + } + if (prec->tse != epicsTimeEventDeviceTime) { + if (epicsTimeGetEvent(&prec->time, prec->tse)) + errlogPrintf("recGblGetTimeStamp: epicsTimeGetEvent failed, %s.TSE = %d\n", + prec->name, prec->tse); + } +} + +void epicsShareAPI recGblTSELwasModified(struct link *plink) +{ + struct pv_link *ppv_link = &plink->value.pv_link; + char *pfieldname; + + if (plink->type != PV_LINK) { + errlogPrintf("recGblTSELwasModified called for non PV_LINK\n"); + return; + } + /*If pvname ends in .TIME then just ask for VAL*/ + /*Note that the VAL value will not be used*/ + pfieldname = strstr(ppv_link->pvname, ".TIME"); + if (pfieldname) { + strcpy(pfieldname, ".VAL"); + ppv_link->pvlMask |= pvlOptTSELisTime; + } +} + +static void getMaxRangeValues(short field_type, double *pupper_limit, + double *plower_limit) +{ + switch(field_type){ + case(DBF_CHAR): + *pupper_limit = -128.0; + *plower_limit = 127.0; + break; + case(DBF_UCHAR): + *pupper_limit = 255.0; + *plower_limit = 0.0; + break; + case(DBF_SHORT): + *pupper_limit = (double)SHRT_MAX; + *plower_limit = (double)SHRT_MIN; + break; + case(DBF_ENUM): + case(DBF_USHORT): + *pupper_limit = (double)USHRT_MAX; + *plower_limit = (double)0; + break; + case(DBF_LONG): + /* long did not work using cast to double */ + *pupper_limit = 2147483647.; + *plower_limit = -2147483648.; + break; + case(DBF_ULONG): + *pupper_limit = (double)ULONG_MAX; + *plower_limit = (double)0; + break; + case(DBF_FLOAT): + *pupper_limit = (double)1e+30; + *plower_limit = (double)-1e30; + break; + case(DBF_DOUBLE): + *pupper_limit = (double)1e+30; + *plower_limit = (double)-1e30; + break; + } + return; +} diff --git a/src/db/recGbl.h b/src/db/recGbl.h new file mode 100644 index 000000000..888d00977 --- /dev/null +++ b/src/db/recGbl.h @@ -0,0 +1,75 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* recGbl.h */ +/* Record Global + * Author: Marty Kraimer + * Date: 13Jun95 + */ +#ifndef INCrecGblh +#define INCrecGblh 1 + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************************/ + +#define recGblSetSevr(PREC,NSTA,NSEV) \ +(\ + ((PREC)->nsev<(NSEV))\ + ? ((PREC)->nsta=(NSTA),(PREC)->nsev=(NSEV),TRUE)\ + : FALSE\ +) + +/* Structures needed for args */ + +struct link; +struct dbAddr; +struct dbr_alDouble; +struct dbr_ctrlDouble; +struct dbr_grDouble; +struct dbCommon; + +/* Hook Routine */ + +typedef void (*RECGBL_ALARM_HOOK_ROUTINE)(struct dbCommon *prec, + unsigned short prev_sevr, unsigned short prev_stat); +extern RECGBL_ALARM_HOOK_ROUTINE recGblAlarmHook; + +/* Global Record Support Routines */ + +epicsShareFunc void epicsShareAPI recGblDbaddrError( + long status, const struct dbAddr *paddr, const char *pcaller_name); +epicsShareFunc void epicsShareAPI recGblRecordError( + long status, void *precord, const char *pcaller_name); +epicsShareFunc void epicsShareAPI recGblRecSupError( + long status, const struct dbAddr *paddr, const char *pcaller_name, const char *psupport_name); +epicsShareFunc void epicsShareAPI recGblGetGraphicDouble( + const struct dbAddr *paddr, struct dbr_grDouble *pgd); +epicsShareFunc void epicsShareAPI recGblGetControlDouble( + const struct dbAddr *paddr, struct dbr_ctrlDouble *pcd); +epicsShareFunc void epicsShareAPI recGblGetAlarmDouble( + const struct dbAddr *paddr, struct dbr_alDouble *pad); +epicsShareFunc void epicsShareAPI recGblGetPrec( + const struct dbAddr *paddr, long *pprecision); +epicsShareFunc int epicsShareAPI recGblInitConstantLink( + struct link *plink,short dbftype,void *pdest); +epicsShareFunc unsigned short epicsShareAPI recGblResetAlarms(void *precord); +epicsShareFunc void epicsShareAPI recGblFwdLink(void *precord); +epicsShareFunc void epicsShareAPI recGblGetTimeStamp(void *precord); +epicsShareFunc void epicsShareAPI recGblTSELwasModified(struct link *plink); + +#ifdef __cplusplus +} +#endif + +#endif /*INCrecGblh*/ diff --git a/src/db/templateInstances.cpp b/src/db/templateInstances.cpp new file mode 100644 index 000000000..6ecc5aa65 --- /dev/null +++ b/src/db/templateInstances.cpp @@ -0,0 +1,46 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#define epicsExportSharedSymbols + +#include "dbCAC.h" +#include "dbChannelIO.h" +#include "dbPutNotifyBlocker.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class tsFreeList < dbChannelIO, 256, epicsMutexNOOP >; +template class tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP >; +template class tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP >; +template class resTable < dbBaseIO, chronIntId >; +template class chronIntIdResTable < dbBaseIO >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif diff --git a/src/db/test/Makefile b/src/db/test/Makefile new file mode 100644 index 000000000..164ab4d1a --- /dev/null +++ b/src/db/test/Makefile @@ -0,0 +1,30 @@ +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../.. + +include $(TOP)/configure/CONFIG + +callbackTest_LIBS = dbIoc +callbackTest_LIBS_DEFAULT = dbStaticHost +callbackTest_LIBS_vxWorks = dbStaticIoc +callbackTest_LIBS_RTEMS = dbStaticIoc +PROD_LIBS = ca Com + +TESTPROD_HOST += callbackTest +callbackTest_SRCS += callbackTest.c +PROD_vxWorks += callbackTest +TESTS += callbackTest + +# When we add more test programs here, this must become a vxTestHarness +TESTSPEC_vxWorks = callbackTest.munch; callbackTest + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES + diff --git a/src/db/test/callbackTest.c b/src/db/test/callbackTest.c new file mode 100644 index 000000000..c2f1a127c --- /dev/null +++ b/src/db/test/callbackTest.c @@ -0,0 +1,123 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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: Marty Kraimer Date: 26JAN2000 */ + +#include +#include +#include +#include +#include +#include + +#include "callback.h" +#include "cantProceed.h" +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsTime.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +#define NCALLBACKS 168 +#define DELAY_QUANTUM 0.25 + +#define TEST_DELAY(i) ((i / NUM_CALLBACK_PRIORITIES) * DELAY_QUANTUM) + +typedef struct myPvt { + CALLBACK cb1; + CALLBACK cb2; + epicsTimeStamp start; + double delay; + int pass; +} myPvt; + +epicsEventId finished; + + +static void myCallback(CALLBACK *pCallback) +{ + myPvt *pmyPvt; + epicsTimeStamp now; + double delay, error; + + epicsTimeGetCurrent(&now); + callbackGetUser(pmyPvt, pCallback); + + if (pmyPvt->pass++ == 0) { + delay = 0.0; + error = epicsTimeDiffInSeconds(&now, &pmyPvt->start); + pmyPvt->start = now; + callbackRequestDelayed(&pmyPvt->cb2, pmyPvt->delay); + } else if (pmyPvt->pass == 2) { + double diff = epicsTimeDiffInSeconds(&now, &pmyPvt->start); + delay = pmyPvt->delay; + error = fabs(delay - diff); + } else { + testFail("pass = %d for delay = %f", pmyPvt->pass, pmyPvt->delay); + return; + } + testOk(error < 0.05, "delay %f seconds, callback time error %f", + delay, error); +} + +static void finalCallback(CALLBACK *pCallback) +{ + myCallback(pCallback); + epicsEventSignal(finished); +} + +MAIN(callbackTest) +{ + myPvt *pcbt[NCALLBACKS]; + myPvt *pfinal; + int i; + + testPlan(NCALLBACKS * 2 + 2); + + callbackInit(); + epicsThreadSleep(1.0); + + finished = epicsEventMustCreate(epicsEventEmpty); + + for (i = 0; i < NCALLBACKS ; i++) { + pcbt[i] = callocMustSucceed(1, sizeof(myPvt), "pcbt"); + callbackSetCallback(myCallback, &pcbt[i]->cb1); + callbackSetCallback(myCallback, &pcbt[i]->cb2); + callbackSetUser(pcbt[i], &pcbt[i]->cb1); + callbackSetUser(pcbt[i], &pcbt[i]->cb2); + callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb1); + callbackSetPriority(i % NUM_CALLBACK_PRIORITIES, &pcbt[i]->cb2); + pcbt[i]->delay = TEST_DELAY(i); + pcbt[i]->pass = 0; + } + + for (i = 0; i < NCALLBACKS ; i++) { + epicsTimeGetCurrent(&pcbt[i]->start); + callbackRequest(&pcbt[i]->cb1); + } + + pfinal = callocMustSucceed(1, sizeof(myPvt), "final"); + callbackSetCallback(myCallback, &pfinal->cb1); + callbackSetCallback(finalCallback, &pfinal->cb2); + callbackSetUser(pfinal, &pfinal->cb1); + callbackSetUser(pfinal, &pfinal->cb2); + callbackSetPriority(0, &pfinal->cb1); + callbackSetPriority(0, &pfinal->cb2); + pfinal->delay = TEST_DELAY(NCALLBACKS) + 1.0; + pfinal->pass = 0; + + epicsTimeGetCurrent(&pfinal->start); + callbackRequest(&pfinal->cb1); + + epicsEventWait(finished); + + return testDone(); +} diff --git a/src/dbStatic/Makefile b/src/dbStatic/Makefile new file mode 100644 index 000000000..0f4590c75 --- /dev/null +++ b/src/dbStatic/Makefile @@ -0,0 +1,66 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../.. + +include $(TOP)/configure/CONFIG + +INC += alarm.h +INC += alarmString.h +INC += dbBase.h +INC += dbFldTypes.h +INC += dbStaticLib.h +INC += dbStaticPvt.h +INC += link.h +INC += special.h +INC += guigroup.h +INC += devSup.h +INC += drvSup.h +INC += recSup.h +INC += dbStaticIocRegister.h + +LIBSRCS += dbStaticLib.c +LIBSRCS += dbYacc.c +LIBSRCS += dbPvdLib.c + +dbStaticHost_SRCS += dbStaticNoRun.c +dbStaticIoc_SRCS += dbStaticRun.c +dbStaticIoc_SRCS += dbStaticIocRegister.c + +LIBRARY_HOST += dbStaticHost +LIBRARY_IOC += dbStaticIoc + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=dbStaticIoc +OBJLIB_SRCS = $(LIBSRCS) $(dbStaticIoc_SRCS) +endif + +dbStaticHost_LIBS = Com +dbStaticIoc_LIBS = Com + +dbStaticHost_RCS = dbStaticHost.rc +dbStaticIoc_RCS = dbStaticIoc.rc + +PROD_LIBS := dbStaticHost Com +PROD_HOST = dbReadTest dbExpand dbToMenuH dbToRecordtypeH + +dbReadTest_SRCS = dbReadTest.c +dbExpand_SRCS = dbExpand.c +dbToMenuH_SRCS = dbToMenuH.c +dbToRecordtypeH_SRCS = dbToRecordtypeH.c + +include $(TOP)/configure/RULES + +# Extra rule since dbLexRoutines.c is included in dbYacc.c +dbYacc.c: dbLex.c ../dbLexRoutines.c + +clean:: + @$(RM) dbLex.c dbYacc.c + +# EOF Makefile.Host for base/src/dbStatic diff --git a/src/dbStatic/alarm.h b/src/dbStatic/alarm.h new file mode 100644 index 000000000..e8c098810 --- /dev/null +++ b/src/dbStatic/alarm.h @@ -0,0 +1,112 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Alarm definitions, must match menuAlarmSevr.dbd and menuAlarmStat.dbd */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * Authors: Bob Dalesio and Marty Kraimer + * Date: 11-7-90 + */ + +#ifndef INC_alarm_H +#define INC_alarm_H + +#include "shareLib.h" +#include "epicsTypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define NO_ALARM 0 + +/* ALARM SEVERITIES - must match menuAlarmSevr.dbd */ + +typedef enum { + epicsSevNone = NO_ALARM, + epicsSevMinor, + epicsSevMajor, + epicsSevInvalid, + ALARM_NSEV +} epicsAlarmSeverity; + +#define firstEpicsAlarmSev epicsSevNone +#define MINOR_ALARM epicsSevMinor +#define MAJOR_ALARM epicsSevMajor +#define INVALID_ALARM epicsSevInvalid +#define lastEpicsAlarmSev epicsSevInvalid + +epicsShareExtern const char *epicsAlarmSeverityStrings [ALARM_NSEV]; + + +/* ALARM STATUS - must match menuAlarmStat.dbd */ + +typedef enum { + epicsAlarmNone = NO_ALARM, + epicsAlarmRead, + epicsAlarmWrite, + epicsAlarmHiHi, + epicsAlarmHigh, + epicsAlarmLoLo, + epicsAlarmLow, + epicsAlarmState, + epicsAlarmCos, + epicsAlarmComm, + epicsAlarmTimeout, + epicsAlarmHwLimit, + epicsAlarmCalc, + epicsAlarmScan, + epicsAlarmLink, + epicsAlarmSoft, + epicsAlarmBadSub, + epicsAlarmUDF, + epicsAlarmDisable, + epicsAlarmSimm, + epicsAlarmReadAccess, + epicsAlarmWriteAccess, + ALARM_NSTATUS +} epicsAlarmCondition; + +#define firstEpicsAlarmCond epicsAlarmNone +#define READ_ALARM epicsAlarmRead +#define WRITE_ALARM epicsAlarmWrite +#define HIHI_ALARM epicsAlarmHiHi +#define HIGH_ALARM epicsAlarmHigh +#define LOLO_ALARM epicsAlarmLoLo +#define LOW_ALARM epicsAlarmLow +#define STATE_ALARM epicsAlarmState +#define COS_ALARM epicsAlarmCos +#define COMM_ALARM epicsAlarmComm +#define TIMEOUT_ALARM epicsAlarmTimeout +#define HW_LIMIT_ALARM epicsAlarmHwLimit +#define CALC_ALARM epicsAlarmCalc +#define SCAN_ALARM epicsAlarmScan +#define LINK_ALARM epicsAlarmLink +#define SOFT_ALARM epicsAlarmSoft +#define BAD_SUB_ALARM epicsAlarmBadSub +#define UDF_ALARM epicsAlarmUDF +#define DISABLE_ALARM epicsAlarmDisable +#define SIMM_ALARM epicsAlarmSimm +#define READ_ACCESS_ALARM epicsAlarmReadAccess +#define WRITE_ACCESS_ALARM epicsAlarmWriteAccess +#define lastEpicsAlarmCond epicsAlarmWriteAccess + +epicsShareExtern const char *epicsAlarmConditionStrings [ALARM_NSTATUS]; + +#ifdef __cplusplus +} +#endif + +#ifdef epicsAlarmGLOBAL +# include "alarmString.h" +#endif + + +#endif /* INC_alarm_H */ diff --git a/src/dbStatic/alarmString.h b/src/dbStatic/alarmString.h new file mode 100644 index 000000000..48beb74c5 --- /dev/null +++ b/src/dbStatic/alarmString.h @@ -0,0 +1,69 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ + +/* String names for alarms */ + +#ifndef INC_alarmString_H +#define INC_alarmString_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Compatibility with original alarmString.h names */ + +#define alarmSeverityString epicsAlarmSeverityStrings +#define alarmStatusString epicsAlarmConditionStrings + + +/* Name strings */ + +/* ALARM SEVERITIES - must match menuAlarmSevr.dbd and alarm.h */ + +const char * epicsAlarmSeverityStrings[] = { + "NO_ALARM", + "MINOR", + "MAJOR", + "INVALID" +}; + + +/* ALARM STATUS - must match menuAlarmStat.dbd and alarm.h */ + +const char * epicsAlarmConditionStrings[] = { + "NO_ALARM", + "READ", + "WRITE", + "HIHI", + "HIGH", + "LOLO", + "LOW", + "STATE", + "COS", + "COMM", + "TIMEOUT", + "HWLIMIT", + "CALC", + "SCAN", + "LINK", + "SOFT", + "BAD_SUB", + "UDF", + "DISABLE", + "SIMM", + "READ_ACCESS", + "WRITE_ACCESS" +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/dbStatic/dbBase.h b/src/dbStatic/dbBase.h new file mode 100644 index 000000000..552fd9e43 --- /dev/null +++ b/src/dbStatic/dbBase.h @@ -0,0 +1,173 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 + * + * Current Author: Marty Kraimer + * Date: 03-19-92 + */ + +#ifndef INCdbBaseh +#define INCdbBaseh 1 + +#include "epicsTypes.h" +#include "dbFldTypes.h" +#include "ellLib.h" +#include "dbDefs.h" + +typedef struct dbMenu { + ELLNODE node; + char *name; + int nChoice; + char **papChoiceName; + char **papChoiceValue; +}dbMenu; + +typedef struct drvSup { + ELLNODE node; + char *name; + struct drvet *pdrvet; +}drvSup; + +typedef struct devSup { + ELLNODE node; + char *name; + char *choice; + int link_type; + /*Following only available on run time system*/ + struct dset *pdset; + struct dsxt *pdsxt; /* Extended device support */ +}devSup; + +typedef struct dbDeviceMenu { + int nChoice; + char **papChoice; +}dbDeviceMenu; + +/* conversion types*/ +typedef enum {CT_DECIMAL,CT_HEX} ctType; +/* access level types */ +typedef enum {ASL0,ASL1} asLevel; + +/*Breakpoint Tables */ +typedef struct brkInt{ /* breakpoint interval */ + double raw; /*raw value for beginning of interval */ + double slope; /*slope for interval */ + double eng; /*converted value for beginning of interval*/ +}brkInt; + +typedef struct brkTable { /* breakpoint table */ + ELLNODE node; + char *name; /*breakpoint table name */ + long number; /*number of brkInt in this table*/ + struct brkInt *paBrkInt; /* ptr to array of brkInts */ +}brkTable; + +typedef struct dbFldDes{ /* field description */ + char *prompt; /*Prompt string for DCT*/ + char *name; /*Field name*/ + char *extra; /*C def for DBF_NOACCESS*/ + struct dbRecordType *pdbRecordType; + short indRecordType; /*within dbRecordType.papFldDes */ + short special; /*Special processing requirements */ + dbfType field_type; /*Field type as defined in dbFldTypes.h */ + short process_passive;/*should dbPutField process passive */ + ctType base; /*base for integer to string conversions*/ + short promptgroup; /*prompt, i.e. gui group */ + short interest; /*interest level */ + asLevel as_level; /*access security level */ + char *initial; /*initial value */ + /*If (DBF_MENU,DBF_DEVICE) ftPvt is (pdbMenu,pdbDeviceMenu) */ + void *ftPvt; + /*On no runtime following only set for STRING */ + short size; /*length in bytes of a field element */ + /*The following are only available on run time system*/ + short offset; /*Offset in bytes from beginning of record*/ +}dbFldDes; + +typedef struct dbInfoNode { /*non-field per-record information*/ + ELLNODE node; + char *name; + char *string; + void *pointer; +}dbInfoNode; + +#define DBRN_FLAGS_VISIBLE 1 +#define DBRN_FLAGS_ISALIAS 2 +#define DBRN_FLAGS_HASALIAS 4 + +typedef struct dbRecordNode { + ELLNODE node; + void *precord; + char *recordname; + ELLLIST infoList; /*LIST head of info nodes*/ + int flags; +}dbRecordNode; + +/*dbRecordAttribute is for "psuedo" fields */ +/*pdbFldDes is so that other access routines work correctly*/ +/*Until base supports char * value MUST be fixed length string*/ +typedef struct dbRecordAttribute { + ELLNODE node; + char *name; + dbFldDes *pdbFldDes; + char value[MAX_STRING_SIZE]; +}dbRecordAttribute; + +typedef struct dbText { + ELLNODE node; + char *text; +}dbText; + +typedef struct dbVariableDef { + ELLNODE node; + char *name; + char *type; + +}dbVariableDef; + +typedef struct dbRecordType { + ELLNODE node; + ELLLIST attributeList; /*LIST head of attributes*/ + ELLLIST recList; /*LIST head of sorted dbRecordNodes*/ + ELLLIST devList; /*List of associated device support*/ + ELLLIST cdefList; /*LIST of Cdef text items*/ + char *name; + short no_fields; /* number of fields defined */ + short no_prompt; /* number of fields to configure*/ + short no_links; /* number of links */ + short no_aliases; /* number of aliases in recList */ + short *link_ind; /* addr of array of ind in papFldDes*/ + char **papsortFldName;/* ptr to array of ptr to fld names*/ + short *sortFldInd; /* addr of array of ind in papFldDes*/ + dbFldDes *pvalFldDes; /*pointer dbFldDes for VAL field*/ + short indvalFlddes; /*ind in papFldDes*/ + dbFldDes **papFldDes; /* ptr to array of ptr to fldDes*/ + /*The following are only available on run time system*/ + struct rset *prset; + int rec_size; /*record size in bytes */ +}dbRecordType; + +struct dbPvd; /* Contents private to dbPvdLib code */ +struct gphPvt; /* Contents private to gpHashLib code */ + +typedef struct dbBase { + ELLLIST menuList; + ELLLIST recordTypeList; + ELLLIST drvList; + ELLLIST registrarList; + ELLLIST functionList; + ELLLIST variableList; + ELLLIST bptList; + void *pathPvt; + struct dbPvd *ppvd; + struct gphPvt *pgpHash; + short ignoreMissingMenus; + short loadCdefs; +}dbBase; +#endif diff --git a/src/dbStatic/dbExpand.c b/src/dbStatic/dbExpand.c new file mode 100644 index 000000000..80c95cabd --- /dev/null +++ b/src/dbStatic/dbExpand.c @@ -0,0 +1,123 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbExpand.c */ +/* Author: Marty Kraimer Date: 30NOV95 */ + +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errMdef.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "dbBase.h" +#include "gpHash.h" +#include "osiFileName.h" + +DBBASE *pdbbase = NULL; + +void usage(void) +{ + fprintf(stderr, "Usage:\n\tdbExpand -Ipath -ooutfile " + "-S macro=value file1.dbd file2.dbd ...\n"); + fprintf(stderr,"Specifying any path will replace the default of '.'\n"); +} + +int main(int argc,char **argv) +{ + char *path = NULL; + char *sub = NULL; + int pathLength = 0; + int subLength = 0; + char *outFilename = NULL; + FILE *outFP = stdout; + long status; + long returnStatus = 0; + static char *pathSep = OSI_PATH_LIST_SEPARATOR; + static char *subSep = ","; + + /* Discard program name argv[0] */ + ++argv; + --argc; + + while ((argc > 1) && (**argv == '-')) { + char optLtr = (*argv)[1]; + char *optArg; + + if (strlen(*argv) > 2) { + optArg = *argv+2; + ++argv; + --argc; + } else { + optArg = argv[1]; + argv += 2; + argc -= 2; + } + + switch (optLtr) { + case 'o': + outFilename = optArg; + break; + + case 'I': + dbCatString(&path, &pathLength, optArg, pathSep); + break; + + case 'S': + dbCatString(&sub, &subLength, optArg, subSep); + break; + + default: + fprintf(stderr, "dbExpand: Unknown option '-%c'\n", optLtr); + usage(); + exit(1); + } + } + + if (argc < 1) { + fprintf(stderr, "dbExpand: No input file specified\n"); + usage(); + exit(1); + } + + for (; argc>0; --argc, ++argv) { + status = dbReadDatabase(&pdbbase,*argv,path,sub); + if (status) returnStatus = status; + } + if (returnStatus) { + errlogFlush(); + fprintf(stderr, "dbExpand: Input errors, no output generated\n"); + exit(1); + } + if (outFilename) { + outFP = fopen(outFilename, "w"); + if (!outFP) { + perror("dbExpand"); + exit(1); + } + } + + dbWriteMenuFP(pdbbase,outFP,0); + dbWriteRecordTypeFP(pdbbase,outFP,0); + dbWriteDeviceFP(pdbbase,outFP); + dbWriteDriverFP(pdbbase,outFP); + dbWriteRegistrarFP(pdbbase,outFP); + dbWriteFunctionFP(pdbbase,outFP); + dbWriteVariableFP(pdbbase,outFP); + dbWriteBreaktableFP(pdbbase,outFP); + dbWriteRecordFP(pdbbase,outFP,0,0); + + free((void *)path); + free((void *)sub); + return 0; +} diff --git a/src/dbStatic/dbFldTypes.h b/src/dbStatic/dbFldTypes.h new file mode 100644 index 000000000..26b2acf6c --- /dev/null +++ b/src/dbStatic/dbFldTypes.h @@ -0,0 +1,93 @@ +/*************************************************************************\ +* 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: Marty Kraimer + * Date: 6-1-90 + */ +#ifndef INCdbFldTypesh +#define INCdbFldTypesh 1 + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* field types */ +typedef enum { + DBF_STRING, + DBF_CHAR, + DBF_UCHAR, + DBF_SHORT, + DBF_USHORT, + DBF_LONG, + DBF_ULONG, + DBF_FLOAT, + DBF_DOUBLE, + DBF_ENUM, + DBF_MENU, + DBF_DEVICE, + DBF_INLINK, + DBF_OUTLINK, + DBF_FWDLINK, + DBF_NOACCESS +}dbfType; +#define DBF_NTYPES DBF_NOACCESS+1 + +typedef struct mapdbfType{ + char *strvalue; + dbfType value; +}mapdbfType; + +epicsShareExtern mapdbfType pamapdbfType[]; +#ifdef DBFLDTYPES_GBLSOURCE +epicsShareDef mapdbfType pamapdbfType[DBF_NTYPES] = { + {"DBF_STRING",DBF_STRING}, + {"DBF_CHAR",DBF_CHAR}, + {"DBF_UCHAR",DBF_UCHAR}, + {"DBF_SHORT",DBF_SHORT}, + {"DBF_USHORT",DBF_USHORT}, + {"DBF_LONG",DBF_LONG}, + {"DBF_ULONG",DBF_ULONG}, + {"DBF_FLOAT",DBF_FLOAT}, + {"DBF_DOUBLE",DBF_DOUBLE}, + {"DBF_ENUM",DBF_ENUM}, + {"DBF_MENU",DBF_MENU}, + {"DBF_DEVICE",DBF_DEVICE}, + {"DBF_INLINK",DBF_INLINK}, + {"DBF_OUTLINK",DBF_OUTLINK}, + {"DBF_FWDLINK",DBF_FWDLINK}, + {"DBF_NOACCESS",DBF_NOACCESS} +}; +#endif /*DBFLDTYPES_GBLSOURCE*/ + +/* data request buffer types */ +#define DBR_STRING DBF_STRING +#define DBR_CHAR DBF_CHAR +#define DBR_UCHAR DBF_UCHAR +#define DBR_SHORT DBF_SHORT +#define DBR_USHORT DBF_USHORT +#define DBR_LONG DBF_LONG +#define DBR_ULONG DBF_ULONG +#define DBR_FLOAT DBF_FLOAT +#define DBR_DOUBLE DBF_DOUBLE +#define DBR_ENUM DBF_ENUM +#define DBR_PUT_ACKT DBR_ENUM+1 +#define DBR_PUT_ACKS DBR_PUT_ACKT+1 +#define DBR_NOACCESS DBF_NOACCESS +#define VALID_DB_REQ(x) ((x >= 0) && (x <= DBR_ENUM)) +#define INVALID_DB_REQ(x) ((x < 0) || (x > DBR_ENUM)) + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbFldTypesh*/ diff --git a/src/dbStatic/dbLex.l b/src/dbStatic/dbLex.l new file mode 100644 index 000000000..c06a99242 --- /dev/null +++ b/src/dbStatic/dbLex.l @@ -0,0 +1,90 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +newline "\n" +backslash "\\" +doublequote "\"" +comment "#" +whitespace [ \t\r\n] +escape {backslash}. +stringchar [^"\n\\] +bareword [a-zA-Z0-9_\-+:.\[\]<>;] + +%{ +#undef YY_INPUT +#define YY_INPUT(b,r,ms) (r=(*db_yyinput)((char *)b,ms)) + +static int yyreset(void) +{ + BEGIN INITIAL; + return(0); +} + +%} + +%% + +"include" return(tokenINCLUDE); +"path" return(tokenPATH); +"addpath" return(tokenADDPATH); +"menu" return(tokenMENU); +"choice" return(tokenCHOICE); +"recordtype" return(tokenRECORDTYPE); +"field" return(tokenFIELD); +"device" return(tokenDEVICE); +"driver" return(tokenDRIVER); +"breaktable" return(tokenBREAKTABLE); +"record" return(tokenRECORD); +"grecord" return(tokenGRECORD); +"alias" return(tokenALIAS); +"info" return(tokenINFO); +"registrar" return(tokenREGISTRAR); +"function" return(tokenFUNCTION); +"variable" return(tokenVARIABLE); + +{bareword}+ { /* unquoted string or number */ + yylval.Str = dbmfStrdup(yytext); + return(tokenSTRING); +} + +{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */ + yylval.Str = dbmfStrdup(yytext+1); + yylval.Str[strlen(yylval.Str)-1] = '\0'; + return(tokenSTRING); +} + +%.* { /*C definition in recordtype*/ + yylval.Str = dbmfStrdup(yytext+1); + return(tokenCDEFS); +} + +"{" return(yytext[0]); +"}" return(yytext[0]); +"(" return(yytext[0]); +")" return(yytext[0]); +"," return(yytext[0]); + +{comment}.* ; +{whitespace} ; + +{doublequote}({stringchar}|{escape})*{newline} { /* bad string */ + yyerror("Newline in string, closing quote missing"); +} + +. { + char message[40]; + YY_BUFFER_STATE *dummy=0; + + sprintf(message,"Invalid character '%c'",yytext[0]); + yyerror(message); + /*The following suppresses compiler warning messages*/ + if(FALSE) yyunput('c',(unsigned char *) message); + if(FALSE) yy_switch_to_buffer(*dummy); +} + +%% diff --git a/src/dbStatic/dbLexRoutines.c b/src/dbStatic/dbLexRoutines.c new file mode 100644 index 000000000..5a9ca76e6 --- /dev/null +++ b/src/dbStatic/dbLexRoutines.c @@ -0,0 +1,1041 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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: Marty Kraimer Date: 13JUL95*/ + +/*The routines in this module are serially reusable NOT reentrant*/ + +#include +#include +#include +#include + +#include "dbmf.h" + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errMdef.h" +#include "ellLib.h" +#include "gpHash.h" +#include "freeList.h" +#include "guigroup.h" +#include "special.h" +#include "macLib.h" +#include "epicsString.h" +#include "epicsExport.h" + +#define epicsExportSharedSymbols +#include "dbFldTypes.h" +#include "link.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" + + + +/*global declarations*/ +epicsShareDef char *makeDbdDepends=0; + +epicsShareDef int dbRecordsOnceOnly=0; +epicsExportAddress(int,dbRecordsOnceOnly); + +epicsShareDef int dbBptNotMonotonic=0; +epicsExportAddress(int,dbBptNotMonotonic); + +/*private routines */ +static void yyerrorAbort(char *str); +static void allocTemp(void *pvoid); +static void *popFirstTemp(void); +static void *getLastTemp(void); +static int db_yyinput(char *buf,int max_size); +static void dbIncludePrint(void); +static void dbPathCmd(char *path); +static void dbAddPathCmd(char *path); +static void dbIncludeNew(char *include_file); +static void dbMenuHead(char *name); +static void dbMenuChoice(char *name,char *value); +static void dbMenuBody(void); + +static void dbRecordtypeHead(char *name); +static void dbRecordtypeBody(void); +static void dbRecordtypeFieldHead(char *name,char *type); +static void dbRecordtypeFieldItem(char *name,char *value); + +static void dbDevice(char *recordtype,char *linktype, + char *dsetname,char *choicestring); +static void dbDriver(char *name); +static void dbRegistrar(char *name); +static void dbFunction(char *name); +static void dbVariable(char *name, char *type); + +static void dbBreakHead(char *name); +static void dbBreakItem(char *value); +static void dbBreakBody(void); + +static void dbRecordHead(char *recordType,char*name,int visible); +static void dbRecordField(char *name,char *value); +static void dbRecordBody(void); + +/*private declarations*/ +#define MY_BUFFER_SIZE 1024 +static char *my_buffer=NULL; +static char *mac_input_buffer=NULL; +static char *my_buffer_ptr=NULL; +static MAC_HANDLE *macHandle = NULL; +typedef struct inputFile{ + ELLNODE node; + char *path; + char *filename; + FILE *fp; + int line_num; +}inputFile; +static ELLLIST inputFileList = ELLLIST_INIT; + +static inputFile *pinputFileNow = NULL; +static DBBASE *pdbbase = NULL; + +typedef struct tempListNode { + ELLNODE node; + void *item; +}tempListNode; + +static ELLLIST tempList = ELLLIST_INIT; +static void *freeListPvt = NULL; +static int duplicate = FALSE; + +static void yyerrorAbort(char *str) +{ + yyerror(str); + yyAbort = TRUE; + while (ellCount(&tempList)) + popFirstTemp(); +} + +static void allocTemp(void *pvoid) +{ + tempListNode *ptempListNode; + + ptempListNode = freeListCalloc(freeListPvt); + ptempListNode->item = pvoid; + ellAdd(&tempList,&ptempListNode->node); +} + +static void *popFirstTemp(void) +{ + tempListNode *ptempListNode; + void *ptemp; + + ptempListNode = (tempListNode *)ellFirst(&tempList); + ptemp = ptempListNode->item; + ellDelete(&tempList,(ELLNODE *)ptempListNode); + freeListFree(freeListPvt,ptempListNode); + return(ptemp); +} + +static void *getLastTemp(void) +{ + tempListNode *ptempListNode; + + ptempListNode = (tempListNode *)ellLast(&tempList); + return(ptempListNode->item); +} + +static char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp) +{ + ELLLIST *ppathList = (ELLLIST *)pdbbase->pathPvt; + dbPathNode *pdbPathNode; + char *fullfilename; + + *fp = 0; + if (!filename) return 0; + if (!ppathList || ellCount(ppathList) == 0 || + strchr(filename, '/') || strchr(filename, '\\')) { + *fp = fopen(filename, "r"); + if (*fp && makeDbdDepends) + fprintf(stdout, "%s:%s \n", makeDbdDepends, filename); + return 0; + } + pdbPathNode = (dbPathNode *)ellFirst(ppathList); + while (pdbPathNode) { + fullfilename = dbMalloc(strlen(pdbPathNode->directory) + + strlen(filename) + 2); + strcpy(fullfilename, pdbPathNode->directory); + strcat(fullfilename, "/"); + strcat(fullfilename, filename); + *fp = fopen(fullfilename, "r"); + if (*fp && makeDbdDepends) + fprintf(stdout, "%s:%s \n", makeDbdDepends, fullfilename); + free((void *)fullfilename); + if (*fp) return pdbPathNode->directory; + pdbPathNode = (dbPathNode *)ellNext(&pdbPathNode->node); + } + return 0; +} + + +static void freeInputFileList(void) +{ + inputFile *pinputFileNow; + + while((pinputFileNow=(inputFile *)ellFirst(&inputFileList))) { + if(fclose(pinputFileNow->fp)) + errPrintf(0,__FILE__, __LINE__, + "Closing file %s",pinputFileNow->filename); + free((void *)pinputFileNow->filename); + ellDelete(&inputFileList,(ELLNODE *)pinputFileNow); + free((void *)pinputFileNow); + } +} + +static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, + const char *path,const char *substitutions) +{ + long status; + inputFile *pinputFile = NULL; + char *penv; + char **macPairs; + + if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); + pdbbase = *ppdbbase; + if(path && strlen(path)>0) { + dbPath(pdbbase,path); + } else { + penv = getenv("EPICS_DB_INCLUDE_PATH"); + if(penv) { + dbPath(pdbbase,penv); + } else { + dbPath(pdbbase,"."); + } + } + my_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char)); + freeListInitPvt(&freeListPvt,sizeof(tempListNode),100); + if(substitutions) { + if(macCreateHandle(&macHandle,NULL)) { + epicsPrintf("macCreateHandle error\n"); + status = -1; + goto cleanup; + } + macParseDefns(macHandle,(char *)substitutions,&macPairs); + if(macPairs ==NULL) { + macDeleteHandle(macHandle); + macHandle = NULL; + } else { + macInstallMacros(macHandle,macPairs); + free((void *)macPairs); + mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char)); + } + } + pinputFile = dbCalloc(1,sizeof(inputFile)); + if(filename) { + pinputFile->filename = macEnvExpand(filename); + } + if(!fp) { + FILE *fp1; + + if(pinputFile->filename) pinputFile->path = dbOpenFile(pdbbase,pinputFile->filename,&fp1); + if(!pinputFile->filename || !fp1) { + errPrintf(0,__FILE__, __LINE__, + "dbRead opening file %s",pinputFile->filename); + free((void *)pinputFile->filename); + free((void *)pinputFile); + status = -1; + goto cleanup; + } + pinputFile->fp = fp1; + } else { + pinputFile->fp = fp; + } + pinputFile->line_num = 0; + pinputFileNow = pinputFile; + my_buffer[0] = '\0'; + my_buffer_ptr = my_buffer; + ellAdd(&inputFileList,&pinputFile->node); + status = pvt_yy_parse(); + dbFreePath(pdbbase); + if(!status) { /*add RTYP and VERS as an attribute */ + DBENTRY dbEntry; + DBENTRY *pdbEntry = &dbEntry; + long localStatus; + + dbInitEntry(pdbbase,pdbEntry); + localStatus = dbFirstRecordType(pdbEntry); + while(!localStatus) { + localStatus = dbPutRecordAttribute(pdbEntry,"RTYP", + dbGetRecordTypeName(pdbEntry)); + if(!localStatus) { + localStatus = dbPutRecordAttribute(pdbEntry,"VERS", + "none specified"); + } + if(localStatus) { + fprintf(stderr,"dbPutRecordAttribute status %ld\n",status); + } else { + localStatus = dbNextRecordType(pdbEntry); + } + } + dbFinishEntry(pdbEntry); + } +cleanup: + if(macHandle) macDeleteHandle(macHandle); + macHandle = NULL; + if(mac_input_buffer) free((void *)mac_input_buffer); + mac_input_buffer = NULL; + if(freeListPvt) freeListCleanup(freeListPvt); + freeListPvt = NULL; + if(my_buffer) free((void *)my_buffer); + my_buffer = NULL; + freeInputFileList(); + return(status); +} + +long epicsShareAPI dbReadDatabase(DBBASE **ppdbbase,const char *filename, + const char *path,const char *substitutions) +{return (dbReadCOM(ppdbbase,filename,0,path,substitutions));} + +long epicsShareAPI dbReadDatabaseFP(DBBASE **ppdbbase,FILE *fp, + const char *path,const char *substitutions) +{return (dbReadCOM(ppdbbase,0,fp,path,substitutions));} + +static int db_yyinput(char *buf, int max_size) +{ + int l,n; + char *fgetsRtn; + + if(yyAbort) return(0); + if(*my_buffer_ptr==0) { + while(TRUE) { /*until we get some input*/ + if(macHandle) { + fgetsRtn = fgets(mac_input_buffer,MY_BUFFER_SIZE, + pinputFileNow->fp); + if(fgetsRtn) { + n = macExpandString(macHandle,mac_input_buffer, + my_buffer,MY_BUFFER_SIZE); + if(n<0) { + errPrintf(0,__FILE__, __LINE__, + "macExpandString failed for file %s", + pinputFileNow->filename); + } + } + } else { + fgetsRtn = fgets(my_buffer,MY_BUFFER_SIZE,pinputFileNow->fp); + } + if(fgetsRtn) break; + if(fclose(pinputFileNow->fp)) + errPrintf(0,__FILE__, __LINE__, + "Closing file %s",pinputFileNow->filename); + free((void *)pinputFileNow->filename); + ellDelete(&inputFileList,(ELLNODE *)pinputFileNow); + free((void *)pinputFileNow); + pinputFileNow = (inputFile *)ellLast(&inputFileList); + if(!pinputFileNow) return(0); + } + if(dbStaticDebug) fprintf(stderr,"%s",my_buffer); + pinputFileNow->line_num++; + my_buffer_ptr = &my_buffer[0]; + } + l = strlen(my_buffer_ptr); + n = (l<=max_size ? l : max_size); + memcpy(buf,my_buffer_ptr,n); + my_buffer_ptr += n; + return(n); +} + +static void dbIncludePrint(void) +{ + inputFile *pinputFile = pinputFileNow; + + while (pinputFile) { + epicsPrintf(" in"); + if (pinputFile->path) + epicsPrintf(" path \"%s\" ",pinputFile->path); + if (pinputFile->filename) { + epicsPrintf(" file \"%s\"",pinputFile->filename); + } else { + epicsPrintf(" standard input"); + } + epicsPrintf(" line %d\n",pinputFile->line_num); + pinputFile = (inputFile *)ellPrevious(&pinputFile->node); + } + return; +} + +static void dbPathCmd(char *path) +{ + dbPath(pdbbase,path); +} + +static void dbAddPathCmd(char *path) +{ + dbAddPath(pdbbase,path); +} + +static void dbIncludeNew(char *filename) +{ + inputFile *pinputFile; + FILE *fp; + + pinputFile = dbCalloc(1,sizeof(inputFile)); + pinputFile->filename = macEnvExpand(filename); + pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp); + if (!fp) { + epicsPrintf("Can't open include file \"%s\"\n", filename); + yyerror(NULL); + free((void *)pinputFile->filename); + free((void *)pinputFile); + return; + } + pinputFile->fp = fp; + ellAdd(&inputFileList,&pinputFile->node); + pinputFileNow = pinputFile; +} + +static void dbMenuHead(char *name) +{ + dbMenu *pdbMenu; + GPHENTRY *pgphentry; + + pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList); + if(pgphentry) { + duplicate = TRUE; + return; + } + if(ellCount(&tempList)) yyerrorAbort("dbMenuHead: tempList not empty"); + pdbMenu = dbCalloc(1,sizeof(dbMenu)); + pdbMenu->name = epicsStrDup(name); + allocTemp(pdbMenu); +} + +static void dbMenuChoice(char *name,char *value) +{ + if(duplicate) return; + allocTemp(epicsStrDup(name)); + allocTemp(epicsStrDup(value)); +} + +static void dbMenuBody(void) +{ + dbMenu *pnewMenu; + dbMenu *pMenu; + int nChoice; + int i; + GPHENTRY *pgphentry; + + if(duplicate) { + duplicate = FALSE; + return; + } + pnewMenu = (dbMenu *)popFirstTemp(); + pnewMenu->nChoice = nChoice = ellCount(&tempList)/2; + pnewMenu->papChoiceName = dbCalloc(pnewMenu->nChoice,sizeof(char *)); + pnewMenu->papChoiceValue = dbCalloc(pnewMenu->nChoice,sizeof(char *)); + for(i=0; ipapChoiceName[i] = (char *)popFirstTemp(); + pnewMenu->papChoiceValue[i] = (char *)popFirstTemp(); + } + if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty"); + /* Add menu in sorted order */ + pMenu = (dbMenu *)ellFirst(&pdbbase->menuList); + while(pMenu && strcmp(pMenu->name,pnewMenu->name) >0 ) + pMenu = (dbMenu *)ellNext(&pMenu->node); + if(pMenu) + ellInsert(&pdbbase->menuList,ellPrevious(&pMenu->node),&pnewMenu->node); + else + ellAdd(&pdbbase->menuList,&pnewMenu->node); + pgphentry = gphAdd(pdbbase->pgpHash,pnewMenu->name,&pdbbase->menuList); + if(!pgphentry) { + yyerrorAbort("gphAdd failed"); + } else { + pgphentry->userPvt = pnewMenu; + } +} + +static void dbRecordtypeHead(char *name) +{ + dbRecordType *pdbRecordType; + GPHENTRY *pgphentry; + + pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList); + if(pgphentry) { + duplicate = TRUE; + return; + } + pdbRecordType = dbCalloc(1,sizeof(dbRecordType)); + pdbRecordType->name = epicsStrDup(name); + if (pdbbase->loadCdefs) ellInit(&pdbRecordType->cdefList); + if(ellCount(&tempList)) + yyerrorAbort("dbRecordtypeHead tempList not empty"); + allocTemp(pdbRecordType); +} + +static void dbRecordtypeFieldHead(char *name,char *type) +{ + dbFldDes *pdbFldDes; + int i; + + if(duplicate) return; + pdbFldDes = dbCalloc(1,sizeof(dbFldDes)); + allocTemp(pdbFldDes); + pdbFldDes->name = epicsStrDup(name); + pdbFldDes->as_level = ASL1; + for(i=0; ifield_type = pamapdbfType[i].value; + return; + } + } + yyerrorAbort("Illegal Field Type"); +} + +static void dbRecordtypeFieldItem(char *name,char *value) +{ + dbFldDes *pdbFldDes; + + if(duplicate) return; + pdbFldDes = (dbFldDes *)getLastTemp(); + if(strcmp(name,"asl")==0) { + if(strcmp(value,"ASL0")==0) { + pdbFldDes->as_level = ASL0; + } else if(strcmp(value,"ASL1")==0) { + pdbFldDes->as_level = ASL1; + } else { + yyerror("Illegal Access Security value: Must be ASL0 or ASL1"); + } + return; + } + if(strcmp(name,"initial")==0) { + pdbFldDes->initial = epicsStrDup(value); + return; + } + if(strcmp(name,"promptgroup")==0) { + int i; + for(i=0; ipromptgroup = pamapguiGroup[i].value; + return; + } + } + yyerror("Illegal promptgroup. See guigroup.h for legal values"); + return; + } + if(strcmp(name,"prompt")==0) { + pdbFldDes->prompt = epicsStrDup(value); + return; + } + if(strcmp(name,"special")==0) { + int i; + for(i=0; ispecial = pamapspcType[i].value; + return; + } + } + if(sscanf(value,"%hd",&pdbFldDes->special)==1) { + return; + } + yyerror("Illegal special value."); + return; + } + if(strcmp(name,"pp")==0) { + if((strcmp(value,"YES")==0) || (strcmp(value,"TRUE")==0)) { + pdbFldDes->process_passive = TRUE; + } else if((strcmp(value,"NO")==0) || (strcmp(value,"FALSE")==0)) { + pdbFldDes->process_passive = FALSE; + } else { + yyerror("Illegal value. Must be NO or YES"); + } + return; + } + if(strcmp(name,"interest")==0) { + if(sscanf(value,"%hd",&pdbFldDes->interest)!=1) + yyerror("Illegal value. Must be integer"); + return; + } + if(strcmp(name,"base")==0) { + if(strcmp(value,"DECIMAL")==0) { + pdbFldDes->base = CT_DECIMAL; + } else if(strcmp(value,"HEX")==0) { + pdbFldDes->base = CT_HEX; + } else { + yyerror("Illegal value. Must be CT_DECIMAL or CT_HEX"); + } + return; + } + if(strcmp(name,"size")==0) { + if(sscanf(value,"%hd",&pdbFldDes->size)!=1) + yyerror("Illegal value. Must be integer"); + return; + } + if(strcmp(name,"extra")==0) { + pdbFldDes->extra = epicsStrDup(value); + return; + } + if(strcmp(name,"menu")==0) { + pdbFldDes->ftPvt = (dbMenu *)dbFindMenu(pdbbase,value); + if(!pdbbase->ignoreMissingMenus && !pdbFldDes->ftPvt) + yyerrorAbort("menu not found"); + return; + } +} + +static void dbRecordtypeCdef(char *text) { + dbText *pdbCdef; + tempListNode *ptempListNode; + dbRecordType *pdbRecordType; + + if (!pdbbase->loadCdefs || duplicate) return; + ptempListNode = (tempListNode *)ellFirst(&tempList); + pdbRecordType = ptempListNode->item; + + pdbCdef = dbCalloc(1,sizeof(dbText)); + if (text[0] == ' ') text++; /* strip leading space if present */ + pdbCdef->text = epicsStrDup(text); + ellAdd(&pdbRecordType->cdefList, &pdbCdef->node); + return; +} + +static void dbRecordtypeBody(void) +{ + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + int i,j,ilink; + GPHENTRY *pgphentry; + int no_fields,no_prompt,no_links; + dbfType field_type; + char *psortFldNameTemp; + short psortFldIndTemp; + char **papsortFldName; + short *sortFldInd; + + if(duplicate) { + duplicate = FALSE; + return; + } + pdbRecordType= (dbRecordType *)popFirstTemp(); + pdbRecordType->no_fields = no_fields = ellCount(&tempList); + pdbRecordType->papFldDes = dbCalloc(no_fields,sizeof(dbFldDes *)); + pdbRecordType->papsortFldName = dbCalloc(no_fields,sizeof(char *)); + pdbRecordType->sortFldInd = dbCalloc(no_fields,sizeof(short)); + no_prompt = no_links = 0; + for(i=0; ipdbRecordType = pdbRecordType; + pdbFldDes->indRecordType = i; + pdbRecordType->papFldDes[i] = pdbFldDes; + if(pdbFldDes->promptgroup) no_prompt++; + field_type = pdbFldDes->field_type; + if((field_type>=DBF_INLINK) && (field_type<=DBF_FWDLINK))no_links++; + if((field_type==DBF_STRING) && (pdbFldDes->size==0)) + fprintf(stderr,"recordtype(%s).%s size not specified\n", + pdbRecordType->name,pdbFldDes->name); + if((field_type==DBF_NOACCESS) && (pdbFldDes->extra==0)) + fprintf(stderr,"recordtype(%s).%s extra not specified\n", + pdbRecordType->name,pdbFldDes->name); + } + if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty"); + pdbRecordType->no_prompt = no_prompt; + pdbRecordType->no_links = no_links; + pdbRecordType->link_ind = dbCalloc(no_links,sizeof(short)); + ilink = 0; + for(i=0; ipapFldDes[i]; + /* if prompt is null make it a null string */ + if(!pdbFldDes->prompt) pdbFldDes->prompt = dbCalloc(1,sizeof(char)); + field_type = pdbFldDes->field_type; + if((field_type>=DBF_INLINK) && (field_type<=DBF_FWDLINK)) + pdbRecordType->link_ind[ilink++] = i; + if(strcmp(pdbFldDes->name,"VAL")==0) { + pdbRecordType->pvalFldDes = pdbRecordType->papFldDes[i]; + pdbRecordType->indvalFlddes = i; + } + pdbRecordType->papsortFldName[i] = pdbFldDes->name; + pdbRecordType->sortFldInd[i] = i; + } + /*Now sort fields. Sorry dumb sort algorithm */ + papsortFldName = pdbRecordType->papsortFldName; + sortFldInd = pdbRecordType->sortFldInd; + for(i=0; iattributeList); + ellInit(&pdbRecordType->recList); + ellInit(&pdbRecordType->devList); + pgphentry = gphAdd(pdbbase->pgpHash,pdbRecordType->name, + &pdbbase->recordTypeList); + if(!pgphentry) { + yyerrorAbort("gphAdd failed"); + } else { + pgphentry->userPvt = pdbRecordType; + } + ellAdd(&pdbbase->recordTypeList,&pdbRecordType->node); +} + +static void dbDevice(char *recordtype,char *linktype, + char *dsetname,char *choicestring) +{ + devSup *pdevSup; + dbRecordType *pdbRecordType; + GPHENTRY *pgphentry; + int i,link_type; + pgphentry = gphFind(pdbbase->pgpHash,recordtype,&pdbbase->recordTypeList); + if(!pgphentry) { + epicsPrintf("Record type \"%s\" not found for device \"%s\"\n", + recordtype, choicestring); + yyerror(NULL); + return; + } + link_type=-1; + for(i=0; iuserPvt; + pgphentry = gphFind(pdbbase->pgpHash,choicestring,&pdbRecordType->devList); + if(pgphentry) { + return; + } + pdevSup = dbCalloc(1,sizeof(devSup)); + pdevSup->name = epicsStrDup(dsetname); + pdevSup->choice = epicsStrDup(choicestring); + pdevSup->link_type = link_type; + pgphentry = gphAdd(pdbbase->pgpHash,pdevSup->choice,&pdbRecordType->devList); + if(!pgphentry) { + yyerrorAbort("gphAdd failed"); + } else { + pgphentry->userPvt = pdevSup; + } + ellAdd(&pdbRecordType->devList,&pdevSup->node); +} + +static void dbDriver(char *name) +{ + drvSup *pdrvSup; + GPHENTRY *pgphentry; + + pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList); + if(pgphentry) { + return; + } + pdrvSup = dbCalloc(1,sizeof(drvSup)); + pdrvSup->name = epicsStrDup(name); + pgphentry = gphAdd(pdbbase->pgpHash,pdrvSup->name,&pdbbase->drvList); + if(!pgphentry) { + yyerrorAbort("gphAdd failed"); + } + pgphentry->userPvt = pdrvSup; + ellAdd(&pdbbase->drvList,&pdrvSup->node); +} + +static void dbRegistrar(char *name) +{ + dbText *ptext; + GPHENTRY *pgphentry; + + pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList); + if(pgphentry) { + return; + } + ptext = dbCalloc(1,sizeof(dbText)); + ptext->text = epicsStrDup(name); + pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->registrarList); + if(!pgphentry) { + yyerrorAbort("gphAdd failed"); + } + pgphentry->userPvt = ptext; + ellAdd(&pdbbase->registrarList,&ptext->node); +} + +static void dbFunction(char *name) +{ + dbText *ptext; + GPHENTRY *pgphentry; + + pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList); + if(pgphentry) { + return; + } + ptext = dbCalloc(1,sizeof(dbText)); + ptext->text = epicsStrDup(name); + pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->functionList); + if(!pgphentry) { + yyerrorAbort("gphAdd failed"); + } + pgphentry->userPvt = ptext; + ellAdd(&pdbbase->functionList,&ptext->node); +} + +static void dbVariable(char *name, char *type) +{ + dbVariableDef *pvar; + GPHENTRY *pgphentry; + + pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList); + if(pgphentry) { + return; + } + pvar = dbCalloc(1,sizeof(dbVariableDef)); + pvar->name = epicsStrDup(name); + pvar->type = epicsStrDup(type); + pgphentry = gphAdd(pdbbase->pgpHash,pvar->name,&pdbbase->variableList); + if(!pgphentry) { + yyerrorAbort("gphAdd failed"); + } + pgphentry->userPvt = pvar; + ellAdd(&pdbbase->variableList,&pvar->node); +} + +static void dbBreakHead(char *name) +{ + brkTable *pbrkTable; + GPHENTRY *pgphentry; + + pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList); + if(pgphentry) { + duplicate = TRUE; + return; + } + pbrkTable = dbCalloc(1,sizeof(brkTable)); + pbrkTable->name = epicsStrDup(name); + if(ellCount(&tempList)) yyerrorAbort("dbBreakHead:tempList not empty"); + allocTemp(pbrkTable); +} + +static void dbBreakItem(char *value) +{ + double dummy; + if (duplicate) return; + if (epicsScanDouble(value, &dummy) != 1) { + yyerrorAbort("Non-numeric value in breaktable"); + } + allocTemp(epicsStrDup(value)); +} + +static void dbBreakBody(void) +{ + brkTable *pnewbrkTable; + brkInt *paBrkInt; + brkTable *pbrkTable; + int number, down=0; + int i; + GPHENTRY *pgphentry; + + if (duplicate) { + duplicate = FALSE; + return; + } + pnewbrkTable = (brkTable *)popFirstTemp(); + number = ellCount(&tempList); + if (number % 2) { + yyerrorAbort("breaktable: Raw value missing"); + return; + } + number /= 2; + if (number < 2) { + yyerrorAbort("breaktable: Must have at least two points!"); + return; + } + pnewbrkTable->number = number; + pnewbrkTable->paBrkInt = paBrkInt = dbCalloc(number, sizeof(brkInt)); + for (i=0; ibptList); + while (pbrkTable) { + if (strcmp(pbrkTable->name, pnewbrkTable->name) > 0) { + ellInsert(&pdbbase->bptList, ellPrevious((ELLNODE *)pbrkTable), + (ELLNODE *)pnewbrkTable); + break; + } + pbrkTable = (brkTable *)ellNext(&pbrkTable->node); + } + if (!pbrkTable) ellAdd(&pdbbase->bptList, &pnewbrkTable->node); + pgphentry = gphAdd(pdbbase->pgpHash,pnewbrkTable->name,&pdbbase->bptList); + if (!pgphentry) { + yyerrorAbort("dbBreakBody: gphAdd failed"); + return; + } + pgphentry->userPvt = pnewbrkTable; +} + +static void dbRecordHead(char *recordType,char *name, int visible) +{ + DBENTRY *pdbentry; + long status; + + pdbentry = dbAllocEntry(pdbbase); + if(ellCount(&tempList)) + yyerrorAbort("dbRecordHead: tempList not empty"); + allocTemp(pdbentry); + status = dbFindRecordType(pdbentry,recordType); + if(status) { + epicsPrintf("Record \"%s\" is of unknown type \"%s\" - ", + name, recordType); + yyerrorAbort(NULL); + return; + } + /*Duplicate records ok if the same type */ + status = dbCreateRecord(pdbentry,name); + if(status==S_dbLib_recExists) { + if(strcmp(recordType,dbGetRecordTypeName(pdbentry))!=0) { + epicsPrintf("Record %s already defined with different type %s\n", + name, dbGetRecordTypeName(pdbentry)); + yyerror(NULL); + duplicate = TRUE; + return; + } else if (dbRecordsOnceOnly) { + epicsPrintf("Record \"%s\" already defined (dbRecordsOnceOnly is set)\n", + name); + yyerror(NULL); + duplicate = TRUE; + } + } else if(status) { + epicsPrintf("Can't create record \"%s\" of type \"%s\"\n", + name, recordType); + yyerrorAbort(NULL); + } + if(visible) dbVisibleRecord(pdbentry); +} + +static void dbRecordField(char *name,char *value) +{ + DBENTRY *pdbentry; + tempListNode *ptempListNode; + long status; + + if(duplicate) return; + ptempListNode = (tempListNode *)ellFirst(&tempList); + pdbentry = ptempListNode->item; + status = dbFindField(pdbentry,name); + if(status) { + epicsPrintf("Record \"%s\" does not have a field \"%s\"\n", + dbGetRecordName(pdbentry), name); + yyerror(NULL); + return; + } + dbTranslateEscape(value, value); /* yuck: in-place, but safe */ + status = dbPutString(pdbentry,value); + if(status) { + epicsPrintf("Can't set \"%s.%s\" to \"%s\"\n", + dbGetRecordName(pdbentry), name, value); + yyerror(NULL); + return; + } +} + +static void dbRecordInfo(char *name, char *value) +{ + DBENTRY *pdbentry; + tempListNode *ptempListNode; + long status; + + if(duplicate) return; + ptempListNode = (tempListNode *)ellFirst(&tempList); + pdbentry = ptempListNode->item; + status = dbPutInfo(pdbentry,name,value); + if(status) { + epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n", + dbGetRecordName(pdbentry), name, value); + yyerror(NULL); + return; + } +} + +static void dbRecordAlias(char *name) +{ + DBENTRY *pdbentry; + tempListNode *ptempListNode; + long status; + + if(duplicate) return; + ptempListNode = (tempListNode *)ellFirst(&tempList); + pdbentry = ptempListNode->item; + status = dbCreateAlias(pdbentry, name); + if(status) { + epicsPrintf("Can't create alias \"%s\" for \"%s\"\n", + name, dbGetRecordName(pdbentry)); + yyerror(NULL); + return; + } +} + +static void dbAlias(char *name, char *alias) +{ + DBENTRY dbEntry; + DBENTRY *pdbEntry = &dbEntry; + + dbInitEntry(pdbbase, pdbEntry); + if (dbFindRecord(pdbEntry, name)) { + epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n", + alias, name); + yyerror(NULL); + } else if (dbCreateAlias(pdbEntry, alias)) { + epicsPrintf("Can't create alias \"%s\" referring to \"%s\"\n", + alias, name); + yyerror(NULL); + } + dbFinishEntry(pdbEntry); +} + +static void dbRecordBody(void) +{ + DBENTRY *pdbentry; + + if(duplicate) { + duplicate = FALSE; + return; + } + pdbentry = (DBENTRY *)popFirstTemp(); + if(ellCount(&tempList)) + yyerrorAbort("dbRecordBody: tempList not empty"); + dbFreeEntry(pdbentry); +} diff --git a/src/dbStatic/dbPvdLib.c b/src/dbStatic/dbPvdLib.c new file mode 100644 index 000000000..43f9cd40f --- /dev/null +++ b/src/dbStatic/dbPvdLib.c @@ -0,0 +1,227 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* dbPvdLib.c */ + +#include +#include +#include +#include + +#include "dbDefs.h" +#include "ellLib.h" +#include "dbBase.h" +#include "epicsStdio.h" +#include "epicsString.h" +#include "epicsMutex.h" +#define epicsExportSharedSymbols +#include "dbStaticLib.h" +#include "dbStaticPvt.h" + +typedef struct { + ELLLIST list; + epicsMutexId lock; +} dbPvdBucket; + +typedef struct dbPvd { + unsigned int size; + unsigned int mask; + dbPvdBucket **buckets; +} dbPvd; + +unsigned int dbPvdHashTableSize = 0; + +#define MIN_SIZE 256 +#define DEFAULT_SIZE 512 +#define MAX_SIZE 65536 + + +int dbPvdTableSize(int size) +{ + if (size & (size - 1)) { + printf("dbPvdTableSize: %d is not a power of 2\n", size); + return -1; + } + + if (size < MIN_SIZE) + size = MIN_SIZE; + + if (size > MAX_SIZE) + size = MAX_SIZE; + + dbPvdHashTableSize = size; + return 0; +} + +void dbPvdInitPvt(dbBase *pdbbase) +{ + dbPvd *ppvd; + + if (pdbbase->ppvd) return; + + if (dbPvdHashTableSize == 0) { + dbPvdHashTableSize = DEFAULT_SIZE; + } + + ppvd = (dbPvd *)dbMalloc(sizeof(dbPvd)); + ppvd->size = dbPvdHashTableSize; + ppvd->mask = dbPvdHashTableSize - 1; + ppvd->buckets = dbCalloc(ppvd->size, sizeof(dbPvdBucket *)); + + pdbbase->ppvd = ppvd; + return; +} + +PVDENTRY *dbPvdFind(dbBase *pdbbase, const char *name, int lenName) +{ + dbPvd *ppvd = pdbbase->ppvd; + dbPvdBucket *pbucket; + PVDENTRY *ppvdNode; + + pbucket = ppvd->buckets[epicsMemHash(name, lenName, 0) & ppvd->mask]; + if (pbucket == NULL) return NULL; + + epicsMutexMustLock(pbucket->lock); + ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); + while (ppvdNode) { + const char *recordname = ppvdNode->precnode->recordname; + + if (strncmp(name, recordname, lenName) == 0 && + strlen(recordname) == lenName) + break; + ppvdNode = (PVDENTRY *) ellNext((ELLNODE *)ppvdNode); + } + epicsMutexUnlock(pbucket->lock); + return ppvdNode; +} + +PVDENTRY *dbPvdAdd(dbBase *pdbbase, dbRecordType *precordType, + dbRecordNode *precnode) +{ + dbPvd *ppvd = pdbbase->ppvd; + dbPvdBucket *pbucket; + PVDENTRY *ppvdNode; + char *name = precnode->recordname; + unsigned int h; + + h = epicsStrHash(name, 0) & ppvd->mask; + pbucket = ppvd->buckets[h]; + if (pbucket == NULL) { + pbucket = dbCalloc(1, sizeof(dbPvdBucket)); + ellInit(&pbucket->list); + pbucket->lock = epicsMutexCreate(); + ppvd->buckets[h] = pbucket; + } + + epicsMutexMustLock(pbucket->lock); + ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); + while (ppvdNode) { + if (strcmp(name, ppvdNode->precnode->recordname) == 0) { + epicsMutexUnlock(pbucket->lock); + return NULL; + } + ppvdNode = (PVDENTRY *) ellNext((ELLNODE *)ppvdNode); + } + ppvdNode = dbCalloc(1, sizeof(PVDENTRY)); + ppvdNode->precordType = precordType; + ppvdNode->precnode = precnode; + ellAdd(&pbucket->list, (ELLNODE *)ppvdNode); + epicsMutexUnlock(pbucket->lock); + return ppvdNode; +} + +void dbPvdDelete(dbBase *pdbbase, dbRecordNode *precnode) +{ + dbPvd *ppvd = pdbbase->ppvd; + dbPvdBucket *pbucket; + PVDENTRY *ppvdNode; + char *name = precnode->recordname; + + pbucket = ppvd->buckets[epicsStrHash(name, 0) & ppvd->mask]; + if (pbucket == NULL) return; + + epicsMutexMustLock(pbucket->lock); + ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); + while (ppvdNode) { + if (ppvdNode->precnode && + ppvdNode->precnode->recordname && + strcmp(name, ppvdNode->precnode->recordname) == 0) { + ellDelete(&pbucket->list, (ELLNODE *)ppvdNode); + free(ppvdNode); + break; + } + ppvdNode = (PVDENTRY *) ellNext((ELLNODE *)ppvdNode); + } + epicsMutexUnlock(pbucket->lock); + return; +} + +void dbPvdFreeMem(dbBase *pdbbase) +{ + dbPvd *ppvd = pdbbase->ppvd; + unsigned int h; + + if (ppvd == NULL) return; + pdbbase->ppvd = NULL; + + for (h = 0; h < ppvd->size; h++) { + dbPvdBucket *pbucket = ppvd->buckets[h]; + PVDENTRY *ppvdNode; + + if (pbucket == NULL) continue; + epicsMutexMustLock(pbucket->lock); + ppvd->buckets[h] = NULL; + while ((ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list))) { + ellDelete(&pbucket->list, (ELLNODE *)ppvdNode); + free(ppvdNode); + } + epicsMutexDestroy(pbucket->lock); + free(pbucket); + } + free(ppvd->buckets); + free(ppvd); +} + +void epicsShareAPI dbPvdDump(dbBase *pdbbase, int verbose) +{ + unsigned int empty = 0; + dbPvd *ppvd; + unsigned int h; + + if (!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + ppvd = pdbbase->ppvd; + if (ppvd == NULL) return; + + printf("Process Variable Directory has %u buckets", ppvd->size); + + for (h = 0; h < ppvd->size; h++) { + dbPvdBucket *pbucket = ppvd->buckets[h]; + PVDENTRY *ppvdNode; + int i = 1; + + if (pbucket == NULL) { + empty++; + continue; + } + epicsMutexMustLock(pbucket->lock); + ppvdNode = (PVDENTRY *) ellFirst(&pbucket->list); + printf("\n [%4u] %4d ", h, ellCount(&pbucket->list)); + while (ppvdNode && verbose) { + if (!(++i % 4)) + printf("\n "); + printf(" %s", ppvdNode->precnode->recordname); + ppvdNode = (PVDENTRY *) ellNext((ELLNODE*)ppvdNode); + } + epicsMutexUnlock(pbucket->lock); + } + printf("\n%u buckets empty.\n", empty); +} diff --git a/src/dbStatic/dbReadTest.c b/src/dbStatic/dbReadTest.c new file mode 100644 index 000000000..c7a433232 --- /dev/null +++ b/src/dbStatic/dbReadTest.c @@ -0,0 +1,90 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbReadTest.c */ +/* Author: Marty Kraimer Date: 13JUL95 */ + +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errMdef.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "dbBase.h" +#include "gpHash.h" +#include "osiFileName.h" + +DBBASE *pdbbase = NULL; + +int main(int argc,char **argv) +{ + int i; + int strip; + char *path = NULL; + char *sub = NULL; + int pathLength = 0; + int subLength = 0; + char **pstr; + char *psep; + int *len; + long status; + static char *pathSep = OSI_PATH_LIST_SEPARATOR; + static char *subSep = ","; + + /*Look for options*/ + if(argc<2) { + printf("usage: dbReadTest -Idir -Smacsub file.dbd file.db \n"); + exit(0); + } + while((strncmp(argv[1],"-I",2)==0)||(strncmp(argv[1],"-S",2)==0)) { + if(strncmp(argv[1],"-I",2)==0) { + pstr = &path; + psep = pathSep; + len = &pathLength; + } else { + pstr = ⊂ + psep = subSep; + len = &subLength; + } + if(strlen(argv[1])==2) { + dbCatString(pstr,len,argv[2],psep); + strip = 2; + } else { + dbCatString(pstr,len,argv[1]+2,psep); + strip = 1; + } + argc -= strip; + for(i=1; ipgpHash); + dbDumpMenu(pdbbase,NULL); + dbDumpRecord(pdbbase,NULL,0); + dbReportDeviceConfig(pdbbase,stdout); +*/ + dbFreeBase(pdbbase); + return(0); +} diff --git a/src/dbStatic/dbStaticHost.rc b/src/dbStatic/dbStaticHost.rc new file mode 100755 index 000000000..a6d32066a --- /dev/null +++ b/src/dbStatic/dbStaticHost.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Static Host Database Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Static Host Database Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "dbStaticHost\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "dbStaticHost.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/dbStatic/dbStaticIoc.rc b/src/dbStatic/dbStaticIoc.rc new file mode 100755 index 000000000..865e8102d --- /dev/null +++ b/src/dbStatic/dbStaticIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Static Ioc Database Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Static Ioc Database Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "dbStaticIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "dbStaticIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/dbStatic/dbStaticIocRegister.c b/src/dbStatic/dbStaticIocRegister.c new file mode 100644 index 000000000..b77b0d29c --- /dev/null +++ b/src/dbStatic/dbStaticIocRegister.c @@ -0,0 +1,170 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "iocsh.h" + +#define epicsExportSharedSymbols +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "dbStaticIocRegister.h" + +/* common arguments */ + +static const iocshArg argPdbbase = { "pdbbase", iocshArgPdbbase}; +static const iocshArg argRecType = { "recordTypeName", iocshArgString}; + + +/* dbDumpPath */ +static const iocshArg * const dbDumpPathArgs[] = {&argPdbbase}; +static const iocshFuncDef dbDumpPathFuncDef = {"dbDumpPath",1,dbDumpPathArgs}; +static void dbDumpPathCallFunc(const iocshArgBuf *args) +{ + dbDumpPath(*iocshPpdbbase); +} + +/* dbDumpRecord */ +static const iocshArg dbDumpRecordArg2 = { "interest level",iocshArgInt}; +static const iocshArg * const dbDumpRecordArgs[] = + {&argPdbbase, &argRecType, &dbDumpRecordArg2}; +static const iocshFuncDef dbDumpRecordFuncDef = + {"dbDumpRecord",3,dbDumpRecordArgs}; +static void dbDumpRecordCallFunc(const iocshArgBuf *args) +{ + dbDumpRecord(*iocshPpdbbase,args[1].sval,args[2].ival); +} + +/* dbDumpMenu */ +static const iocshArg dbDumpMenuArg1 = { "menuName",iocshArgString}; +static const iocshArg * const dbDumpMenuArgs[] = { + &argPdbbase, &dbDumpMenuArg1}; +static const iocshFuncDef dbDumpMenuFuncDef = {"dbDumpMenu",2,dbDumpMenuArgs}; +static void dbDumpMenuCallFunc(const iocshArgBuf *args) +{ + dbDumpMenu(*iocshPpdbbase,args[1].sval); +} + +/* dbDumpRecordType */ +static const iocshArg * const dbDumpRecordTypeArgs[] = + {&argPdbbase, &argRecType}; +static const iocshFuncDef dbDumpRecordTypeFuncDef = + {"dbDumpRecordType",2,dbDumpRecordTypeArgs}; +static void dbDumpRecordTypeCallFunc(const iocshArgBuf *args) +{ + dbDumpRecordType(*iocshPpdbbase,args[1].sval); +} + +/* dbDumpField */ +static const iocshArg dbDumpFieldArg2 = { "fieldName",iocshArgString}; +static const iocshArg * const dbDumpFieldArgs[] = + {&argPdbbase, &argRecType,&dbDumpFieldArg2}; +static const iocshFuncDef dbDumpFieldFuncDef = {"dbDumpField",3,dbDumpFieldArgs}; +static void dbDumpFieldCallFunc(const iocshArgBuf *args) +{ + dbDumpField(*iocshPpdbbase,args[1].sval,args[2].sval); +} + +/* dbDumpDevice */ +static const iocshArg * const dbDumpDeviceArgs[] = { + &argPdbbase, &argRecType}; +static const iocshFuncDef dbDumpDeviceFuncDef = {"dbDumpDevice",2,dbDumpDeviceArgs}; +static void dbDumpDeviceCallFunc(const iocshArgBuf *args) +{ + dbDumpDevice(*iocshPpdbbase,args[1].sval); +} + +/* dbDumpDriver */ +static const iocshArg * const dbDumpDriverArgs[] = { &argPdbbase}; +static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs}; +static void dbDumpDriverCallFunc(const iocshArgBuf *args) +{ + dbDumpDriver(*iocshPpdbbase); +} + +/* dbDumpRegistrar */ +static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase}; +static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs}; +static void dbDumpRegistrarCallFunc(const iocshArgBuf *args) +{ + dbDumpRegistrar(*iocshPpdbbase); +} + +/* dbDumpFunction */ +static const iocshArg * const dbDumpFunctionArgs[] = { &argPdbbase}; +static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs}; +static void dbDumpFunctionCallFunc(const iocshArgBuf *args) +{ + dbDumpFunction(*iocshPpdbbase); +} + +/* dbDumpVariable */ +static const iocshArg * const dbDumpVariableArgs[] = { &argPdbbase}; +static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs}; +static void dbDumpVariableCallFunc(const iocshArgBuf *args) +{ + dbDumpVariable(*iocshPpdbbase); +} + +/* dbDumpBreaktable */ +static const iocshArg dbDumpBreaktableArg1 = { "tableName",iocshArgString}; +static const iocshArg * const dbDumpBreaktableArgs[] = + {&argPdbbase,&dbDumpBreaktableArg1}; +static const iocshFuncDef dbDumpBreaktableFuncDef = + {"dbDumpBreaktable",2,dbDumpBreaktableArgs}; +static void dbDumpBreaktableCallFunc(const iocshArgBuf *args) +{ + dbDumpBreaktable(*iocshPpdbbase,args[1].sval); +} + +/* dbPvdDump */ +static const iocshArg dbPvdDumpArg1 = { "verbose",iocshArgInt}; +static const iocshArg * const dbPvdDumpArgs[] = { + &argPdbbase,&dbPvdDumpArg1}; +static const iocshFuncDef dbPvdDumpFuncDef = {"dbPvdDump",2,dbPvdDumpArgs}; +static void dbPvdDumpCallFunc(const iocshArgBuf *args) +{ + dbPvdDump(*iocshPpdbbase,args[1].ival); +} + +/* dbPvdTableSize */ +static const iocshArg dbPvdTableSizeArg0 = { "size",iocshArgInt}; +static const iocshArg * const dbPvdTableSizeArgs[1] = + {&dbPvdTableSizeArg0}; +static const iocshFuncDef dbPvdTableSizeFuncDef = + {"dbPvdTableSize",1,dbPvdTableSizeArgs}; +static void dbPvdTableSizeCallFunc(const iocshArgBuf *args) +{ + dbPvdTableSize(args[0].ival); +} + +/* dbReportDeviceConfig */ +static const iocshArg * const dbReportDeviceConfigArgs[] = {&argPdbbase}; +static const iocshFuncDef dbReportDeviceConfigFuncDef = { + "dbReportDeviceConfig",1,dbReportDeviceConfigArgs}; +static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args) +{ + dbReportDeviceConfig(*iocshPpdbbase,stdout); +} + +void epicsShareAPI dbStaticIocRegister(void) +{ + iocshRegister(&dbDumpPathFuncDef, dbDumpPathCallFunc); + iocshRegister(&dbDumpRecordFuncDef, dbDumpRecordCallFunc); + iocshRegister(&dbDumpMenuFuncDef, dbDumpMenuCallFunc); + iocshRegister(&dbDumpRecordTypeFuncDef, dbDumpRecordTypeCallFunc); + iocshRegister(&dbDumpFieldFuncDef, dbDumpFieldCallFunc); + iocshRegister(&dbDumpDeviceFuncDef, dbDumpDeviceCallFunc); + iocshRegister(&dbDumpDriverFuncDef, dbDumpDriverCallFunc); + iocshRegister(&dbDumpRegistrarFuncDef,dbDumpRegistrarCallFunc); + iocshRegister(&dbDumpFunctionFuncDef, dbDumpFunctionCallFunc); + iocshRegister(&dbDumpVariableFuncDef, dbDumpVariableCallFunc); + iocshRegister(&dbDumpBreaktableFuncDef, dbDumpBreaktableCallFunc); + iocshRegister(&dbPvdDumpFuncDef, dbPvdDumpCallFunc); + iocshRegister(&dbPvdTableSizeFuncDef,dbPvdTableSizeCallFunc); + iocshRegister(&dbReportDeviceConfigFuncDef, dbReportDeviceConfigCallFunc); +} diff --git a/src/dbStatic/dbStaticIocRegister.h b/src/dbStatic/dbStaticIocRegister.h new file mode 100644 index 000000000..762f2b123 --- /dev/null +++ b/src/dbStatic/dbStaticIocRegister.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_dbStaticIocRegister_H +#define INC_dbStaticIocRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI dbStaticIocRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_dbStaticIocRegister_H */ diff --git a/src/dbStatic/dbStaticLib.c b/src/dbStatic/dbStaticLib.c new file mode 100644 index 000000000..3c53d1f63 --- /dev/null +++ b/src/dbStatic/dbStaticLib.c @@ -0,0 +1,4120 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ + +#include +#include +#include +#include +#include +#include + +#include "cantProceed.h" +#define DBFLDTYPES_GBLSOURCE +#define GUIGROUPS_GBLSOURCE +#define SPECIAL_GBLSOURCE +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errlog.h" +#include "ellLib.h" +#include "cvtFast.h" +#include "gpHash.h" +#include "dbmf.h" +#include "postfix.h" +#include "osiFileName.h" +#include "epicsStdlib.h" +#include "epicsString.h" +#include "epicsStdioRedirect.h" + +#define epicsExportSharedSymbols +#include "link.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "drvSup.h" +#include "special.h" +#include "guigroup.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" + +int dbStaticDebug = 0; +static char *pNullString = ""; +#define messagesize 100 +#define RPCL_LEN 184 + +static char *ppstring[5]={"NPP","PP","CA","CP","CPP"}; +static char *msstring[4]={"NMS","MS","MSI","MSS"}; + +epicsShareDef maplinkType pamaplinkType[LINK_NTYPES] = { + {"CONSTANT",CONSTANT}, + {"PV_LINK",PV_LINK}, + {"VME_IO",VME_IO}, + {"CAMAC_IO",CAMAC_IO}, + {"AB_IO",AB_IO}, + {"GPIB_IO",GPIB_IO}, + {"BITBUS_IO",BITBUS_IO}, + {"MACRO_LINK",MACRO_LINK}, + {"DB_LINK",DB_LINK}, + {"CA_LINK",CA_LINK}, + {"INST_IO",INST_IO}, + {"BBGPIB_IO",BBGPIB_IO}, + {"RF_IO",RF_IO}, + {"VXI_IO",VXI_IO} +}; + +static int mapDBFtoDCT[DBF_NOACCESS+1] = { + DCT_STRING, + DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER,DCT_INTEGER, + DCT_REAL,DCT_REAL, + DCT_INTEGER, + DCT_MENU, + DCT_MENUFORM, + DCT_INLINK,DCT_OUTLINK,DCT_FWDLINK, + DCT_NOACCESS}; + +struct form { + DBLINK *plink; + int linkType; + int nlines; + char **prompt; + char **value; + char **verify; +}; + +static char *promptCONSTANT[] = { + "Constant:"}; +static char *promptINLINK[] = { + " PV Name:", + "NPP PP CA CP CPP:", + " NMS MS MSI MSS:"}; +static char *promptOUTLINK[] = { + " PV Name:", + "NPP PP CA:", + "NMS or MS:"}; +static char *promptFWDLINK[] = { + " PV Name:", + " CA:"}; +static char *promptVME_IO[] = { + " card:", + "signal:", + " parm:"}; +static char *promptCAMAC_IO[] = { + " branch:", + " crate:", + " station:", + "subaddress:", + " function:", + " parameter:"}; +static char *promptAB_IO[] = { + " link:", + " adapter:", + " card:", + " signal:", + " parm:"}; +static char *promptGPIB_IO[] = { + "link:", + "addr:", + "parm:"}; +static char *promptBITBUS_IO[] = { + " link:", + " node:", + " port:", + "signal:", + " parm:"}; +static char *promptINST_IO[] = { + "parm:"}; +static char *promptBBGPIB_IO[] = { + " link:", + " bbaddr:", + "gpibaddr:", + " parm:"}; +static char *promptRF_IO[] = { + " cryo:", + " micro:", + " dataset:", + " element:"}; +static char *promptVXI_IO[] = { + " Dynamic?", + "DYN frame:", + "DYN slot:", + "STATIC la:", + " Signal:", + " parm:"}; + +/*Each DBF link type is separate case*/ +#define FORM_CONSTANT 0 +#define FORM_INLINK 1 +#define FORM_OUTLINK 2 +#define FORM_FWDLINK 3 +#define FORM_VME_IO 4 +#define FORM_CAMAC_IO 5 +#define FORM_AB_IO 6 +#define FORM_GPIB_IO 7 +#define FORM_BITBUS_IO 8 +#define FORM_INST_IO 9 +#define FORM_BBGPIB_IO 10 +#define FORM_RF_IO 11 +#define FORM_VXI_IO 12 +#define FORM_NTYPES (FORM_VXI_IO + 1) + +static char **promptAddr[FORM_NTYPES] = { +promptCONSTANT, +promptINLINK, +promptOUTLINK, +promptFWDLINK, +promptVME_IO, +promptCAMAC_IO, +promptAB_IO, +promptGPIB_IO, +promptBITBUS_IO, +promptINST_IO, +promptBBGPIB_IO, +promptRF_IO, +promptVXI_IO}; + +static int formlines[FORM_NTYPES] = { +sizeof(promptCONSTANT)/sizeof(char *), +sizeof(promptINLINK)/sizeof(char *), +sizeof(promptOUTLINK)/sizeof(char *), +sizeof(promptFWDLINK)/sizeof(char *), +sizeof(promptVME_IO)/sizeof(char *), +sizeof(promptCAMAC_IO)/sizeof(char *), +sizeof(promptAB_IO)/sizeof(char *), +sizeof(promptGPIB_IO)/sizeof(char *), +sizeof(promptBITBUS_IO)/sizeof(char *), +sizeof(promptINST_IO)/sizeof(char *), +sizeof(promptBBGPIB_IO)/sizeof(char *), +sizeof(promptRF_IO)/sizeof(char *), +sizeof(promptVXI_IO)/sizeof(char *)}; + +/*forward references for private routines*/ +static FILE *openOutstream(const char *filename); +static void finishOutstream(FILE *stream); +static long setLinkType(DBENTRY *pdbentry); +static long putParmString(char **pparm,const char *pstring); +static long mapLINKTtoFORMT(DBLINK *plink,dbFldDes *pflddes,int *ind); +static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess); +static void zeroDbentry(DBENTRY *pdbentry); +static char *getpMessage(DBENTRY *pdbentry); +static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname); +static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length); + +/* internal routines*/ +static FILE *openOutstream(const char *filename) +{ + FILE *stream; + errno = 0; + stream = fopen(filename,"w"); + if(!stream) { + fprintf(stderr,"error opening %s %s\n",filename,strerror(errno)); + return 0; + } + return stream; +} + +static void finishOutstream(FILE *stream) +{ + if(stream==stdout) { + fflush(stdout); + } else { + if(fclose(stream)) fprintf(stderr,"fclose error %s\n",strerror(errno)); + } +} + +static long setLinkType(DBENTRY *pdbentry) +{ + DBENTRY dbEntry; + dbFldDes *pflddes; + dbRecordType *precordType; + devSup *pdevSup; + DBLINK *plink; + long status=0; + int link_type,ind,type; + + dbCopyEntryContents(pdbentry, &dbEntry); + status = dbFindField(&dbEntry, "DTYP"); + if (status) { + epicsPrintf("field DTYP does not exist for recordtype %s\n", + dbGetRecordTypeName(&dbEntry)); + status = S_dbLib_fieldNotFound; + goto done; + } + + precordType = dbEntry.precordType; + if (!precordType) { + status = S_dbLib_badField; + goto done; + } + + if (ellCount(&precordType->devList) == 0) goto done; + + ind = dbGetMenuIndex(&dbEntry); + if (ind == -1) { + char *pstring; + + pstring = dbGetString(&dbEntry); + if (strstr(pstring, "$(") || strstr(pstring, "${")) { + link_type = MACRO_LINK; + } else { + status = S_dbLib_badField; + goto done; + } + } else { + pdevSup = (devSup *)ellNth(&precordType->devList, ind + 1); + if (!pdevSup) { + status = S_dbLib_badField; + goto done; + } + link_type = pdevSup->link_type; + } + + pflddes = pdbentry->pflddes; + plink = (DBLINK *)pdbentry->pfield; + if (plink->type == link_type) goto done; + + if (plink->text) + { + /* re-parse link text when DTYP has changed */ + char * link_text; + link_text = plink->text; + plink->text = NULL; + dbFreeLinkContents(plink); + plink->type = link_type; + dbPutString(pdbentry, link_text); + free(link_text); + goto done; + } + + type = plink->type; + if ((type == CONSTANT || type == PV_LINK || type == DB_LINK || type == CA_LINK) && + (link_type == CONSTANT || link_type == PV_LINK)) goto done; + + dbFreeLinkContents(plink); + plink->type = link_type; + switch (plink->type) { + case VME_IO: plink->value.vmeio.parm = pNullString; break; + case CAMAC_IO: plink->value.camacio.parm = pNullString; break; + case AB_IO: plink->value.abio.parm = pNullString; break; + case GPIB_IO: plink->value.gpibio.parm = pNullString; break; + case BITBUS_IO: plink->value.bitbusio.parm = pNullString; break; + case INST_IO: plink->value.instio.string = pNullString; break; + case BBGPIB_IO: plink->value.bbgpibio.parm = pNullString; break; + case VXI_IO: plink->value.vxiio.parm = pNullString; break; + } +done: + dbFinishEntry(&dbEntry); + return(status); +} + +static long putParmString(char **pparm,const char *pstring) +{ + if (*pparm && *pparm != pNullString) { + free(*pparm); + *pparm = pNullString; + } + if (!pstring) return 0; + pstring = strchr(pstring, '@'); + if (!pstring || !*++pstring) return 0; + *pparm = epicsStrDup(pstring); + return 0; +} + +void dbFreeLinkContents(struct link *plink) +{ + char *parm = NULL; + + switch(plink->type) { + case CONSTANT: free((void *)plink->value.constantStr); break; + case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break; + case PV_LINK: free((void *)plink->value.pv_link.pvname); break; + case VME_IO: parm = plink->value.vmeio.parm; break; + case CAMAC_IO: parm = plink->value.camacio.parm; break; + case AB_IO: parm = plink->value.abio.parm; break; + case GPIB_IO: parm = plink->value.gpibio.parm; break; + case BITBUS_IO: parm = plink->value.bitbusio.parm;break; + case INST_IO: parm = plink->value.instio.string; break; + case BBGPIB_IO: parm = plink->value.bbgpibio.parm;break; + case VXI_IO: parm = plink->value.vxiio.parm; break; + default: + epicsPrintf("dbFreeLink called but link type unknown\n"); + } + if(parm && (parm != pNullString)) free((void *)parm); + if(plink->text) free(plink->text); + memset((char *)plink,0,sizeof(struct link)); +} + +void dbFreePath(DBBASE *pdbbase) +{ + ELLLIST *ppathList; + dbPathNode *pdbPathNode; + + if(!pdbbase) return; + ppathList = (ELLLIST *)pdbbase->pathPvt; + if(!ppathList) return; + while((pdbPathNode = (dbPathNode *)ellFirst(ppathList))) { + ellDelete(ppathList,&pdbPathNode->node); + free((void *)pdbPathNode->directory); + free((void *)pdbPathNode); + } + free((void *)ppathList); + pdbbase->pathPvt = 0; + return; +} + + +static long mapLINKTtoFORMT(DBLINK *plink,dbFldDes *pflddes,int *ind) +{ + switch(plink->type) { + case CONSTANT: + *ind = FORM_CONSTANT; return(0); + case PV_LINK: + switch(pflddes->field_type) { + case DBF_INLINK: + *ind = FORM_INLINK; return(0); + case DBF_OUTLINK: + *ind = FORM_OUTLINK; return(0); + case DBF_FWDLINK: + *ind = FORM_FWDLINK; return(0); + default: + break; + } + break; + case VME_IO: + *ind = FORM_VME_IO; return(0); + case CAMAC_IO: + *ind = FORM_CAMAC_IO; return(0); + case AB_IO: + *ind = FORM_AB_IO; return(0); + case GPIB_IO: + *ind = FORM_GPIB_IO; return(0); + case BITBUS_IO: + *ind = FORM_BITBUS_IO; return(0); + case INST_IO: + *ind = FORM_INST_IO; return(0); + case BBGPIB_IO: + *ind = FORM_BBGPIB_IO; return(0); + case RF_IO: + *ind = FORM_RF_IO; return(0); + case VXI_IO: + *ind = FORM_VXI_IO; return(0); + default: + break; + } + return(S_dbLib_badLink); +} + +static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess) +{ + char message[200]; + char *pmessage=&message[0]; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes = pdbentry->pflddes; + char *pname = NULL; + + *pmessage=0; + if(pdbentry->precordType) pname = pdbentry->precordType->name; + if(pname) { + strcat(pmessage,"RecordType:"); + strcat(pmessage,pname); + } + if(precnode){ + if (dbIsAlias(pdbentry)) + strcat(pmessage," Record Alias:"); + else + strcat(pmessage," Record:"); + strcat(pmessage,(char *)precnode->precord); + } + if(pflddes) { + char *pstr=pflddes->name; + + strcat(pmessage," Field:"); + strcat(pmessage,pstr); + } + strcat(pmessage,"\n"); + strcat(pmessage,mess); + errMessage(status,pmessage); +} + +static void zeroDbentry(DBENTRY *pdbentry) +{ + /*NOTE that pdbbase, message, and formpvt MUST NOT be set to NULL*/ + pdbentry->precordType=NULL; + pdbentry->pflddes=NULL; + pdbentry->precnode=NULL; + pdbentry->pfield=NULL; + pdbentry->indfield=0; +} + +static char *getpMessage(DBENTRY *pdbentry) +{ + char *msg = pdbentry->message; + if (!msg) { + msg = dbCalloc(1, messagesize); + pdbentry->message = msg; + } + *msg = 0; + return msg; +} + +static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname) +{ + dbFldDes *pflddes; + DBLINK *plink; + char *pname; + + dbGetFieldAddress(pdbentry); + pflddes = pdbentry->pflddes; + if(!pflddes) return(-1); + plink = (DBLINK *)pdbentry->pfield; + if(!plink) return(-1); + switch (pflddes->field_type) { + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + if(plink->type != PV_LINK) return(S_dbLib_badLink); + pname = plink->value.pv_link.pvname; + if(pname) free((void *)pname); + pname = dbCalloc(strlen(pvname)+1,sizeof(char)); + plink->value.pv_link.pvname = pname; + strcpy(pname,pvname); + plink->value.pv_link.pvlMask = pvlMask; + return(0); + default: + errPrintf(-1,__FILE__, __LINE__,"Logic Error\n"); + } + return(S_dbLib_badLink); +} + +/*Public only for dbStaticNoRun*/ +dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) +{ + dbRecordType *precordType = pdbentry->precordType; + dbFldDes *pflddes = pdbentry->pflddes; + dbDeviceMenu *pdbDeviceMenu; + devSup *pdevSup; + int ind; + int nChoice; + + if(!precordType) return(NULL); + if(!pflddes) return(NULL); + if(pflddes->field_type!=DBF_DEVICE) return(NULL); + if(pflddes->ftPvt){ + pdbDeviceMenu = (dbDeviceMenu *)pflddes->ftPvt; + if(pdbDeviceMenu->nChoice == ellCount(&precordType->devList)) + return(pdbDeviceMenu); + free((void *)pdbDeviceMenu->papChoice); + free((void *)pdbDeviceMenu); + pflddes->ftPvt = NULL; + } + nChoice = ellCount(&precordType->devList); + if(nChoice <= 0) return(NULL); + pdbDeviceMenu = dbCalloc(1,sizeof(dbDeviceMenu)); + pdbDeviceMenu->nChoice = nChoice; + pdbDeviceMenu->papChoice = dbCalloc(pdbDeviceMenu->nChoice,sizeof(char *)); + pdevSup = (devSup *)ellFirst(&precordType->devList); + ind = 0; + while(pdevSup) { + pdbDeviceMenu->papChoice[ind] = pdevSup->choice; + ind++; + pdevSup = (devSup *)ellNext(&pdevSup->node); + } + pflddes->ftPvt = pdbDeviceMenu; + return(pdbDeviceMenu); +} + +/* Beginning of Public Routines */ + +#define INC_SIZE 256 +void epicsShareAPI dbCatString(char **string,int *stringLength,char *src,char *separator) +{ + if((*string==NULL) + || ((strlen(*string)+strlen(src)+2) > (size_t)*stringLength)) { + char *newString; + size_t size; + + size = strlen(src); + if(*string) size += strlen(*string); + /*Make size multiple of INC_SIZE*/ + size = ((size + 2 + INC_SIZE)/INC_SIZE) * INC_SIZE; + newString = dbCalloc(size,sizeof(char)); + if(*string) { + strcpy(newString,*string); + free((void *)(*string)); + } + *string = newString; + } + if(*stringLength>0) { + strcat(*string,separator); + *stringLength += strlen(separator); + } + strcat(*string,src); + *stringLength += strlen(src); +} + +dbBase * epicsShareAPI dbAllocBase(void) +{ + dbBase *pdbbase; + + pdbbase = dbCalloc(1,sizeof(dbBase)); + ellInit(&pdbbase->menuList); + ellInit(&pdbbase->recordTypeList); + ellInit(&pdbbase->drvList); + ellInit(&pdbbase->registrarList); + ellInit(&pdbbase->functionList); + ellInit(&pdbbase->variableList); + ellInit(&pdbbase->bptList); + gphInitPvt(&pdbbase->pgpHash,256); + dbPvdInitPvt(pdbbase); + return (pdbbase); +} +void epicsShareAPI dbFreeBase(dbBase *pdbbase) +{ + dbMenu *pdbMenu; + dbMenu *pdbMenuNext; + dbRecordType *pdbRecordType; + dbRecordType *pdbRecordTypeNext; + dbFldDes * pdbFldDes; + dbRecordNode *pdbRecordNode; + dbRecordNode *pdbRecordNodeNext; + dbRecordAttribute *pAttribute; + dbRecordAttribute *pAttributeNext; + devSup *pdevSup; + devSup *pdevSupNext; + dbText *ptext; + dbText *ptextNext; + dbVariableDef *pvar; + dbVariableDef *pvarNext; + drvSup *pdrvSup; + drvSup *pdrvSupNext; + brkTable *pbrkTable; + brkTable *pbrkTableNext; + int i; + DBENTRY dbentry; + + + dbInitEntry(pdbbase,&dbentry); + pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + while(pdbRecordType) { + pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); + while(pdbRecordNode) { + pdbRecordNodeNext = (dbRecordNode *)ellNext(&pdbRecordNode->node); + if(!dbFindRecord(&dbentry,pdbRecordNode->recordname)) + dbDeleteRecord(&dbentry); + pdbRecordNode = pdbRecordNodeNext; + } + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node); + } + dbFinishEntry(&dbentry); + pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + while(pdbRecordType) { + for(i=0; ino_fields; i++) { + pdbFldDes = pdbRecordType->papFldDes[i]; + free((void *)pdbFldDes->prompt); + free((void *)pdbFldDes->name); + free((void *)pdbFldDes->extra); + free((void *)pdbFldDes->initial); + if(pdbFldDes->field_type==DBF_DEVICE && pdbFldDes->ftPvt) { + dbDeviceMenu *pdbDeviceMenu; + + pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; + free((void *)pdbDeviceMenu->papChoice); + free((void *)pdbDeviceMenu); + pdbFldDes->ftPvt=0; + } + free((void *)pdbFldDes); + } + pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); + while(pdevSup) { + pdevSupNext = (devSup *)ellNext(&pdevSup->node); + ellDelete(&pdbRecordType->devList,&pdevSup->node); + free((void *)pdevSup->name); + free((void *)pdevSup->choice); + free((void *)pdevSup); + pdevSup = pdevSupNext; + } + ptext = (dbText *)ellFirst(&pdbRecordType->cdefList); + while(ptext) { + ptextNext = (dbText *)ellNext(&ptext->node); + ellDelete(&pdbRecordType->cdefList,&ptext->node); + free((void *)ptext->text); + free((void *)ptext); + ptext = ptextNext; + } + pAttribute = + (dbRecordAttribute *)ellFirst(&pdbRecordType->attributeList); + while(pAttribute) { + pAttributeNext = (dbRecordAttribute *)ellNext(&pAttribute->node); + ellDelete(&pdbRecordType->attributeList,&pAttribute->node); + free((void *)pAttribute->name); + free((void *)pAttribute->pdbFldDes); + pAttribute = pAttributeNext; + } + pdbRecordTypeNext = (dbRecordType *)ellNext(&pdbRecordType->node); + gphDelete(pdbbase->pgpHash,pdbRecordType->name,&pdbbase->recordTypeList); + ellDelete(&pdbbase->recordTypeList,&pdbRecordType->node); + free((void *)pdbRecordType->name); + free((void *)pdbRecordType->link_ind); + free((void *)pdbRecordType->papsortFldName); + free((void *)pdbRecordType->sortFldInd); + free((void *)pdbRecordType->papFldDes); + free((void *)pdbRecordType); + pdbRecordType = pdbRecordTypeNext; + } + pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList); + while(pdbMenu) { + pdbMenuNext = (dbMenu *)ellNext(&pdbMenu->node); + gphDelete(pdbbase->pgpHash,pdbMenu->name,&pdbbase->menuList); + ellDelete(&pdbbase->menuList,&pdbMenu->node); + for(i=0; i< pdbMenu->nChoice; i++) { + free((void *)pdbMenu->papChoiceName[i]); + free((void *)pdbMenu->papChoiceValue[i]); + } + free((void *)pdbMenu->papChoiceName); + free((void *)pdbMenu->papChoiceValue); + free((void *)pdbMenu ->name); + free((void *)pdbMenu); + pdbMenu = pdbMenuNext; + } + pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); + while(pdrvSup) { + pdrvSupNext = (drvSup *)ellNext(&pdrvSup->node); + ellDelete(&pdbbase->drvList,&pdrvSup->node); + free((void *)pdrvSup->name); + free((void *)pdrvSup); + pdrvSup = pdrvSupNext; + } + ptext = (dbText *)ellFirst(&pdbbase->registrarList); + while(ptext) { + ptextNext = (dbText *)ellNext(&ptext->node); + ellDelete(&pdbbase->registrarList,&ptext->node); + free((void *)ptext->text); + free((void *)ptext); + ptext = ptextNext; + } + ptext = (dbText *)ellFirst(&pdbbase->functionList); + while(ptext) { + ptextNext = (dbText *)ellNext(&ptext->node); + ellDelete(&pdbbase->functionList,&ptext->node); + free((void *)ptext->text); + free((void *)ptext); + ptext = ptextNext; + } + pvar = (dbVariableDef *)ellFirst(&pdbbase->variableList); + while(pvar) { + pvarNext = (dbVariableDef *)ellNext(&pvar->node); + ellDelete(&pdbbase->variableList,&pvar->node); + free((void *)pvar->name); + free((void *)pvar->type); + free((void *)pvar); + pvar = pvarNext; + } + pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList); + while(pbrkTable) { + pbrkTableNext = (brkTable *)ellNext(&pbrkTable->node); + gphDelete(pdbbase->pgpHash,pbrkTable->name,&pdbbase->bptList); + ellDelete(&pdbbase->bptList,&pbrkTable->node); + free(pbrkTable->name); + free((void *)pbrkTable->paBrkInt); + free((void *)pbrkTable); + pbrkTable = pbrkTableNext; + } + gphFreeMem(pdbbase->pgpHash); + dbPvdFreeMem(pdbbase); + dbFreePath(pdbbase); + free((void *)pdbbase); + return; +} + +DBENTRY * epicsShareAPI dbAllocEntry(dbBase *pdbbase) +{ + DBENTRY *pdbentry; + + pdbentry = dbmfMalloc(sizeof(DBENTRY)); + memset(pdbentry,'\0',sizeof(DBENTRY)); + pdbentry->pdbbase = pdbbase; + return(pdbentry); +} + +void epicsShareAPI dbFreeEntry(DBENTRY *pdbentry) +{ + if(pdbentry->message) free((void *)pdbentry->message); + if(pdbentry->formpvt) dbFreeForm(pdbentry); + dbmfFree(pdbentry); +} + +void epicsShareAPI dbInitEntry(dbBase *pdbbase,DBENTRY *pdbentry) +{ + memset((char *)pdbentry,'\0',sizeof(DBENTRY)); + pdbentry->pdbbase = pdbbase; +} + +void epicsShareAPI dbFinishEntry(DBENTRY *pdbentry) +{ + if(pdbentry->message) { + free((void *)pdbentry->message); + pdbentry->message = NULL; + } + if(pdbentry->formpvt) dbFreeForm(pdbentry); +} + +DBENTRY * epicsShareAPI dbCopyEntry(DBENTRY *pdbentry) +{ + DBENTRY *pnew; + + pnew = dbAllocEntry(pdbentry->pdbbase); + *pnew = *pdbentry; + pnew->message = NULL; + pnew->formpvt = NULL; + return(pnew); +} + +void epicsShareAPI dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto) +{ + *pto = *pfrom; + pto->message = NULL; + pto->formpvt = NULL; +} + + +long epicsShareAPI dbPath(DBBASE *pdbbase,const char *path) +{ + if(!pdbbase) return(-1); + dbFreePath(pdbbase); + if(!path || strlen(path)==0) return(dbAddPath(pdbbase,".")); + return(dbAddPath(pdbbase,path)); +} + +long epicsShareAPI dbAddPath(DBBASE *pdbbase,const char *path) +{ + ELLLIST *ppathList; + const char *pcolon; + const char *plast; + unsigned expectingPath; + unsigned sawMissingPath; + + if(!pdbbase) return(-1); + ppathList = (ELLLIST *)pdbbase->pathPvt; + if(!ppathList) { + ppathList = dbCalloc(1,sizeof(ELLLIST)); + ellInit(ppathList); + pdbbase->pathPvt = (void *)ppathList; + } + if (!path) return(0); /* Empty path strings are ignored */ + /* care is taken to properly deal with white space + * 1) preceding and trailing white space is removed from paths + * 2) white space inbetween path separator counts as an empty name + * (see below) + */ + expectingPath = FALSE; + sawMissingPath = FALSE; + while (*path) { + size_t len; + + /* preceding white space is removed */ + if (isspace((int)*path)) { + path++; + continue; + } + pcolon = strstr (path, OSI_PATH_LIST_SEPARATOR); + if (pcolon==path) { + sawMissingPath = TRUE; + path += strlen (OSI_PATH_LIST_SEPARATOR); + continue; + } + if (pcolon) { + plast = pcolon - 1; + expectingPath = TRUE; + } else { + plast = strlen (path) + path - 1; + expectingPath = FALSE; + } + /* trailing white space is removed */ + while (isspace((int)*plast)) { + plast--; + } + + /* + * len is always nonzero because we found something that + * 1) isnt white space + * 2) isnt a path separator + */ + len = (plast - path) + 1; + if (dbAddOnePath (pdbbase, path, len)) return (-1); + path += len; + if (pcolon) { + path += strlen(OSI_PATH_LIST_SEPARATOR); + } + } + + /* + * an empty name at beginning, middle, or end of a path string that isnt + * empty means current directory + */ + if (expectingPath||sawMissingPath) { + return dbAddOnePath (pdbbase, ".", 1); + } + return(0); +} + +static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length) +{ + ELLLIST *ppathList; + dbPathNode *pdbPathNode; + + if(!pdbbase) return(-1); + ppathList = (ELLLIST *)pdbbase->pathPvt; + + pdbPathNode = (dbPathNode *)dbCalloc(1, sizeof(dbPathNode)); + pdbPathNode->directory = (char *)dbCalloc(length+1, sizeof(char)); + strncpy(pdbPathNode->directory, path, length); + pdbPathNode->directory[length] = '\0'; + ellAdd(ppathList, &pdbPathNode->node); + return 0; +} + + +long epicsShareAPI dbWriteRecord(DBBASE *ppdbbase,const char *filename, + const char *precordTypename,int level) +{ + FILE *stream; + long status; + + stream = openOutstream(filename); + if(!stream) return -1; + status = dbWriteRecordFP(ppdbbase,stream,precordTypename,level); + finishOutstream(stream); + return status; +} + +long epicsShareAPI dbWriteRecordFP( + DBBASE *pdbbase,FILE *fp,const char *precordTypename,int level) +{ + DBENTRY dbentry; + DBENTRY *pdbentry=&dbentry; + long status; + int dctonly; + + dctonly = ((level>1) ? FALSE : TRUE); + dbInitEntry(pdbbase,pdbentry); + if(!precordTypename) { + status = dbFirstRecordType(pdbentry); + if(status) { + fprintf(stderr,"dbWriteRecordFP: No record descriptions\n"); + dbFinishEntry(pdbentry); + return(status); + } + } else { + status = dbFindRecordType(pdbentry,precordTypename); + if(status) { + fprintf(stderr,"dbWriteRecordFP: No record description for %s\n", + precordTypename); + dbFinishEntry(pdbentry); + return(status); + } + } + while(!status) { + status = dbFirstRecord(pdbentry); + while(!status) { + if (dbIsAlias(pdbentry)) { + status = dbNextRecord(pdbentry); + continue; + } + if(dbIsVisibleRecord(pdbentry)) + fprintf(fp,"grecord(%s,\"%s\") {\n", + dbGetRecordTypeName(pdbentry),dbGetRecordName(pdbentry)); + else + fprintf(fp,"record(%s,\"%s\") {\n", + dbGetRecordTypeName(pdbentry),dbGetRecordName(pdbentry)); + status = dbFirstField(pdbentry,dctonly); + while(!status) { + if (!dbIsDefaultValue(pdbentry) || level>0) { + char *pvalstring = dbGetString(pdbentry); + + if (!pvalstring) { + fprintf(fp,"\tfield(%s,\"\")\n", + dbGetFieldName(pdbentry)); + } else { + fprintf(fp,"\tfield(%s,\"", + dbGetFieldName(pdbentry)); + epicsStrPrintEscaped(fp,pvalstring,strlen(pvalstring)); + fprintf(fp,"\")\n"); + } + } else if(level>0) { /*generate 0 length string*/ + fprintf(fp,"\tfield(%s,\"\")\n",dbGetFieldName(pdbentry)); + } + status=dbNextField(pdbentry,dctonly); + } + status = dbFirstInfo(pdbentry); + while(!status) { + fprintf(fp,"\tinfo(\"%s\",\"%s\")\n", + dbGetInfoName(pdbentry), dbGetInfoString(pdbentry)); + status=dbNextInfo(pdbentry); + } + fprintf(fp,"}\n"); + status = dbNextRecord(pdbentry); + } + status = dbFirstRecord(pdbentry); + while (!status) { + if (!dbIsAlias(pdbentry)) { + status = dbNextRecord(pdbentry); + continue; + } + fprintf(fp, "alias(\"%s\",\"%s\")\n", + dbRecordName(pdbentry), dbGetRecordName(pdbentry)); + status = dbNextRecord(pdbentry); + } + if(precordTypename) break; + status = dbNextRecordType(pdbentry); + } + dbFinishEntry(pdbentry); + return(0); +} + +long epicsShareAPI dbWriteMenu( + DBBASE *ppdbbase,const char *filename,const char *menuName) +{ + FILE *stream; + long status; + + stream = openOutstream(filename); + status = dbWriteMenuFP(ppdbbase,stream,menuName); + finishOutstream(stream); + return status; +} + +long epicsShareAPI dbWriteMenuFP(DBBASE *pdbbase,FILE *fp,const char *menuName) +{ + dbMenu *pdbMenu; + int gotMatch; + int i; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return(-1); + } + pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList); + while(pdbMenu) { + if(menuName) { + gotMatch = (strcmp(menuName,pdbMenu->name)==0) ? TRUE : FALSE; + }else { + gotMatch=TRUE; + } + if(gotMatch) { + fprintf(fp,"menu(%s) {\n",pdbMenu->name); + for(i=0; inChoice; i++) { + fprintf(fp,"\tchoice(%s,\"%s\")\n",pdbMenu->papChoiceName[i], + pdbMenu->papChoiceValue[i]); + } + fprintf(fp,"}\n"); + if(menuName) break; + } + pdbMenu = (dbMenu *)ellNext(&pdbMenu->node); + } + return(0); +} + +long epicsShareAPI dbWriteRecordType( + DBBASE *pdbbase,const char *filename,const char *recordTypeName) +{ + FILE *stream; + long status; + + stream = openOutstream(filename); + status = dbWriteRecordTypeFP(pdbbase,stream,recordTypeName); + finishOutstream(stream); + return status; +} + +long epicsShareAPI dbWriteRecordTypeFP( + DBBASE *pdbbase,FILE *fp,const char *recordTypeName) +{ + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + int gotMatch; + int i; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return(-1); + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + if(recordTypeName) { + gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) + ? TRUE : FALSE; + }else { + gotMatch=TRUE; + } + if(!gotMatch) continue; + fprintf(fp,"recordtype(%s) {\n",pdbRecordType->name); + for(i=0; ino_fields; i++) { + int j; + + pdbFldDes = pdbRecordType->papFldDes[i]; + fprintf(fp,"\tfield(%s,",pdbFldDes->name); + for(j=0; jfield_type) break; + } + if(j>=DBF_NTYPES) + fprintf(stderr,"\t field_type: %d\n", + pdbFldDes->field_type); + else + fprintf(fp,"%s) {\n",pamapdbfType[j].strvalue); + if(pdbFldDes->prompt) + fprintf(fp,"\t\tprompt(\"%s\")\n",pdbFldDes->prompt); + if(pdbFldDes->initial) + fprintf(fp,"\t\tinitial(\"%s\")\n",pdbFldDes->initial); + if(pdbFldDes->promptgroup) { + for(j=0; jpromptgroup) { + fprintf(fp,"\t\tpromptgroup(%s)\n", + pamapguiGroup[j].strvalue); + break; + } + } + } + if(pdbFldDes->special) { + if(pdbFldDes->special >= SPC_NTYPES) { + fprintf(fp,"\t\tspecial(%d)\n",pdbFldDes->special); + } else for(j=0; jspecial) { + fprintf(fp,"\t\tspecial(%s)\n", + pamapspcType[j].strvalue); + break; + } + } + } + if(pdbFldDes->extra) + fprintf(fp,"\t\textra(\"%s\")\n",pdbFldDes->extra); + if(pdbFldDes->field_type==DBF_MENU) { + if(pdbFldDes->ftPvt) + fprintf(fp,"\t\tmenu(%s)\n", + ((dbMenu *)pdbFldDes->ftPvt)->name); + else + fprintf(stderr,"\t\t menu: NOT FOUND\n"); + } + if(pdbFldDes->field_type==DBF_STRING) { + fprintf(fp,"\t\tsize(%d)\n", + pdbFldDes->size); + } + if(pdbFldDes->process_passive) fprintf(fp,"\t\tpp(TRUE)\n"); + if(pdbFldDes->base) fprintf(fp,"\t\tbase(HEX)\n"); + if(pdbFldDes->interest) + fprintf(fp,"\t\tinterest(%d)\n",pdbFldDes->interest); + if(!pdbFldDes->as_level) fprintf(fp,"\t\tasl(ASL0)\n"); + fprintf(fp,"\t}\n"); + } + fprintf(fp,"}\n"); + if(recordTypeName) break; + } + return(0); +} + +long epicsShareAPI dbWriteDevice(DBBASE *pdbbase,const char *filename) +{ + FILE *stream; + long status; + + stream = openOutstream(filename); + status = dbWriteDeviceFP(pdbbase,stream); + finishOutstream(stream); + return status; +} + +long epicsShareAPI dbWriteDeviceFP(DBBASE *pdbbase,FILE *fp) +{ + dbRecordType *pdbRecordType; + devSup *pdevSup; + + if(!pdbbase) { + fprintf(stderr,"dbWriteDeviceFP: pdbbase not specified\n"); + return(-1); + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + for(pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); + pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) { + int j; + + for(j=0; j< LINK_NTYPES; j++) { + if(pamaplinkType[j].value==pdevSup->link_type) break; + } + if(j>=LINK_NTYPES) { + fprintf(fp,"link_type not valid\n"); + continue; + } + fprintf(fp,"device(%s,%s,%s,\"%s\")\n", + pdbRecordType->name, + pamaplinkType[j].strvalue, + pdevSup->name,pdevSup->choice); + } + } + return(0); +} + +long epicsShareAPI dbWriteDriver(DBBASE *pdbbase,const char *filename) +{ + FILE *stream; + long status; + + stream = openOutstream(filename); + status = dbWriteDriverFP(pdbbase,stream); + finishOutstream(stream); + return status; +} + +long epicsShareAPI dbWriteDriverFP(DBBASE *pdbbase,FILE *fp) +{ + drvSup *pdrvSup; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return(-1); + } + for(pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); + pdrvSup; pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { + fprintf(fp,"driver(%s)\n",pdrvSup->name); + } + return(0); +} + +long epicsShareAPI dbWriteRegistrarFP(DBBASE *pdbbase,FILE *fp) +{ + dbText *ptext; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return(-1); + } + for(ptext = (dbText *)ellFirst(&pdbbase->registrarList); + ptext; ptext = (dbText *)ellNext(&ptext->node)) { + fprintf(fp,"registrar(%s)\n",ptext->text); + } + return(0); +} + +long epicsShareAPI dbWriteFunctionFP(DBBASE *pdbbase,FILE *fp) +{ + dbText *ptext; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return(-1); + } + for(ptext = (dbText *)ellFirst(&pdbbase->functionList); + ptext; ptext = (dbText *)ellNext(&ptext->node)) { + fprintf(fp,"function(%s)\n",ptext->text); + } + return(0); +} + +long epicsShareAPI dbWriteVariableFP(DBBASE *pdbbase,FILE *fp) +{ + dbVariableDef *pvar; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return(-1); + } + for(pvar = (dbVariableDef *)ellFirst(&pdbbase->variableList); + pvar; pvar = (dbVariableDef *)ellNext(&pvar->node)) { + fprintf(fp,"variable(%s,%s)\n",pvar->name,pvar->type); + } + return(0); +} + +long epicsShareAPI dbWriteBreaktable(DBBASE *pdbbase,const char *filename) +{ + FILE *stream; + long status; + + stream = openOutstream(filename); + status = dbWriteBreaktableFP(pdbbase,stream); + finishOutstream(stream); + return status; +} + +long epicsShareAPI dbWriteBreaktableFP(DBBASE *pdbbase,FILE *fp) +{ + brkTable *pbrkTable; + brkInt *pbrkInt; + int i; + + if (!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return(-1); + } + for (pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList); + pbrkTable; + pbrkTable = (brkTable *)ellNext(&pbrkTable->node)) { + fprintf(fp,"breaktable(%s) {\n",pbrkTable->name); + pbrkInt = pbrkTable->paBrkInt; + for(i=0; i < pbrkTable->number; i++) { + fprintf(fp,"\t%e, %e\n",pbrkInt->raw,pbrkInt->eng); + pbrkInt++; + } + fprintf(fp,"}\n"); + } + return(0); +} + +long epicsShareAPI dbFindRecordType(DBENTRY *pdbentry,const char *recordType) +{ + dbBase *pdbbase = pdbentry->pdbbase; + GPHENTRY *phash; + + zeroDbentry(pdbentry); + phash = gphFind(pdbbase->pgpHash,recordType,&pdbbase->recordTypeList); + if(!phash) return(S_dbLib_recordTypeNotFound); + pdbentry->precordType = phash->userPvt; + return(0); +} + +long epicsShareAPI dbFirstRecordType(DBENTRY *pdbentry) +{ + dbRecordType *precordType; + + zeroDbentry(pdbentry); + precordType = (dbRecordType *)ellFirst(&pdbentry->pdbbase->recordTypeList); + if(!precordType) return(S_dbLib_recordTypeNotFound); + pdbentry->precordType = precordType; + return(0); +} + +long epicsShareAPI dbNextRecordType(DBENTRY *pdbentry) +{ + dbRecordType *precordType = pdbentry->precordType; + + zeroDbentry(pdbentry); + precordType = (dbRecordType *)ellNext(&precordType->node); + if(!precordType) return(S_dbLib_recordTypeNotFound); + pdbentry->precordType = precordType; + return(0); +} + +char * epicsShareAPI dbGetRecordTypeName(DBENTRY *pdbentry) +{ + return(pdbentry->precordType->name); +} + +int epicsShareAPI dbGetNRecordTypes(DBENTRY *pdbentry) +{ + return(ellCount(&pdbentry->pdbbase->recordTypeList)); +} + +long epicsShareAPI dbPutRecordAttribute( + DBENTRY *pdbentry, const char *name, const char*value) +{ + dbRecordType *precordType = pdbentry->precordType; + int createNew = TRUE; + int compare; + dbRecordAttribute *pattribute; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList); + /*put new attribute name in sort order*/ + while(pattribute) { + compare = strcmp(pattribute->name,name); + if(compare==0) { + createNew = FALSE; + } + if(compare>=0) break; + pattribute = (dbRecordAttribute *)ellNext(&pattribute->node); + } + if(createNew) { + dbRecordAttribute *pnew; + dbFldDes *pdbFldDes; + + pnew = dbCalloc(1,sizeof(dbRecordAttribute)); + if(pattribute) { + ellInsert(&precordType->attributeList,&pattribute->node, + &pnew->node); + } else { + ellAdd(&precordType->attributeList,&pnew->node); + } + pattribute = pnew; + pattribute->name = dbCalloc(strlen(name)+1,sizeof(char)); + strcpy(pattribute->name,name); + pdbFldDes = dbCalloc(1,sizeof(dbFldDes)); + pdbFldDes->name = pattribute->name; + pdbFldDes->pdbRecordType = precordType; + pdbFldDes->special = SPC_ATTRIBUTE; + pdbFldDes->field_type = DBF_STRING; + pdbFldDes->as_level = ASL1; + pdbFldDes->size = MAX_STRING_SIZE; + pattribute->pdbFldDes = pdbFldDes; + } + strncpy(pattribute->value,value,MAX_STRING_SIZE); + pattribute->value[MAX_STRING_SIZE-1] = 0; + return(0); +} + +long epicsShareAPI dbGetAttributePart(DBENTRY *pdbentry, const char **ppname) +{ + dbRecordType *precordType = pdbentry->precordType; + const char *pname = *ppname; + dbRecordAttribute *pattribute; + + if (!precordType) return S_dbLib_recordTypeNotFound; + pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList); + while (pattribute) { + int nameLen = strlen(pattribute->name); + int compare = strncmp(pattribute->name, pname, nameLen); + int ch = pname[nameLen]; + if (compare == 0 && !(ch == '_' || isalnum(ch))) { + pdbentry->pflddes = pattribute->pdbFldDes; + pdbentry->pfield = pattribute->value; + *ppname = &pname[nameLen]; + return 0; + } + if (compare >= 0) break; + pattribute = (dbRecordAttribute *)ellNext(&pattribute->node); + } + return S_dbLib_fieldNotFound; +} + +long epicsShareAPI dbGetRecordAttribute(DBENTRY *pdbentry, const char *pname) +{ + return dbGetAttributePart(pdbentry, &pname); +} + +long epicsShareAPI dbFirstField(DBENTRY *pdbentry,int dctonly) +{ + + pdbentry->indfield = -1; + return(dbNextField(pdbentry,dctonly)); +} + +long epicsShareAPI dbNextField(DBENTRY *pdbentry,int dctonly) +{ + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes; + short indfield = pdbentry->indfield; + long status; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + indfield++; + while(TRUE) { + if(indfield>=precordType->no_fields) { + pdbentry->indfield = 0; + pdbentry->pflddes = NULL; + pdbentry->pfield = NULL; + return(S_dbLib_fieldNotFound); + } + pflddes = precordType->papFldDes[indfield]; + if(!dctonly || pflddes->promptgroup) { + /*Skip field if dctonly and no device support*/ + if(!dctonly || (pflddes->field_type!=DBF_DEVICE) + || (ellCount(&precordType->devList)>0)) { + pdbentry->indfield = indfield; + pdbentry->pflddes = pflddes; + pdbentry->indfield = indfield; + if(precnode) { + status = dbGetFieldAddress(pdbentry); + }else { + pdbentry->pfield = NULL; + } + return(0); + } + } + indfield++; + } +} + +int epicsShareAPI dbGetFieldType(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + long status; + + if(!pflddes){ + status = S_dbLib_flddesNotFound; + entryErrMessage(pdbentry,status,"dbGetFieldType"); + return(status); + } + return(mapDBFtoDCT[pflddes->field_type]); +} + +int epicsShareAPI dbGetNFields(DBENTRY *pdbentry,int dctonly) +{ + dbRecordType *precordType = pdbentry->precordType; + dbFldDes *pflddes; + int indfield,n; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + n = 0; + for(indfield=0; indfieldno_fields; indfield++) { + pflddes = precordType->papFldDes[indfield]; + if(dctonly && (pflddes->field_type==DBF_DEVICE) + && (ellCount(&precordType->devList)==0) ) continue; + if(!dctonly || pflddes->promptgroup) n++; + } + return(n); +} + +char * epicsShareAPI dbGetFieldName(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pflddes) return(NULL); + return(pflddes->name); +} + +char * epicsShareAPI dbGetDefault(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pflddes) return(NULL); + return(pflddes->initial); +} + +char * epicsShareAPI dbGetPrompt(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pflddes) return(NULL); + return(&pflddes->prompt[0]); +} + +int epicsShareAPI dbGetPromptGroup(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pflddes) return(0); + return(pflddes->promptgroup); +} + +long epicsShareAPI dbCreateRecord(DBENTRY *pdbentry,const char *precordName) +{ + dbRecordType *precordType = pdbentry->precordType; + dbFldDes *pdbFldDes; + PVDENTRY *ppvd; + ELLLIST *preclist = NULL; + dbRecordNode *precnode = NULL; + dbRecordNode *pNewRecNode = NULL; + long status = 0; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + /*Get size of NAME field*/ + pdbFldDes = precordType->papFldDes[0]; + if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0)) + return(S_dbLib_nameLength); + if((int)strlen(precordName)>=pdbFldDes->size) return(S_dbLib_nameLength); + /* clear callers entry */ + zeroDbentry(pdbentry); + if(!dbFindRecord(pdbentry,precordName)) return (S_dbLib_recExists); + zeroDbentry(pdbentry); + pdbentry->precordType = precordType; + preclist = &precordType->recList; + /* create a recNode */ + pNewRecNode = dbCalloc(1,sizeof(dbRecordNode)); + /* create a new record of this record type */ + pdbentry->precnode = pNewRecNode; + if((status = dbAllocRecord(pdbentry,precordName))) return(status); + pNewRecNode->recordname = dbRecordName(pdbentry); + ellInit(&pNewRecNode->infoList); + /* install record node in list in sorted postion */ + status = dbFirstRecord(pdbentry); + while(status==0) { + if(strcmp(precordName,dbGetRecordName(pdbentry)) < 0) break; + status = dbNextRecord(pdbentry); + } + if(status==0) { + precnode = pdbentry->precnode; + ellInsert(preclist,ellPrevious(&precnode->node),&pNewRecNode->node); + } else { + ellAdd(preclist,&pNewRecNode->node); + } + pdbentry->precnode = pNewRecNode; + ppvd = dbPvdAdd(pdbentry->pdbbase,precordType,pNewRecNode); + if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} + return(0); +} + +long epicsShareAPI dbDeleteAliases(DBENTRY *pdbentry) +{ + dbBase *pdbbase = pdbentry->pdbbase; + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + ELLLIST *preclist = &precordType->recList; + dbRecordNode *pAliasNode, *pAliasNodeNext; + DBENTRY dbentry; + void *precord; + + if (!precnode) return S_dbLib_recNotFound; + if (precnode->flags & DBRN_FLAGS_ISALIAS) return S_dbLib_recExists; + precord = precnode->precord; + + dbInitEntry(pdbbase, &dbentry); + pAliasNode = (dbRecordNode *)ellFirst(preclist); + while (pAliasNode) { + pAliasNodeNext = (dbRecordNode *)ellNext(&pAliasNode->node); + if (pAliasNode->flags & DBRN_FLAGS_ISALIAS && + pAliasNode->precord == precord && + !dbFindRecord(&dbentry, pAliasNode->recordname)) { + dbDeleteRecord(&dbentry); + } + pAliasNode = pAliasNodeNext; + } + precnode->flags &= ~DBRN_FLAGS_HASALIAS; + return 0; +} + +long epicsShareAPI dbDeleteRecord(DBENTRY *pdbentry) +{ + dbBase *pdbbase = pdbentry->pdbbase; + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + ELLLIST *preclist; + long status; + + if (!precnode) return S_dbLib_recNotFound; + if (precnode->flags & DBRN_FLAGS_HASALIAS) + dbDeleteAliases(pdbentry); + + preclist = &precordType->recList; + ellDelete(preclist, &precnode->node); + dbPvdDelete(pdbbase, precnode); + while (!dbFirstInfo(pdbentry)) { + dbDeleteInfo(pdbentry); + } + if (precnode->flags & DBRN_FLAGS_ISALIAS) { + free(precnode->recordname); + precordType->no_aliases--; + } else { + status = dbFreeRecord(pdbentry); + if (status) return status; + } + free(precnode); + pdbentry->precnode = NULL; + return 0; +} + +long epicsShareAPI dbFreeRecords(DBBASE *pdbbase) +{ + DBENTRY dbentry; + dbRecordType *pdbRecordType; + dbRecordNode *pdbRecordNode; + dbRecordNode *pdbRecordNodeNext; + + dbInitEntry(pdbbase,&dbentry); + pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + while(pdbRecordType) { + pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); + while(pdbRecordNode) { + pdbRecordNodeNext = (dbRecordNode *)ellNext(&pdbRecordNode->node); + if(!dbFindRecord(&dbentry,pdbRecordNode->recordname)) + dbDeleteRecord(&dbentry); + pdbRecordNode = pdbRecordNodeNext; + } + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node); + } + dbFinishEntry(&dbentry); + return(0); +} + +long epicsShareAPI dbFindRecordPart(DBENTRY *pdbentry, const char **ppname) +{ + dbBase *pdbbase = pdbentry->pdbbase; + const char *pname = *ppname; + const char *pfn; + int lenName; + PVDENTRY *ppvdNode; + + zeroDbentry(pdbentry); + pfn = strchr(pname, '.'); + if (pfn) { + lenName = pfn - pname; + } else { + lenName = strlen(pname); + } + + ppvdNode = dbPvdFind(pdbbase, pname, lenName); + if (!ppvdNode) + return S_dbLib_recNotFound; + + pdbentry->precnode = ppvdNode->precnode; + pdbentry->precordType = ppvdNode->precordType; + *ppname = pname + lenName; + return 0; +} + +long epicsShareAPI dbFindRecord(DBENTRY *pdbentry, const char *pname) +{ + long status = dbFindRecordPart(pdbentry, &pname); + + if (status) return status; + if (*pname == '.') + return dbFindField(pdbentry, ++pname); + return 0; +} + +long epicsShareAPI dbFirstRecord(DBENTRY *pdbentry) +{ + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode; + + zeroDbentry(pdbentry); + if(!precordType) return(S_dbLib_recordTypeNotFound); + pdbentry->precordType = precordType; + precnode = (dbRecordNode *)ellFirst(&precordType->recList); + if(!precnode) return(S_dbLib_recNotFound); + pdbentry->precnode = precnode; + return(0); +} + +long epicsShareAPI dbNextRecord(DBENTRY *pdbentry) +{ + dbRecordNode *precnode=pdbentry->precnode; + long status=0; + + if(!precnode) return(S_dbLib_recNotFound); + precnode = (dbRecordNode *)ellNext(&precnode->node); + if(!precnode) status = S_dbLib_recNotFound; + pdbentry->precnode = precnode; + pdbentry->pfield = NULL; + return(status); +} + +int epicsShareAPI dbGetNRecords(DBENTRY *pdbentry) +{ + dbRecordType *precordType = pdbentry->precordType; + + if(!precordType) return(0); + return(ellCount(&precordType->recList)); +} + +int epicsShareAPI dbGetNAliases(DBENTRY *pdbentry) +{ + dbRecordType *precordType = pdbentry->precordType; + + if(!precordType) return(0); + return(precordType->no_aliases); +} + +char * epicsShareAPI dbGetRecordName(DBENTRY *pdbentry) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + + if(!pdbRecordType) return NULL; + if(!precnode) return NULL; + return precnode->recordname; +} + +long epicsShareAPI dbRenameRecord(DBENTRY *pdbentry,const char *newName) +{ + dbBase *pdbbase = pdbentry->pdbbase; + dbRecordType *precordType = pdbentry->precordType; + dbFldDes *pdbFldDes; + dbRecordNode *precnode = pdbentry->precnode; + PVDENTRY *ppvd; + ELLLIST *preclist; + dbRecordNode *plistnode; + long status; + DBENTRY dbentry; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + /*Get size of NAME field*/ + pdbFldDes = precordType->papFldDes[0]; + if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0)) + return(S_dbLib_nameLength); + if((int)strlen(newName)>=pdbFldDes->size) return(S_dbLib_nameLength); + if (!precnode || dbIsAlias(pdbentry)) return S_dbLib_recNotFound; + dbInitEntry(pdbentry->pdbbase,&dbentry); + status = dbFindRecord(&dbentry,newName); + dbFinishEntry(&dbentry); + if(!status) return(S_dbLib_recExists); + dbPvdDelete(pdbbase,precnode); + pdbentry->pflddes = precordType->papFldDes[0]; + if((status = dbGetFieldAddress(pdbentry))) return(status); + strcpy(pdbentry->pfield,newName); + ppvd = dbPvdAdd(pdbbase,precordType,precnode); + if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} + /*remove from record list and reinstall in sorted order*/ + preclist = &precordType->recList; + ellDelete(preclist,&precnode->node); + plistnode = (dbRecordNode *)ellFirst(preclist); + while(plistnode) { + pdbentry->precnode = plistnode; + if(strcmp(newName,dbGetRecordName(pdbentry)) >=0) break; + plistnode = (dbRecordNode *)ellNext(&plistnode->node); + } + if(plistnode) + ellInsert(preclist,ellPrevious(&plistnode->node),&precnode->node); + else + ellAdd(preclist,&precnode->node); + /*Leave pdbentry pointing to newly renamed record*/ + return(dbFindRecord(pdbentry,newName)); +} + +long epicsShareAPI dbVisibleRecord(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + + if(!precnode) return(S_dbLib_recNotFound); + precnode->flags |= DBRN_FLAGS_VISIBLE; + return 0; +} + +long epicsShareAPI dbInvisibleRecord(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + + if(!precnode) return(S_dbLib_recNotFound); + precnode->flags &= ~DBRN_FLAGS_VISIBLE; + return 0; +} + +int epicsShareAPI dbIsVisibleRecord(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + + if(!precnode) return 0; + return precnode->flags & DBRN_FLAGS_VISIBLE ? 1 : 0; +} + +long epicsShareAPI dbCreateAlias(DBENTRY *pdbentry, const char *alias) +{ + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbRecordNode *pnewnode; + PVDENTRY *ppvd; + ELLLIST *preclist = NULL; + long status; + + if (!precordType) return S_dbLib_recordTypeNotFound; + if (!precnode) return S_dbLib_recNotFound; + zeroDbentry(pdbentry); + if (!dbFindRecord(pdbentry, alias)) return S_dbLib_recExists; + zeroDbentry(pdbentry); + pdbentry->precordType = precordType; + preclist = &precordType->recList; + pnewnode = dbCalloc(1, sizeof(dbRecordNode)); + pnewnode->recordname = epicsStrDup(alias); + pnewnode->precord = precnode->precord; + pnewnode->flags = DBRN_FLAGS_ISALIAS; + if (!(precnode->flags & DBRN_FLAGS_ISALIAS)) + precnode->flags |= DBRN_FLAGS_HASALIAS; + ellInit(&pnewnode->infoList); + /* install record node in list in sorted postion */ + status = dbFirstRecord(pdbentry); + while (!status) { + if (strcmp(alias, dbGetRecordName(pdbentry)) < 0) break; + status = dbNextRecord(pdbentry); + } + if (!status) { + precnode = pdbentry->precnode; + ellInsert(preclist, ellPrevious(&precnode->node), &pnewnode->node); + } else { + ellAdd(preclist, &pnewnode->node); + } + precordType->no_aliases++; + pdbentry->precnode = pnewnode; + ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode); + if (!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} + return 0; +} + +int epicsShareAPI dbIsAlias(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + + if(!precnode) return 0; + return precnode->flags & DBRN_FLAGS_ISALIAS ? 1 : 0; +} + +long epicsShareAPI dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int overWriteOK) +{ + dbRecordType *precordType = pdbentry->precordType; + dbFldDes *pdbFldDes; + dbRecordNode *precnode = pdbentry->precnode; + long status; + DBENTRY dbentry; + char *pvalue; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + /*Get size of NAME field*/ + pdbFldDes = precordType->papFldDes[0]; + if(!pdbFldDes || (strcmp(pdbFldDes->name,"NAME")!=0)) + return(S_dbLib_nameLength); + if((int)strlen(newRecordName)>=pdbFldDes->size) return(S_dbLib_nameLength); + if (!precnode || dbIsAlias(pdbentry)) return S_dbLib_recNotFound; + dbInitEntry(pdbentry->pdbbase,&dbentry); + status = dbFindRecord(&dbentry,newRecordName); + if(!status) { + if(!overWriteOK) { + dbFinishEntry(&dbentry); + return(S_dbLib_recExists); + } + status = dbDeleteRecord(&dbentry); + if(status) return(status); + } + dbFinishEntry(&dbentry); + if((status = dbFindRecordType(&dbentry,precordType->name))) return(status); + if((status = dbCreateRecord(&dbentry,newRecordName))) return(status); + if((status = dbFirstField(pdbentry,TRUE))) return(status); + if((status = dbFirstField(&dbentry,TRUE))) return(status); + while(!status) { + if(!dbIsDefaultValue(pdbentry)) { + pvalue = dbGetString(pdbentry); + if((status = dbPutString(&dbentry,pvalue))) return(status); + } + status = dbNextField(pdbentry,TRUE); + if(!status) status = dbNextField(&dbentry,TRUE); + if(!status && (pdbentry->pflddes!=dbentry.pflddes)) { + epicsPrintf("dbCopyRecord: Logic Error\n"); + return(-1); + } + } + /*Copy the info strings too*/ + status = dbFirstInfo(pdbentry); + while (!status) { + status = dbPutInfo(&dbentry, dbGetInfoName(pdbentry), dbGetInfoString(pdbentry)); + if (status) return (status); + status = dbNextInfo(pdbentry); + } + /*Leave pdbentry pointing to newRecordName*/ + return(dbFindRecord(pdbentry,newRecordName)); +} + +long epicsShareAPI dbFindFieldPart(DBENTRY *pdbentry,const char **ppname) +{ + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + const char *pname = *ppname; + char *precord; + short top, bottom, test; + char **papsortFldName; + short *sortFldInd; + int ch; + int nameLen; + + if (!precordType) return S_dbLib_recordTypeNotFound; + if (!precnode) return S_dbLib_recNotFound; + precord = precnode->precord; + papsortFldName = precordType->papsortFldName; + sortFldInd = precordType->sortFldInd; + + /* Measure field name length; name is a valid C identifier */ + nameLen = 0; + if ((ch = *pname) && + (ch == '_' || isalpha(ch))) { + while ((ch = pname[++nameLen])) + if (!(ch == '_' || isalnum(ch))) break; + } + + /* Handle absent field name */ + if (nameLen == 0) { + dbFldDes *pflddes = precordType->pvalFldDes; + + if (!pflddes) + return S_dbLib_recordTypeNotFound; + pdbentry->pflddes = pflddes; + pdbentry->indfield = precordType->indvalFlddes; + *ppname = &pname[nameLen]; + return dbGetFieldAddress(pdbentry); + } + + /* binary search through ordered field names */ + top = precordType->no_fields - 1; + bottom = 0; + test = (top + bottom) / 2; + while (1) { + int compare = strncmp(papsortFldName[test], pname, nameLen); + if (compare == 0) + compare = strlen(papsortFldName[test]) - nameLen; + if (compare == 0) { + dbFldDes *pflddes = precordType->papFldDes[sortFldInd[test]]; + + if (!pflddes) + return S_dbLib_recordTypeNotFound; + pdbentry->pflddes = pflddes; + pdbentry->indfield = sortFldInd[test]; + *ppname = &pname[nameLen]; + return dbGetFieldAddress(pdbentry); + } else if (compare > 0) { + top = test - 1; + if (top < bottom) break; + test = (top + bottom) / 2; + } else { + bottom = test + 1; + if (top < bottom) break; + test = (top + bottom) / 2; + } + } + return S_dbLib_fieldNotFound; +} + +long epicsShareAPI dbFindField(DBENTRY *pdbentry,const char *pname) +{ + long status = dbFindFieldPart(pdbentry, &pname); + int ch; + + if (status == S_dbLib_fieldNotFound) + return dbGetRecordAttribute(pdbentry, pname); + if (status) return status; + + ch = *pname; + if (ch == 0 || isspace(ch)) return 0; + return S_dbLib_recNotFound; +} + +int epicsShareAPI dbFoundField(DBENTRY *pdbentry) +{ return((pdbentry->pfield) ? TRUE : FALSE); } + +char * epicsShareAPI dbGetString(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + char *message; + unsigned char cvttype; + DBLINK *plink; + + message = getpMessage(pdbentry); + if(!pflddes) {strcpy(message,"fldDes not found"); return(message);} + cvttype = pflddes->base; + switch (pflddes->field_type) { + case DBF_STRING: + if(!pfield) {strcpy(message,"Field not found"); return(message);} + strcpy(message, (char *)pfield); + break; + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_USHORT: + case DBF_ENUM: + case DBF_LONG: + case DBF_ULONG: + case DBF_FLOAT: + case DBF_DOUBLE: + case DBF_MENU: + case DBF_DEVICE: + return(dbGetStringNum(pdbentry)); + case DBF_INLINK: + case DBF_OUTLINK: + if(!pfield) {strcpy(message,"Field not found"); return(message);} + plink = (DBLINK *)pfield; + switch(plink->type) { + case CONSTANT: + if(plink->value.constantStr) { + strcpy(message,plink->value.constantStr); + } else { + strcpy(message,""); + } + break; + case MACRO_LINK: + if(plink->value.macro_link.macroStr) { + strcpy(message,plink->value.macro_link.macroStr); + } else { + strcpy(message,""); + } + break; + case PV_LINK: + case CA_LINK: + case DB_LINK: { + int ppind; + short pvlMask; + + pvlMask = plink->value.pv_link.pvlMask; + if(pvlMask&pvlOptPP) ppind=1; + else if(pvlMask&pvlOptCA) ppind=2; + else if(pvlMask&pvlOptCP) ppind=3; + else if(pvlMask&pvlOptCPP) ppind=4; + else ppind=0; + if(plink->value.pv_link.pvname) + strcpy(message,plink->value.pv_link.pvname); + else + strcpy(message,""); + strcat(message," "); + strcat(message,ppstring[ppind]); + strcat(message," "); + strcat(message,msstring[pvlMask&pvlOptMsMode]); + break; + } + case VME_IO: + sprintf(message,"#C%d S%d @%s", + plink->value.vmeio.card,plink->value.vmeio.signal, + plink->value.vmeio.parm); + break; + case CAMAC_IO: + sprintf(message,"#B%d C%d N%d A%d F%d @%s", + plink->value.camacio.b,plink->value.camacio.c, + plink->value.camacio.n,plink->value.camacio.a, + plink->value.camacio.f,plink->value.camacio.parm); + break; + case RF_IO: + sprintf(message,"#R%d M%d D%d E%d", + plink->value.rfio.cryo, + plink->value.rfio.micro, + plink->value.rfio.dataset, + plink->value.rfio.element); + break; + case AB_IO: + sprintf(message,"#L%d A%d C%d S%d @%s", + plink->value.abio.link,plink->value.abio.adapter, + plink->value.abio.card,plink->value.abio.signal, + plink->value.abio.parm); + break; + case GPIB_IO: + sprintf(message,"#L%d A%d @%s", + plink->value.gpibio.link,plink->value.gpibio.addr, + plink->value.gpibio.parm); + break; + case BITBUS_IO: + sprintf(message,"#L%u N%u P%u S%u @%s", + plink->value.bitbusio.link,plink->value.bitbusio.node, + plink->value.bitbusio.port,plink->value.bitbusio.signal, + plink->value.bitbusio.parm); + break; + case BBGPIB_IO: + sprintf(message,"#L%u B%u G%u @%s", + plink->value.bbgpibio.link,plink->value.bbgpibio.bbaddr, + plink->value.bbgpibio.gpibaddr,plink->value.bbgpibio.parm); + break; + case INST_IO: + sprintf(message,"@%s", plink->value.instio.string); + break; + case VXI_IO : + if (plink->value.vxiio.flag == VXIDYNAMIC) + sprintf(message,"#V%d C%d S%d @%s", + plink->value.vxiio.frame,plink->value.vxiio.slot, + plink->value.vxiio.signal,plink->value.vxiio.parm); + else + sprintf(message,"#V%d S%d @%s", + plink->value.vxiio.la,plink->value.vxiio.signal, + plink->value.vxiio.parm); + break; + default : + return(NULL); + } + break; + case DBF_FWDLINK: { + DBLINK *plink=(DBLINK *)pfield; + + if(!pfield) {strcpy(message,"Field not found"); return(message);} + switch(plink->type) { + case CONSTANT: + strcpy(message,"0"); + break; + case MACRO_LINK: + if(plink->value.macro_link.macroStr) { + strcpy(message,plink->value.macro_link.macroStr); + } else { + strcpy(message,""); + } + break; + case PV_LINK: + case CA_LINK: + case DB_LINK: { + int ppind; + short pvlMask; + + pvlMask = plink->value.pv_link.pvlMask; + if(pvlMask&pvlOptCA) ppind=2; + else ppind=0; + if(plink->value.pv_link.pvname) + strcpy(message,plink->value.pv_link.pvname); + else + strcpy(message,""); + if(ppind) { + strcat(message," "); + strcat(message,ppstring[ppind]); + } + break; + } + default : + return(NULL); + } + } + break; + default: + return(NULL); + } + return (message); +} + +static void cvtDecimalOrHexToShort(char *from,short *value) +{ + if(strspn(from,"0x")==2 || strspn(from,"0X")==2) { + sscanf(from,"%hi",value); + } else { + sscanf(from,"%hd",value); + } +} + +long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + long status=0; + int macroIsOk; + int stringHasMacro=FALSE; + + if(!pflddes) return(S_dbLib_flddesNotFound); + macroIsOk = dbIsMacroOk(pdbentry); + stringHasMacro = strstr(pstring,"$(") || strstr(pstring,"${"); + if(!macroIsOk && stringHasMacro) { + epicsPrintf("%s.%s Has unexpanded macro\n", + dbGetRecordName(pdbentry),dbGetFieldName(pdbentry)); + return(S_dbLib_badField); + } + switch (pflddes->field_type) { + case DBF_STRING: + if(!pfield) return(S_dbLib_fieldNotFound); + strncpy((char *)pfield, pstring,pflddes->size); + if((pflddes->special == SPC_CALC) && !stringHasMacro) { + char rpcl[RPCL_LEN]; + short err; + + if (postfix(pstring,rpcl,&err)) { + status = S_dbLib_badField; + errlogPrintf("%s in CALC expression '%s'\n", + calcErrorStr(err), pstring); + } + } + if((short)strlen(pstring) >= pflddes->size) status = S_dbLib_strLen; + break; + + case DBF_CHAR: + case DBF_SHORT: + case DBF_LONG: + case DBF_UCHAR: + case DBF_USHORT: + case DBF_ULONG: + case DBF_ENUM: + case DBF_FLOAT: + case DBF_DOUBLE: + case DBF_MENU: + status = dbPutStringNum(pdbentry,pstring); + break; + + case DBF_DEVICE: { + DBENTRY dbEntry; + char *name; + + status = dbPutStringNum(pdbentry, pstring); + if (status) return status; + + name = dbGetRelatedField(pdbentry); + if (!name) return 0; + + dbCopyEntryContents(pdbentry, &dbEntry); + status = dbFindField(&dbEntry, name); + if (!status) + status = setLinkType(&dbEntry); + dbFinishEntry(&dbEntry); + return status; + } + + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: { + DBLINK *plink; + char string[80]; + char *pstr = string; + int ind; + + if (!pfield) + return S_dbLib_fieldNotFound; + + plink = (DBLINK *)pfield; + dbFreeLinkContents(plink); + if (stringHasMacro) { + plink->type = MACRO_LINK; + plink->value.macro_link.macroStr = epicsStrDup(pstring); + goto done; + } + + if (strcmp(pflddes->name, "INP") == 0 || + strcmp(pflddes->name, "OUT") == 0) { + status = setLinkType(pdbentry); /* This uses DTYP to look up and set plink->type, necessary for default DTYP */ + if (status) { + errMessage(status,"in dbPutString from setLinkType"); + return status; + } + /* store link text in case DTYP changes later */ + plink->text = epicsStrDup(pstring); + } + if (strlen(pstring) >= sizeof(string)) { + status = S_dbLib_badField; + errMessage(status, + "dbPutString received a string that is too long"); + return status; + } + strcpy(pstr, pstring); + /* Strip leading blanks and tabs */ + while (*pstr && (*pstr == ' ' || *pstr == '\t')) pstr++; + /* Strip trailing blanks and tabs */ + if (pstr) + for (ind = strlen(pstr) - 1; ind >= 0; ind--) { + if (pstr[ind] != ' ' && pstr[ind] != '\t') break; + pstr[ind] = '\0'; + } + if (!pstr || !*pstr) { + if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry); + if (plink->type != CONSTANT) return S_dbLib_badField; + free((void *)plink->value.constantStr); + plink->value.constantStr = NULL; + goto done; + } + switch (plink->type) { + case CONSTANT: + case PV_LINK: { + short ppOpt = 0; + short msOpt = 0; + char *end; + char *enddouble; + double tempdouble; + char *endlong; + long templong; + + /* Check first to see if string is a constant*/ + /*It is a string if epicsStrtod or strtol eats entire string*/ + /*leading and trailing blanks have already been stripped*/ + tempdouble = epicsStrtod(pstr, &enddouble); + templong = strtol(pstr, &endlong, 0); + + if (*enddouble == 0 || *endlong == 0) { + if (plink->type == PV_LINK) dbCvtLinkToConstant(pdbentry); + if (!plink->value.constantStr || + strlen(plink->value.constantStr) < strlen(pstr)) { + free(plink->value.constantStr); + plink->value.constantStr = + dbCalloc(strlen(pstr) + 1, sizeof(char)); + } + strcpy(plink->value.constantStr, pstr); + goto done; + } + + if (plink->type==CONSTANT) dbCvtLinkToPvlink(pdbentry); + end = strchr(pstr,' '); + if (end) { + switch (pflddes->field_type) { + case DBF_INLINK: { + if (strstr(end, "NPP")) ppOpt = 0; + else if (strstr(end, "CPP")) ppOpt = pvlOptCPP; + else if (strstr(end, "PP")) ppOpt = pvlOptPP; + else if (strstr(end, "CA")) ppOpt = pvlOptCA; + else if (strstr(end, "CP")) ppOpt = pvlOptCP; + else ppOpt = 0; + if (strstr(end, "NMS")) msOpt = pvlOptNMS; + else if (strstr(end, "MSI")) msOpt = pvlOptMSI; + else if (strstr(end, "MSS")) msOpt = pvlOptMSS; +/*must be the last one:*/ else if (strstr(end, "MS")) msOpt = pvlOptMS; + else msOpt = 0; + *end = 0; + } + break; + + case DBF_OUTLINK: { + if (strstr(end, "NPP")) ppOpt = 0; + else if(strstr(end, "PP")) ppOpt = pvlOptPP; + else if(strstr(end, "CA")) ppOpt = pvlOptCA; + else ppOpt = 0; + if (strstr(end, "NMS")) msOpt = pvlOptNMS; + else if(strstr(end, "MSI")) msOpt = pvlOptMSI; + else if(strstr(end, "MSS")) msOpt = pvlOptMSS; +/*must be the last one:*/ else if(strstr(end, "MS")) msOpt = pvlOptMS; + else msOpt = 0; + *end = 0; + } + break; + + case DBF_FWDLINK: { + if (strstr(end, "NPP")) ppOpt = 0; + else if (strstr(end, "CA")) ppOpt = pvlOptCA; + else ppOpt = 0; + msOpt = 0; + *end = 0; + } + break; + + default: + epicsPrintf("dbPutString: Logic Error\n"); + } + } + status = putPvLink(pdbentry,ppOpt|msOpt,pstr); + goto done; + } + /*break; is unnecessary*/ + case VME_IO: { + char *end; + + if (!(end = strchr(pstr,'#'))) return S_dbLib_badField; + pstr = end + 1; + if (!(end = strchr(pstr,'C'))) return S_dbLib_badField; + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.vmeio.card); + if (!(end = strchr(pstr,'S'))) return S_dbLib_badField; + pstr = end + 1; + cvtDecimalOrHexToShort(pstr, &plink->value.vmeio.signal); + status = putParmString(&plink->value.vmeio.parm, pstr); + } + break; + + case CAMAC_IO: { + char *end; + + if (!(end = strchr(pstr,'#'))) return (S_dbLib_badField); + pstr = end + 1; + if (!(end = strchr(pstr,'B'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.camacio.b); + if (!(end = strchr(pstr,'C'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.camacio.c); + if (!(end = strchr(pstr,'N'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.camacio.n); + if (!(end = strchr(pstr,'A'))) { + plink->value.camacio.a = 0; + } else { + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.camacio.a); + } + if (!(end = strchr(pstr,'F'))) { + plink->value.camacio.f = 0; + } else { + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.camacio.f); + } + status = putParmString(&plink->value.camacio.parm,pstr); + } + break; + + case RF_IO: { + char *end; + + if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); + pstr = end + 1; + if(!(end = strchr(pstr,'R'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.rfio.cryo); + if(!(end = strchr(pstr,'M'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.rfio.micro); + if(!(end = strchr(pstr,'D'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.rfio.dataset); + if(!(end = strchr(pstr,'E'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.rfio.element); + } + break; + case AB_IO: { + char *end; + + if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); + pstr = end + 1; + if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.abio.link); + if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.abio.adapter); + if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.abio.card); + if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.abio.signal); + status = putParmString(&plink->value.abio.parm,pstr); + } + break; + case GPIB_IO: { + char *end; + + if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); + pstr = end + 1; + if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.link); + if(!(end = strchr(pstr,'A'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.gpibio.addr); + status = putParmString(&plink->value.gpibio.parm,pstr); + } + break; + case BITBUS_IO: { + /* jbk - the bbgpib struct uses unsigned char's instead + of short, so read values into short and then convert */ + + char *end; + short tmp_val; + + if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); + pstr = end + 1; + if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&tmp_val); + plink->value.bitbusio.link=(unsigned char)tmp_val; + if(!(end = strchr(pstr,'N'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&tmp_val); + plink->value.bitbusio.node=(unsigned char)tmp_val; + if(!(end = strchr(pstr,'P'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&tmp_val); + plink->value.bitbusio.port=(unsigned char)tmp_val; + if(!(end = strchr(pstr,'S'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&tmp_val); + plink->value.bitbusio.signal=(unsigned char)tmp_val; + status = putParmString(&plink->value.bitbusio.parm,pstr); + } + break; + case BBGPIB_IO: { + /* jbk - the bbgpib struct uses unsigned char's instead + of short, so read values into short and then convert */ + + char *end; + short tmp_val; + + if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); + pstr = end + 1; + if(!(end = strchr(pstr,'L'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&tmp_val); + plink->value.bbgpibio.link=(unsigned char)tmp_val; + if(!(end = strchr(pstr,'B'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&tmp_val); + plink->value.bbgpibio.bbaddr=(unsigned char)tmp_val; + if(!(end = strchr(pstr,'G'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&tmp_val); + plink->value.bbgpibio.gpibaddr=tmp_val; + status = putParmString(&plink->value.bbgpibio.parm,pstr); + } + break; + case VXI_IO: { + char *end; + + if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); + pstr = end + 1; + memset((char *)&plink->value.vxiio,0,sizeof(struct vxiio)); + plink->value.vxiio.parm = pNullString; + if(!((end = strchr(pstr,'C'))&&(end < strchr(pstr,'@')) )) { + plink->value.vxiio.flag = VXISTATIC; + if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.la); + } else { + plink->value.vxiio.flag = VXIDYNAMIC; + if(!(end = strchr(pstr,'V'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.frame); + if(!(end = strchr(pstr,'C'))) return (S_dbLib_badField); + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.slot); + } + if((end = strchr(pstr,'S'))) { + pstr = end + 1; + cvtDecimalOrHexToShort(pstr,&plink->value.vxiio.signal); + } else { + plink->value.vxiio.signal = 0; + } + status = putParmString(&plink->value.vxiio.parm,pstr); + } + break; + case INST_IO: { + status = putParmString(&plink->value.instio.string, pstr); + } + break; + } + } + break; + default: + return S_dbLib_badField; + } +done: + if (!status && strcmp(pflddes->name, "VAL") == 0) { + DBENTRY dbentry; + + dbCopyEntryContents(pdbentry, &dbentry); + if (!dbFindField(&dbentry, "UDF")) { + dbPutString(&dbentry, "0"); + } + dbFinishEntry(&dbentry); + } + return(status); +} + +char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) +{ + dbFldDes *pflddes = pdbentry->pflddes; + char *message; + int stringHasMacro=FALSE; + + stringHasMacro = strstr(pstring,"$(") || strstr(pstring,"${"); + message = getpMessage(pdbentry); + if(!pflddes) {strcpy(message,"fldDes not found"); return(message);} + if(strstr(pstring,"$(") || strstr(pstring,"${")) return(NULL); + switch (pflddes->field_type) { + case DBF_STRING: { + size_t length; + + length=strlen(pstring); + if(length>=pflddes->size) { + sprintf(message,"string to big. max=%hd",pflddes->size); + return(message); + } + if((pflddes->special == SPC_CALC) && !stringHasMacro) { + char rpcl[RPCL_LEN]; + short err; + long status; + + status = postfix(pstring,rpcl,&err); + if(status) { + sprintf(message,"%s in CALC expression '%s'", + calcErrorStr(err), pstring); + return(message); + } + } + } + return(NULL); + case DBF_CHAR : + case DBF_SHORT : + case DBF_LONG:{ + long value; + char *endp; + + value = strtol(pstring,&endp,0); + if(*endp!=0) { + strcpy(message,"not an integer number"); + return(message); + } + switch (pflddes->field_type) { + case DBF_CHAR : + if(value<-128 || value>127) { + strcpy(message,"must have -128<=value<=127"); + return(message); + } + return(NULL); + case DBF_SHORT : + if(value<-32768 || value>32767) { + strcpy(message,"must have -32768<=value<=32767"); + return(message); + } + return(NULL); + case DBF_LONG : return(NULL); + default: + errPrintf(-1,__FILE__, __LINE__,"Logic Error\n"); + return(NULL); + } + } + case DBF_UCHAR: + case DBF_USHORT: + case DBF_ULONG: + case DBF_ENUM:{ + unsigned long value; + char *endp; + + if(strchr(pstring,'-')) { + strcpy(message,"not an unsigned number"); + return(message); + } + value = strtoul(pstring,&endp,0); + if(*endp!=0) { + strcpy(message,"not an integer number"); + return(message); + } + switch (pflddes->field_type) { + case DBF_UCHAR : + if(value>255) { + strcpy(message,"must have 0<=value<=255"); + return(message); + } + return(NULL); + case DBF_ENUM: + case DBF_USHORT : + if(value>65535) { + strcpy(message,"must have 0<=value<=65535"); + return(message); + } + return(NULL); + case DBF_ULONG : return(NULL); + default: + errPrintf(-1,__FILE__, __LINE__,"Logic Error\n"); + return(NULL); + } + } + case DBF_FLOAT: + case DBF_DOUBLE: { + double value; + char *endp; + + value = epicsStrtod(pstring,&endp); + if(*endp!=0) { + strcpy(message,"not a number"); + return(message); + } + return(NULL); + } + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + char *pchoice; + int i; + + if(!pdbMenu) return(NULL); + for (i = 0; i < pdbMenu->nChoice; i++) { + if(!(pchoice = pdbMenu->papChoiceValue[i])) continue; + if(strcmp(pchoice, pstring) == 0) { + return(NULL); + } + } + } + strcpy(message,"Not a valid menu choice"); + return (message); + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + char *pchoice; + int i; + + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(NULL); + if(pdbDeviceMenu->nChoice == 0) return(NULL); + for (i = 0; i < pdbDeviceMenu->nChoice; i++) { + if (!(pchoice = pdbDeviceMenu->papChoice[i])) + continue; + if (strcmp(pchoice, pstring) == 0) { + return(NULL); + } + } + } + strcpy(message,"Not a valid menu choice"); + return (message); + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + return(NULL); + default: break; + } + strcpy(message,"Not a valid field type"); + return (message); +} + +char *epicsShareAPI dbGetRange(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + char *message; + + message = getpMessage(pdbentry); + if(!pflddes) {strcpy(message,"fldDes not found"); return(message);} + switch (pflddes->field_type) { + case DBF_STRING: {strcpy(message,"STRING"); return(message);} + case DBF_CHAR : {strcpy(message,"CHAR"); return(message);} + case DBF_SHORT : {strcpy(message,"SHORT");return(message);} + case DBF_LONG: {strcpy(message,"LONG"); return(message);} + case DBF_UCHAR: {strcpy(message,"UCHAR");return(message);} + case DBF_USHORT:{strcpy(message,"USHORT");return(message);} + case DBF_ULONG:{strcpy(message,"ULONG:");return(message);} + case DBF_ENUM: {strcpy(message,"ENUM");return(message);} + case DBF_FLOAT: {strcpy(message,"FLOAT");return(message);} + case DBF_DOUBLE: {strcpy(message,"DOUBLE");return(message);} + case DBF_MENU: {strcpy(message,"MENU");return(message);} + case DBF_DEVICE: {strcpy(message,"DEVICE");return(message);} + case DBF_INLINK: {strcpy(message,"INLINK");return(message);} + case DBF_OUTLINK: {strcpy(message,"OUTLINK");return(message);} + case DBF_FWDLINK: {strcpy(message,"FWDLINK");return(message);} + default: + errPrintf(-1,__FILE__, __LINE__,"Logic Error\n"); + } + strcpy(message,"Not a valid field type"); + return (message); +} + +long epicsShareAPI dbFirstInfo(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + + pdbentry->pinfonode = NULL; + if (!precnode) return (S_dbLib_recNotFound); + + pdbentry->pinfonode = (dbInfoNode *)ellFirst(&precnode->infoList); + return (pdbentry->pinfonode ? 0 : S_dbLib_infoNotFound); +} + +long epicsShareAPI dbNextInfo(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + dbInfoNode *pinfo; + + if (!precnode) return (S_dbLib_recNotFound); + pinfo = pdbentry->pinfonode; + if (!pinfo) return (S_dbLib_infoNotFound); + + pinfo = (dbInfoNode *)ellNext(&pinfo->node); + pdbentry->pinfonode = pinfo; + return (pinfo ? 0 : S_dbLib_infoNotFound); +} + +long epicsShareAPI dbFindInfo(DBENTRY *pdbentry,const char *name) +{ + dbRecordNode *precnode = pdbentry->precnode; + dbInfoNode *pinfo; + + pdbentry->pinfonode = NULL; + if (!precnode) return(S_dbLib_recNotFound); + + pinfo = (dbInfoNode *)ellFirst(&precnode->infoList); + while (pinfo) { + if (!strcmp(pinfo->name, name)) { + pdbentry->pinfonode = pinfo; + return (0); + } + pinfo = (dbInfoNode *)ellNext(&pinfo->node); + } + return (S_dbLib_infoNotFound); +} + +long epicsShareAPI dbDeleteInfo(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + dbInfoNode *pinfo = pdbentry->pinfonode; + + if (!precnode) return (S_dbLib_recNotFound); + if (!pinfo) return (S_dbLib_infoNotFound); + ellDelete(&precnode->infoList,&pinfo->node); + free(pinfo->name); + free(pinfo->string); + free(pinfo); + pdbentry->pinfonode = NULL; + return (0); +} + +const char * epicsShareAPI dbGetInfoName(DBENTRY *pdbentry) +{ + dbInfoNode *pinfo = pdbentry->pinfonode; + if (!pinfo) return (NULL); + return (pinfo->name); +} + +const char * epicsShareAPI dbGetInfoString(DBENTRY *pdbentry) +{ + dbInfoNode *pinfo = pdbentry->pinfonode; + if (!pinfo) return (NULL); + return (pinfo->string); +} + +long epicsShareAPI dbPutInfoString(DBENTRY *pdbentry,const char *string) +{ + dbInfoNode *pinfo = pdbentry->pinfonode; + char *newstring; + if (!pinfo) return (S_dbLib_infoNotFound); + newstring = realloc(pinfo->string,1+strlen(string)); + if (!newstring) return (S_dbLib_outMem); + strcpy(newstring, string); + pinfo->string = newstring; + return (0); +} + +long epicsShareAPI dbPutInfoPointer(DBENTRY *pdbentry, void *pointer) +{ + dbInfoNode *pinfo = pdbentry->pinfonode; + if (!pinfo) return (S_dbLib_infoNotFound); + pinfo->pointer = pointer; + return (0); +} + +void * epicsShareAPI dbGetInfoPointer(DBENTRY *pdbentry) +{ + dbInfoNode *pinfo = pdbentry->pinfonode; + if (!pinfo) return (NULL); + return (pinfo->pointer); +} + +const char * epicsShareAPI dbGetInfo(DBENTRY *pdbentry,const char *name) +{ + if (dbFindInfo(pdbentry, name)) return NULL; + return dbGetInfoString(pdbentry); +} + +long epicsShareAPI dbPutInfo(DBENTRY *pdbentry,const char *name,const char *string) +{ + dbInfoNode *pinfo; + dbRecordNode *precnode = pdbentry->precnode; + if (!precnode) return (S_dbLib_recNotFound); + + dbFindInfo(pdbentry, name); + pinfo = pdbentry->pinfonode; + if (pinfo) return (dbPutInfoString(pdbentry, string)); + + /*Create new info node*/ + pinfo = calloc(1,sizeof(dbInfoNode)); + if (!pinfo) return (S_dbLib_outMem); + pinfo->name = calloc(1,1+strlen(name)); + if (!pinfo->name) { + free(pinfo); + return (S_dbLib_outMem); + } + strcpy(pinfo->name, name); + pinfo->string = calloc(1,1+strlen(string)); + if (!pinfo->string) { + free(pinfo->name); + free(pinfo); + return (S_dbLib_outMem); + } + strcpy(pinfo->string, string); + ellAdd(&precnode->infoList,&pinfo->node); + pdbentry->pinfonode = pinfo; + return (0); +} + +brkTable * epicsShareAPI dbFindBrkTable(dbBase *pdbbase,const char *name) +{ + GPHENTRY *pgph; + + pgph = gphFind(pdbbase->pgpHash,name,(void *)&pdbbase->bptList); + if(!pgph) return(NULL); + return((brkTable *)pgph->userPvt); +} + +dbMenu * epicsShareAPI dbFindMenu(dbBase *pdbbase,const char *name) +{ + GPHENTRY *pgph; + + pgph = gphFind(pdbbase->pgpHash,name,(void *)&pdbbase->menuList); + if(!pgph) return(NULL); + return((dbMenu *)pgph->userPvt); +} + +char ** epicsShareAPI dbGetMenuChoices(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pflddes) return(NULL); + switch (pflddes->field_type) { + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + + if(!pdbMenu) return(NULL); + return(pdbMenu->papChoiceValue); + } + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(NULL); + return(pdbDeviceMenu->papChoice); + } + default: + return(NULL); + } +} + +int epicsShareAPI dbGetNMenuChoices(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pflddes) return(-1); + switch (pflddes->field_type) { + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + + if(!pdbMenu) return(0); + return(pdbMenu->nChoice); + } + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(0); + return(pdbDeviceMenu->nChoice); + } + default: + break; + } + return (-1); +} + +char * epicsShareAPI dbGetMenuStringFromIndex(DBENTRY *pdbentry, int index) +{ + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pflddes) return(NULL); + switch (pflddes->field_type) { + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + + if(!pdbMenu) return(NULL); + if(index<0 || index>=pdbMenu->nChoice) return(NULL); + return(pdbMenu->papChoiceValue[index]); + } + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(NULL); + if(index<0 || index>=pdbDeviceMenu->nChoice) return(NULL); + return(pdbDeviceMenu->papChoice[index]); + } + default: + break; + } + return (NULL); +} + +int epicsShareAPI dbGetMenuIndexFromString(DBENTRY *pdbentry, const char *choice) +{ + dbFldDes *pflddes = pdbentry->pflddes; + int ind; + int nChoice = 0; + char **papChoice = NULL; + + if(!pflddes) return(-1); + switch (pflddes->field_type) { + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + + if(!pdbMenu) return(-1); + papChoice = pdbMenu->papChoiceValue; + nChoice = pdbMenu->nChoice; + break; + } + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(-1); + papChoice = pdbDeviceMenu->papChoice; + nChoice = pdbDeviceMenu->nChoice; + break; + } + default: + return(-1); + } + if(nChoice<=0 || !papChoice) return(-1); + for(ind=0; indpgpHash,name,&pdbbase->drvList); + if (!pgph) return NULL; + return (drvSup *) pgph->userPvt; +} + +int epicsShareAPI dbAllocForm(DBENTRY *psave) +{ + DBENTRY dbEntry; + DBENTRY *pdbentry= &dbEntry; + dbFldDes *pflddes; + DBLINK *plink; + int nlines=0; + char *pstr; + struct form *pform; + int linkType; + long status = 0; + int nbytes,i; + + if(psave->formpvt) { + status = dbFreeForm(psave); + if(status) return(status); + } + dbCopyEntryContents(psave,pdbentry); + pflddes = pdbentry->pflddes; + if(!pflddes) { + epicsPrintf("dbAllocForm called but no field is referenced\n"); + goto done; + } + if(pflddes->field_type == DBF_DEVICE) { + status = dbFindField(pdbentry,"INP"); + if(status) status = dbFindField(pdbentry,"OUT"); + if(status) goto done; + pflddes = pdbentry->pflddes; + } else { + if((pflddes->field_type!=DBF_INLINK) + && (pflddes->field_type!=DBF_OUTLINK) + && (pflddes->field_type!=DBF_FWDLINK)) { + epicsPrintf("dbAllocForm called but not DBF_DEVICE or DBF_xxxLINK\n"); + goto done; + } + plink = (DBLINK *)(pdbentry->pfield); + if(plink->type==MACRO_LINK) goto done; + if(strcmp(pflddes->name,"INP")==0 || strcmp(pflddes->name,"OUT")==0){ + status = setLinkType(pdbentry); + if(status) { + errMessage(status,"in dbAllocForm from setLinkType"); + return(0); + } + } + } + plink = (DBLINK *)(pdbentry->pfield); + if(plink->type==MACRO_LINK) goto done; + status = mapLINKTtoFORMT(plink,pflddes,&linkType); + if(status) goto done; + nlines = formlines[linkType]; + /*Dont know how to handle string size. Just use messagesize*/ + nbytes = sizeof(struct form) + 2*nlines*(sizeof(char *) + messagesize); + pform = dbCalloc(1,nbytes); + pform->nlines = nlines; + pform->linkType = linkType; + psave->formpvt = pform; + pform->plink = plink ; + pform->prompt = promptAddr[linkType]; + pform->value = (char **)((char *)pform + sizeof(struct form)); + pform->verify = (char **)((char *)(pform->value)+nlines*sizeof(char *)); + pstr = (char *)(pform->verify) + nlines*sizeof(char *); + for(i=0; ivalue[i] = pstr; + pstr += messagesize; + } + for(i=0; iverify[i] = pstr; + pstr += messagesize; + } +done: + dbFinishEntry(pdbentry); + return(nlines); +} + +long epicsShareAPI dbFreeForm(DBENTRY *pdbentry) +{ + if(pdbentry->formpvt) { + free(pdbentry->formpvt); + pdbentry->formpvt = NULL; + } + return(0); +} + +char ** epicsShareAPI dbGetFormPrompt(DBENTRY *pdbentry) +{ + struct form *pform = pdbentry->formpvt; + + if(!pform) return(NULL); + return(pform->prompt); +} + +char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry) +{ + struct form *pform = pdbentry->formpvt; + DBLINK *plink; + char **value; + + if(!pform) return(NULL); + plink = pform->plink; + if(!plink) return(NULL); + value = pform->value; + switch(pform->linkType) { + case FORM_CONSTANT: + if(plink->value.constantStr) { + strcpy(*value,plink->value.constantStr); + } else { + strcpy(*value,""); + } + break; + case FORM_INLINK: { + short pvlMask = plink->value.pv_link.pvlMask; + + if(plink->value.pv_link.pvname) + strcpy(*value,plink->value.pv_link.pvname); + else + strcpy(*value,""); + value++; + if(pvlMask&pvlOptPP) strcpy(*value,"PP"); + else if(pvlMask&pvlOptCA) strcpy(*value,"CA"); + else if(pvlMask&pvlOptCP) strcpy(*value,"CP"); + else if(pvlMask&pvlOptCPP) strcpy(*value,"CPP"); + else strcpy(*value,"NPP"); + value++; + strcpy(*value, msstring[pvlMask&pvlOptMsMode]); + value++; + } + break; + case FORM_OUTLINK: { + short pvlMask = plink->value.pv_link.pvlMask; + + if(plink->value.pv_link.pvname) + strcpy(*value,plink->value.pv_link.pvname); + else + strcpy(*value,""); + value++; + if(pvlMask&pvlOptPP) strcpy(*value,"PP"); + else if(pvlMask&pvlOptCA) strcpy(*value,"CA"); + else strcpy(*value,"NPP"); + value++; + strcpy(*value, msstring[pvlMask&pvlOptMsMode]); + value++; + } + break; + case FORM_FWDLINK: { + short pvlMask = plink->value.pv_link.pvlMask; + + if(plink->value.pv_link.pvname) + strcpy(*value,plink->value.pv_link.pvname); + else + strcpy(*value,""); + value++; + if(pvlMask&pvlOptCA) strcpy(*value,"CA"); + else strcpy(*value,""); + } + break; + case FORM_VME_IO: + cvtShortToString(plink->value.vmeio.card,*value); + value++; + cvtShortToString(plink->value.vmeio.signal,*value); + value++; + strcpy(*value,"@"); + strcat(*value,plink->value.vmeio.parm); + break; + case FORM_CAMAC_IO: + cvtShortToString(plink->value.camacio.b,*value); + value++; + cvtShortToString(plink->value.camacio.c,*value); + value++; + cvtShortToString(plink->value.camacio.n,*value); + value++; + cvtShortToString(plink->value.camacio.a,*value); + value++; + cvtShortToString(plink->value.camacio.f,*value); + value++; + strcpy(*value,"@"); + strcat(*value,plink->value.camacio.parm); + break; + case FORM_RF_IO: + cvtShortToString(plink->value.rfio.cryo,*value); + value++; + cvtShortToString(plink->value.rfio.micro,*value); + value++; + cvtShortToString(plink->value.rfio.dataset,*value); + value++; + cvtShortToString(plink->value.rfio.element,*value); + break; + case FORM_AB_IO: + cvtShortToString(plink->value.abio.link,*value); + value++; + cvtShortToString(plink->value.abio.adapter,*value); + value++; + cvtShortToString(plink->value.abio.card,*value); + value++; + cvtShortToString(plink->value.abio.signal,*value); + value++; + strcpy(*value,"@"); + strcat(*value,plink->value.abio.parm); + break; + case FORM_GPIB_IO: + cvtShortToString(plink->value.gpibio.link,*value); + value++; + cvtShortToString(plink->value.gpibio.addr,*value); + value++; + strcpy(*value,"@"); + strcat(*value,plink->value.gpibio.parm); + break; + case FORM_BITBUS_IO: + cvtCharToString(plink->value.bitbusio.link,*value); + value++; + cvtCharToString(plink->value.bitbusio.node,*value); + value++; + cvtCharToString(plink->value.bitbusio.port,*value); + value++; + cvtCharToString(plink->value.bitbusio.signal,*value); + value++; + strcpy(*value,"@"); + strcat(*value,plink->value.bitbusio.parm); + break; + case FORM_INST_IO: + strcpy(*value,"@"); + strcat(*value,plink->value.instio.string); + break; + case FORM_BBGPIB_IO: + cvtCharToString(plink->value.bbgpibio.link,*value); + value++; + cvtCharToString(plink->value.bbgpibio.bbaddr,*value); + value++; + cvtCharToString(plink->value.bbgpibio.gpibaddr,*value); + value++; + strcpy(*value,"@"); + strcat(*value,plink->value.bbgpibio.parm); + break; + case FORM_VXI_IO: + strcpy(*value,(plink->value.vxiio.flag == VXIDYNAMIC ? "Yes" : "No")); + value++; + if(plink->value.vxiio.flag == VXIDYNAMIC) + cvtShortToString(plink->value.vxiio.frame,*value); + else + **value = 0; + value++; + if(plink->value.vxiio.flag == VXIDYNAMIC) + cvtShortToString(plink->value.vxiio.slot,*value); + else + **value = 0; + value++; + if(plink->value.vxiio.flag == VXISTATIC) + cvtShortToString(plink->value.vxiio.la,*value); + else + **value = 0; + value++; + cvtShortToString(plink->value.vxiio.signal,*value); + value++; + strcpy(*value,"@"); + strcat(*value,plink->value.vxiio.parm); + break; + default : + return(NULL); + } + return(pform->value); +} + +long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) +{ + struct form *pform = pdbentry->formpvt; + DBLINK *plink; + char **verify; + long lvalue; + double dvalue; + char *endp; + long status = 0; + + if(!pform) return(S_dbLib_badLink); + plink = pform->plink; + if(!plink) return(S_dbLib_badLink); + verify = pform->verify; + switch(pform->linkType) { + case FORM_CONSTANT: + **verify = 0; /*Initialize to no error*/ + if(**value == '\0') break; + dvalue = epicsStrtod(*value,&endp); + if(*endp!=0) { + strcpy(*verify,"Illegal. Must be number"); + break; + } + if((!plink->value.constantStr) + || ((int)strlen(plink->value.constantStr)<(int)strlen(*value))) { + free(plink->value.constantStr); + plink->value.constantStr = dbCalloc(strlen(*value)+1,sizeof(char)); + } + strcpy(plink->value.constantStr,*value); + break; + case FORM_INLINK: { + DBENTRY dbEntry; + DBENTRY *plinkentry = &dbEntry; + short ppOpt = 0; + short msOpt = 0; + const char *pstr; + + pstr = *value; + **verify = 0; + value++; verify++; + **verify = 0; /*Initialize verify to NULL*/ + if((*value==NULL) || (strcmp(*value,"")==0)) ppOpt = 0; + else if(strstr(*value,"NPP")) ppOpt = 0; + else if(strstr(*value,"CPP")) ppOpt = pvlOptCPP; + else if(strstr(*value,"PP")) ppOpt = pvlOptPP; + else if(strstr(*value,"CA")) ppOpt = pvlOptCA; + else if(strstr(*value,"CP")) ppOpt = pvlOptCP; + else strcpy(*verify,"Illegal. Chose a value"); + value++; verify++; + **verify = 0; /*Initialize verify to NULL*/ + if((*value==NULL) || (strcmp(*value,"")==0)) msOpt = 0; + else if(strstr(*value,"NMS")) msOpt = pvlOptNMS; + else if(strstr(*value,"MSI")) msOpt = pvlOptMSI; + else if(strstr(*value,"MSS")) msOpt = pvlOptMSS; + /*must be the last one:*/ + else if(strstr(*value,"MS")) msOpt = pvlOptMS; + else strcpy(*verify,"Illegal. Chose a value"); + dbCopyEntryContents(pdbentry,plinkentry); + if(pdbentry->pflddes->field_type == DBF_DEVICE) { + status = dbFindField(plinkentry,"INP"); + if(status) { + dbFinishEntry(plinkentry); + return(status); + } + } + putPvLink(plinkentry,ppOpt|msOpt,pstr); + dbFinishEntry(plinkentry); + } + break; + case FORM_OUTLINK: { + DBENTRY dbEntry; + DBENTRY *plinkentry = &dbEntry; + short ppOpt = 0; + short msOpt = 0; + const char *pstr; + + pstr = *value; + **verify = 0; + value++; verify++; + **verify = 0; /*Initialize verify to NULL*/ + if((*value==NULL) || (strcmp(*value,"")==0)) ppOpt = 0; + else if(strstr(*value,"NPP")) ppOpt = 0; + else if(strstr(*value,"PP")) ppOpt = pvlOptPP; + else if(strstr(*value,"CA")) ppOpt = pvlOptCA; + else strcpy(*verify,"Illegal. Chose a value"); + value++; verify++; + **verify = 0; /*Initialize verify to NULL*/ + if((*value==NULL) || (strcmp(*value,"")==0)) msOpt = 0; + else if(strstr(*value,"NMS")) msOpt = pvlOptNMS; + else if(strstr(*value,"MSI")) msOpt = pvlOptMSI; + else if(strstr(*value,"MSS")) msOpt = pvlOptMSS; + /*must be the last one:*/ + else if(strstr(*value,"MS")) msOpt = pvlOptMS; + else strcpy(*verify,"Illegal. Chose a value"); + dbCopyEntryContents(pdbentry,plinkentry); + if(pdbentry->pflddes->field_type == DBF_DEVICE) { + status = dbFindField(plinkentry,"OUT"); + if(status) { + dbFinishEntry(plinkentry); + return(status); + } + } + putPvLink(plinkentry,ppOpt|msOpt,pstr); + dbFinishEntry(plinkentry); + } + break; + case FORM_FWDLINK: { + short ppOpt = 0; + short msOpt = 0; + const char *pstr; + + pstr = *value; + **verify = 0; + value++; verify++; + **verify = 0; /*Initialize verify to NULL*/ + if((*value==NULL) || (strcmp(*value,"")==0)) ppOpt = 0; + else if(strstr(*value,"CA")) ppOpt = pvlOptCA; + else strcpy(*verify,"Illegal. Chose a value"); + putPvLink(pdbentry,ppOpt|msOpt,pstr); + } + break; + case FORM_VME_IO: + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.vmeio.card = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.vmeio.signal = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + status = putParmString(&plink->value.vmeio.parm,*value); + break; + case FORM_CAMAC_IO: + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.camacio.b = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.camacio.c = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.camacio.n = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.camacio.a = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.camacio.f = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + status = putParmString(&plink->value.camacio.parm,*value); + break; + case FORM_RF_IO: + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.rfio.cryo = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.rfio.micro = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.rfio.dataset = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.rfio.element = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + break; + case FORM_AB_IO: + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.abio.link = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.abio.adapter = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.abio.card = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.abio.signal = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + status = putParmString(&plink->value.abio.parm,*value); + break; + case FORM_GPIB_IO: + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.gpibio.link = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.gpibio.addr = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + status = putParmString(&plink->value.gpibio.parm,*value); + **verify = 0; + break; + case FORM_BITBUS_IO: + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.bitbusio.link = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.bitbusio.node = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.bitbusio.port = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.bitbusio.signal = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + status = putParmString(&plink->value.bitbusio.parm,*value); + **verify = 0; + break; + case FORM_INST_IO: + status = putParmString(&plink->value.instio.string,*value); + **verify = 0; + break; + case FORM_BBGPIB_IO: + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.bbgpibio.link = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.bbgpibio.bbaddr = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.bbgpibio.gpibaddr = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + status = putParmString(&plink->value.bbgpibio.parm,*value); + **verify = 0; + break; + case FORM_VXI_IO: + plink->value.vxiio.flag = + ((strchr(*value,'Y')||strchr(*value,'y') ? VXIDYNAMIC : VXISTATIC)); + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.vxiio.frame = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.vxiio.slot = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.vxiio.la = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + lvalue = strtol(*value,&endp,0); + if(*endp==0) { + plink->value.vxiio.signal = lvalue; **verify = 0; + } else { + strcpy(*verify,"Illegal. Must be number"); + } + value++; verify++; + status = putParmString(&plink->value.vxiio.parm,*value); + **verify = 0; + break; + default : + status = S_dbLib_badLink; + } + return(status); +} + +char ** epicsShareAPI dbVerifyForm(DBENTRY *pdbentry,char **value) +{ + struct form *pform = pdbentry->formpvt; + DBLINK *plink; + DBLINK templink; + int nlines,i; + + if(!pform) return(NULL); + plink = pform->plink; + if(!plink) return(NULL); + templink = *plink; + if(plink->type==CONSTANT) templink.value.constantStr = NULL; + if(plink->type==PV_LINK) templink.value.pv_link.pvname = NULL; + pform->plink = &templink; + dbPutForm(pdbentry,value); + if(plink->type==CONSTANT) free((void *)templink.value.constantStr); + if(plink->type==PV_LINK) free((void *)templink.value.pv_link.pvname); + pform->plink = plink; + nlines = pform->nlines; + for(i=0; iverify[i]) return(pform->verify); + } + return(NULL); +} + +char * epicsShareAPI dbGetRelatedField(DBENTRY *psave) +{ + DBENTRY dbEntry; + DBENTRY *pdbentry= &dbEntry; + dbFldDes *pflddes; + char *rtnval = NULL; + long status; + + pflddes = psave->pflddes; + if(pflddes->field_type !=DBF_DEVICE) return(NULL); + dbCopyEntryContents(psave,pdbentry); + pflddes = pdbentry->pflddes; + status = dbFindField(pdbentry,"INP"); + if(status) status = dbFindField(pdbentry,"OUT"); + if(!status) rtnval = pdbentry->pflddes->name; + dbFinishEntry(pdbentry); + return(rtnval); +} + +int epicsShareAPI dbGetNLinks(DBENTRY *pdbentry) +{ + dbRecordType *precordType = pdbentry->precordType; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + return((int)precordType->no_links); +} + +long epicsShareAPI dbGetLinkField(DBENTRY *pdbentry,int index) +{ + dbRecordType *precordType = pdbentry->precordType; + dbFldDes *pflddes; + + if(!precordType) return(S_dbLib_recordTypeNotFound); + if(index<0 || index>=precordType->no_links) return(S_dbLib_badLink); + pdbentry->indfield = precordType->link_ind[index]; + pdbentry->pflddes = pflddes = precordType->papFldDes[pdbentry->indfield]; + dbGetFieldAddress(pdbentry); + return(0); +} + +int epicsShareAPI dbGetLinkType(DBENTRY *pdbentry) +{ + dbFldDes *pflddes; + DBLINK *plink; + int field_type; + + dbGetFieldAddress(pdbentry); + pflddes = pdbentry->pflddes; + if(!pflddes) return(-1); + plink = (DBLINK *)pdbentry->pfield; + if(!plink) return(-1); + field_type = pflddes->field_type; + switch (field_type) { + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + switch(plink->type) { + case CONSTANT: + return(DCT_LINK_CONSTANT); + case PV_LINK: + case DB_LINK: + case CA_LINK: + return(DCT_LINK_PV); + default: + return(DCT_LINK_FORM); + } + } + return(-1); +} + +long epicsShareAPI dbCvtLinkToConstant(DBENTRY *pdbentry) +{ + dbFldDes *pflddes; + DBLINK *plink; + + dbGetFieldAddress(pdbentry); + pflddes = pdbentry->pflddes; + if(!pflddes) return(-1); + plink = (DBLINK *)pdbentry->pfield; + if(!plink) return(-1); + switch (pflddes->field_type) { + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + if(plink->type == CONSTANT) return(0); + if(plink->type != PV_LINK) return(S_dbLib_badLink); + free((void *)plink->value.pv_link.pvname); + plink->value.pv_link.pvname = NULL; + dbFreeForm(pdbentry); + plink->type = CONSTANT; + if(pflddes->initial) { + plink->value.constantStr = + dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); + strcpy(plink->value.constantStr,pflddes->initial); + } else { + plink->value.constantStr = NULL; + } + return(0); + default: + epicsPrintf("dbCvtLinkToConstant called for non link field\n"); + } + return(S_dbLib_badLink); +} + +long epicsShareAPI dbCvtLinkToPvlink(DBENTRY *pdbentry) +{ + dbFldDes *pflddes; + DBLINK *plink; + + dbGetFieldAddress(pdbentry); + pflddes = pdbentry->pflddes; + if(!pflddes) return(-1); + if(!pdbentry->precnode || !pdbentry->precnode->precord) return(-1); + plink = (DBLINK *)pdbentry->pfield; + if(!plink) return(-1); + switch (pflddes->field_type) { + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + if(plink->type == PV_LINK) return(0); + if(plink->type != CONSTANT) return(S_dbLib_badLink); + free(plink->value.constantStr); + dbFreeForm(pdbentry); + plink->type = PV_LINK; + plink->value.pv_link.pvlMask = 0; + plink->value.pv_link.pvname = 0; + plink->value.pv_link.precord = pdbentry->precnode->precord; + return(0); + default: + epicsPrintf("dbCvtLinkToPvlink called for non link field\n"); + } + return(S_dbLib_badLink); +} + +void epicsShareAPI dbDumpPath(DBBASE *pdbbase) +{ + ELLLIST *ppathList; + dbPathNode *pdbPathNode; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + ppathList = (ELLLIST *)pdbbase->pathPvt; + if(!ppathList || !(pdbPathNode = (dbPathNode *)ellFirst(ppathList))) { + printf("no path defined\n"); + return; + } + while(pdbPathNode) { + printf("%s",pdbPathNode->directory); + pdbPathNode = (dbPathNode *)ellNext(&pdbPathNode->node); + if(pdbPathNode) printf("%s", OSI_PATH_LIST_SEPARATOR); + } + printf("\n"); + return; +} + +void epicsShareAPI dbDumpRecord( + dbBase *pdbbase,const char *precordTypename,int level) +{ + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + dbWriteRecordFP(pdbbase,stdout,precordTypename,level); +} + +void epicsShareAPI dbDumpMenu(dbBase *pdbbase,const char *menuName) +{ + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + dbWriteMenuFP(pdbbase,stdout,menuName); +} + +void epicsShareAPI dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName) +{ + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + int gotMatch; + int i; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + if(recordTypeName) { + gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) + ? TRUE : FALSE; + }else { + gotMatch=TRUE; + } + if(!gotMatch) continue; + printf("name(%s) no_fields(%hd) no_prompt(%hd) no_links(%hd)\n", + pdbRecordType->name,pdbRecordType->no_fields, + pdbRecordType->no_prompt,pdbRecordType->no_links); + printf("index name\tsortind sortname\n"); + for(i=0; ino_fields; i++) { + pdbFldDes = pdbRecordType->papFldDes[i]; + printf("%5d %s\t%7d %s\n", + i,pdbFldDes->name, + pdbRecordType->sortFldInd[i],pdbRecordType->papsortFldName[i]); + } + printf("link_ind "); + for(i=0; ino_links; i++) + printf(" %hd",pdbRecordType->link_ind[i]); + printf("\n"); + printf("indvalFlddes %d name %s\n",pdbRecordType->indvalFlddes, + pdbRecordType->pvalFldDes->name); + printf("struct rset * %p rec_size %d\n", + (void *)pdbRecordType->prset,pdbRecordType->rec_size); + if(recordTypeName) break; + } +} + +void epicsShareAPI dbDumpField( + DBBASE *pdbbase,const char *recordTypeName,const char *fname) +{ + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + int gotMatch; + int i; + dbRecordAttribute *pAttribute; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + if(recordTypeName) { + gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) + ? TRUE : FALSE; + }else { + gotMatch=TRUE; + } + if(!gotMatch) continue; + printf("recordtype(%s) \n",pdbRecordType->name); + for(i=0; ino_fields; i++) { + int j; + + pdbFldDes = pdbRecordType->papFldDes[i]; + if(fname && strcmp(fname,pdbFldDes->name)!=0) continue; + printf(" %s\n", pdbFldDes->name); + printf("\t prompt: %s\n", + (pdbFldDes->prompt ? pdbFldDes->prompt : "")); + printf("\t extra: %s\n", + (pdbFldDes->extra ? pdbFldDes->extra: "")); + printf("\t indRecordType: %hd\n",pdbFldDes->indRecordType); + printf("\t special: %hd ",pdbFldDes->special); + if(pdbFldDes->special) { + for(j=0; jspecial) { + printf("%s",pamapspcType[j].strvalue); + break; + } + } + } + printf("\n"); + for(j=0; jfield_type) break; + } + if(j>=DBF_NTYPES) + printf("\t field_type: %d\n", pdbFldDes->field_type); + else + printf("\t field_type: %s\n", pamapdbfType[j].strvalue); + printf("\tprocess_passive: %hd\n",pdbFldDes->process_passive); + printf("\t base: %hd\n",pdbFldDes->base); + if(!pdbFldDes->promptgroup) { + printf("\t promptgroup: %d\n",pdbFldDes->promptgroup); + } else { + for(j=0; jpromptgroup) { + printf("\t promptgroup: %s\n", + pamapguiGroup[j].strvalue); + break; + } + } + } + printf("\t interest: %hd\n", pdbFldDes->interest); + printf("\t as_level: %hd\n",pdbFldDes->as_level); + printf("\t initial: %s\n", + (pdbFldDes->initial ? pdbFldDes->initial : "")); + if(pdbFldDes->field_type==DBF_MENU) { + if(pdbFldDes->ftPvt) + printf("\t\t menu: %s\n", + ((dbMenu *)pdbFldDes->ftPvt)->name); + else + printf("\t\t menu: NOT FOUND\n"); + } + if(pdbFldDes->field_type==DBF_DEVICE) { + printf("\t ftPvt: %p\n",pdbFldDes->ftPvt); + } + printf("\t size: %hd\n",pdbFldDes->size); + printf("\t offset: %hd\n",pdbFldDes->offset); + } + pAttribute = + (dbRecordAttribute *)ellFirst(&pdbRecordType->attributeList); + while(pAttribute) { + printf("Attribute: name %s value %s\n", + pAttribute->name,pAttribute->value); + pAttribute = (dbRecordAttribute *)ellNext(&pAttribute->node); + } + if(recordTypeName) break; + } +} + +void epicsShareAPI dbDumpDevice(DBBASE *pdbbase,const char *recordTypeName) +{ + dbRecordType *pdbRecordType; + devSup *pdevSup; + int gotMatch; + + if(recordTypeName) { + if(recordTypeName[0]==0 || recordTypeName[0] == '*') recordTypeName = 0; + } + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + if(recordTypeName) { + gotMatch = (strcmp(recordTypeName,pdbRecordType->name)==0) + ? TRUE : FALSE; + }else { + gotMatch=TRUE; + } + if(!gotMatch) continue; + printf("recordtype(%s)\n",pdbRecordType->name); + for(pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); + pdevSup; pdevSup = (devSup *)ellNext(&pdevSup->node)) { + printf(" device name: %s\n",pdevSup->name); + printf("\tchoice: %s\n",pdevSup->choice); + printf("\tlink_type: %d\n",pdevSup->link_type); + printf("\tpdset: %p\n",(void *)pdevSup->pdset); + printf("\tpdsxt: %p\n",(void *)pdevSup->pdsxt); + } + if(recordTypeName) break; + } +} + +void epicsShareAPI dbDumpDriver(DBBASE *pdbbase) +{ + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + dbWriteDriverFP(pdbbase,stdout); +} + +void epicsShareAPI dbDumpRegistrar(DBBASE *pdbbase) +{ + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + dbWriteRegistrarFP(pdbbase,stdout); +} + +void epicsShareAPI dbDumpFunction(DBBASE *pdbbase) +{ + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + dbWriteFunctionFP(pdbbase,stdout); +} + +void epicsShareAPI dbDumpVariable(DBBASE *pdbbase) +{ + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + dbWriteVariableFP(pdbbase,stdout); +} + +void epicsShareAPI dbDumpBreaktable(DBBASE *pdbbase,const char *name) +{ + brkTable *pbrkTable; + brkInt *pbrkInt; + int ind; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + for(pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList); + pbrkTable; pbrkTable = (brkTable *)ellNext(&pbrkTable->node)) { + if (name && strcmp(name,pbrkTable->name)!=0) continue; + printf("breaktable(%s) {\n",pbrkTable->name); + pbrkInt = pbrkTable->paBrkInt; + for(ind=0; ind < pbrkTable->number; ind++) { + printf("\traw=%f slope=%e eng=%f\n", + pbrkInt->raw, pbrkInt->slope, pbrkInt->eng); + pbrkInt++; + } + printf("}\n"); + } + return; +} + +static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB", + "GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"}; +void epicsShareAPI dbReportDeviceConfig(dbBase *pdbbase,FILE *report) +{ + DBENTRY dbentry; + DBENTRY *pdbentry=&dbentry; + long status; + char busName[40]; + char linkValue[40]; + char dtypValue[40]; + char cvtValue[40]; + int ilink,nlinks; + struct link *plink; + FILE *stream = (report==0) ? stdout : report; + + if(!pdbbase) { + fprintf(stderr,"pdbbase not specified\n"); + return; + } + dbInitEntry(pdbbase,pdbentry); + status = dbFirstRecordType(pdbentry); + while(!status) { + status = dbFirstRecord(pdbentry); + while(!status) { + nlinks = dbGetNLinks(pdbentry); + for(ilink=0; ilinkpfield; + strcpy(busName,bus[plink->type]); + if(strlen(busName)==0) continue; + strcpy(linkValue,dbGetString(pdbentry)); + status = dbFindField(pdbentry,"DTYP"); + if(status) break; + strcpy(dtypValue,dbGetString(pdbentry)); + status = dbFindField(pdbentry,"LINR"); + if(status) { + cvtValue[0] = 0; + } else { + if(strcmp(dbGetString(pdbentry),"LINEAR")!=0) { + cvtValue[0] = 0; + } else { + strcpy(cvtValue,"cvt("); + status = dbFindField(pdbentry,"EGUL"); + if(!status) strcat(cvtValue,dbGetString(pdbentry)); + status = dbFindField(pdbentry,"EGUF"); + if(!status) { + strcat(cvtValue,","); + strcat(cvtValue,dbGetString(pdbentry)); + } + strcat(cvtValue,")"); + } + } + fprintf(stream,"%-8s %-20s %-20s %-20s %-s\n", + busName,linkValue,dtypValue, + dbGetRecordName(pdbentry),cvtValue); + break; + } + status = dbNextRecord(pdbentry); + } + status = dbNextRecordType(pdbentry); + } + dbFinishEntry(pdbentry); + finishOutstream(stream); + return; +} diff --git a/src/dbStatic/dbStaticLib.h b/src/dbStatic/dbStaticLib.h new file mode 100644 index 000000000..9a406784a --- /dev/null +++ b/src/dbStatic/dbStaticLib.h @@ -0,0 +1,271 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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: Marty Kraimer + * Date: 06-08-93 + */ + +#ifndef INCdbStaticLibh +#define INCdbStaticLibh 1 + +#include +#include + +#include "shareLib.h" +#include "dbFldTypes.h" +#include "dbBase.h" +#include "link.h" +#include "errMdef.h" +#include "cantProceed.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*Field types as seen by static database access clients*/ +#define DCT_STRING 0 +#define DCT_INTEGER 1 +#define DCT_REAL 2 +#define DCT_MENU 3 +#define DCT_MENUFORM 4 +#define DCT_INLINK 5 +#define DCT_OUTLINK 6 +#define DCT_FWDLINK 7 +#define DCT_NOACCESS 8 + +/*Link types as seen by static database access clients*/ +#define DCT_LINK_CONSTANT 0 +#define DCT_LINK_FORM 1 +#define DCT_LINK_PV 2 + +typedef dbBase DBBASE; + +typedef struct{ + DBBASE *pdbbase; + dbRecordType *precordType; + dbFldDes *pflddes; + dbRecordNode *precnode; + dbInfoNode *pinfonode; + void *pfield; + char *message; + short indfield; + void *formpvt; +} DBENTRY; + +/*dbDumpFldDes is obsolete. It is only provided for compatibility*/ +#define dbDumpFldDes dbDumpField + +/* Static database access routines*/ +epicsShareFunc DBBASE * epicsShareAPI dbAllocBase(void); +epicsShareFunc void epicsShareAPI dbFreeBase(DBBASE *pdbbase); +epicsShareFunc DBENTRY * epicsShareAPI dbAllocEntry(DBBASE *pdbbase); +epicsShareFunc void epicsShareAPI dbFreeEntry(DBENTRY *pdbentry); +epicsShareFunc void epicsShareAPI dbInitEntry(DBBASE *pdbbase, + DBENTRY *pdbentry); +epicsShareFunc void epicsShareAPI dbFinishEntry(DBENTRY *pdbentry); +epicsShareFunc DBENTRY * epicsShareAPI dbCopyEntry(DBENTRY *pdbentry); +epicsShareFunc void epicsShareAPI dbCopyEntryContents(DBENTRY *pfrom, + DBENTRY *pto); + +epicsShareFunc long epicsShareAPI dbReadDatabase(DBBASE **ppdbbase, + const char *filename, const char *path, const char *substitutions); +epicsShareFunc long epicsShareAPI dbReadDatabaseFP(DBBASE **ppdbbase, + FILE *fp, const char *path, const char *substitutions); +epicsShareFunc long epicsShareAPI dbPath(DBBASE *pdbbase, const char *path); +epicsShareFunc long epicsShareAPI dbAddPath(DBBASE *pdbbase, const char *path); +epicsShareFunc long epicsShareAPI dbWriteRecord(DBBASE *ppdbbase, + const char *filename, const char *precordTypename, int level); +epicsShareFunc long epicsShareAPI dbWriteRecordFP(DBBASE *ppdbbase, + FILE *fp, const char *precordTypename, int level); +epicsShareFunc long epicsShareAPI dbWriteMenu(DBBASE *pdbbase, + const char *filename, const char *menuName); +epicsShareFunc long epicsShareAPI dbWriteMenuFP(DBBASE *pdbbase, + FILE *fp, const char *menuName); +epicsShareFunc long epicsShareAPI dbWriteRecordType(DBBASE *pdbbase, + const char *filename, const char *recordTypeName); +epicsShareFunc long epicsShareAPI dbWriteRecordTypeFP(DBBASE *pdbbase, + FILE *fp, const char *recordTypeName); +epicsShareFunc long epicsShareAPI dbWriteDevice(DBBASE *pdbbase, + const char *filename); +epicsShareFunc long epicsShareAPI dbWriteDeviceFP(DBBASE *pdbbase, FILE *fp); +epicsShareFunc long epicsShareAPI dbWriteDriver(DBBASE *pdbbase, + const char *filename); +epicsShareFunc long epicsShareAPI dbWriteDriverFP(DBBASE *pdbbase, FILE *fp); +epicsShareFunc long epicsShareAPI dbWriteRegistrarFP(DBBASE *pdbbase, FILE *fp); +epicsShareFunc long epicsShareAPI dbWriteFunctionFP(DBBASE *pdbbase, FILE *fp); +epicsShareFunc long epicsShareAPI dbWriteVariableFP(DBBASE *pdbbase, FILE *fp); +epicsShareFunc long epicsShareAPI dbWriteBreaktable(DBBASE *pdbbase, + const char *filename); +epicsShareFunc long epicsShareAPI dbWriteBreaktableFP(DBBASE *pdbbase, + FILE *fp); + +epicsShareFunc long epicsShareAPI dbFindRecordType(DBENTRY *pdbentry, + const char *recordTypename); +epicsShareFunc long epicsShareAPI dbFirstRecordType(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbNextRecordType(DBENTRY *pdbentry); +epicsShareFunc char * epicsShareAPI dbGetRecordTypeName(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbGetNRecordTypes(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbPutRecordAttribute(DBENTRY *pdbentry, + const char *name, const char*value); +epicsShareFunc long epicsShareAPI dbGetRecordAttribute(DBENTRY *pdbentry, + const char *name); +epicsShareFunc long epicsShareAPI dbGetAttributePart(DBENTRY *pdbentry, + const char **ppname); + +epicsShareFunc long epicsShareAPI dbFirstField(DBENTRY *pdbentry, int dctonly); +epicsShareFunc long epicsShareAPI dbNextField(DBENTRY *pdbentry, int dctonly); +epicsShareFunc int epicsShareAPI dbGetFieldType(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbGetNFields(DBENTRY *pdbentry, int dctonly); +epicsShareFunc char * epicsShareAPI dbGetFieldName(DBENTRY *pdbentry); +epicsShareFunc char * epicsShareAPI dbGetDefault(DBENTRY *pdbentry); +epicsShareFunc char * epicsShareAPI dbGetPrompt(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbGetPromptGroup(DBENTRY *pdbentry); + +epicsShareFunc long epicsShareAPI dbCreateRecord(DBENTRY *pdbentry, + const char *pname); +epicsShareFunc long epicsShareAPI dbDeleteRecord(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbFreeRecords(DBBASE *pdbbase); +epicsShareFunc long epicsShareAPI dbFindRecordPart(DBENTRY *pdbentry, + const char **ppname); +epicsShareFunc long epicsShareAPI dbFindRecord(DBENTRY *pdbentry, + const char *pname); + +epicsShareFunc long epicsShareAPI dbFirstRecord(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbNextRecord(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbGetNRecords(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbGetNAliases(DBENTRY *pdbentry); +epicsShareFunc char * epicsShareAPI dbGetRecordName(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbRenameRecord(DBENTRY *pdbentry, + const char *newName); +epicsShareFunc long epicsShareAPI dbCopyRecord(DBENTRY *pdbentry, + const char *newRecordName, int overWriteOK); + +epicsShareFunc long epicsShareAPI dbVisibleRecord(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbInvisibleRecord(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbIsVisibleRecord(DBENTRY *pdbentry); + +epicsShareFunc long epicsShareAPI dbCreateAlias(DBENTRY *pdbentry, + const char *paliasName); +epicsShareFunc int epicsShareAPI dbIsAlias(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbDeleteAliases(DBENTRY *pdbentry); + +epicsShareFunc long epicsShareAPI dbFindFieldPart(DBENTRY *pdbentry, + const char **ppname); +epicsShareFunc long epicsShareAPI dbFindField(DBENTRY *pdbentry, + const char *pfieldName); +epicsShareFunc int epicsShareAPI dbFoundField(DBENTRY *pdbentry); +epicsShareFunc char * epicsShareAPI dbGetString(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbPutString(DBENTRY *pdbentry, + const char *pstring); +epicsShareFunc char * epicsShareAPI dbVerify(DBENTRY *pdbentry, + const char *pstring); +epicsShareFunc char * epicsShareAPI dbGetRange(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbIsDefaultValue(DBENTRY *pdbentry); + +epicsShareFunc long epicsShareAPI dbFirstInfo(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbNextInfo(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbFindInfo(DBENTRY *pdbentry, + const char *name); +epicsShareFunc long epicsShareAPI dbDeleteInfo(DBENTRY *pdbentry); +epicsShareFunc const char * epicsShareAPI dbGetInfoName(DBENTRY *pdbentry); +epicsShareFunc const char * epicsShareAPI dbGetInfoString(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbPutInfoString(DBENTRY *pdbentry, + const char *string); +epicsShareFunc long epicsShareAPI dbPutInfoPointer(DBENTRY *pdbentry, + void *pointer); +epicsShareFunc void * epicsShareAPI dbGetInfoPointer(DBENTRY *pdbentry); +epicsShareFunc const char * epicsShareAPI dbGetInfo(DBENTRY *pdbentry, + const char *name); +epicsShareFunc long epicsShareAPI dbPutInfo(DBENTRY *pdbentry, + const char *name, const char *string); + +epicsShareFunc brkTable * epicsShareAPI dbFindBrkTable(DBBASE *pdbbase, + const char *name); + +epicsShareFunc dbMenu * epicsShareAPI dbFindMenu(DBBASE *pdbbase, + const char *name); +epicsShareFunc char ** epicsShareAPI dbGetMenuChoices(DBENTRY *pdbentry); +epicsShareFunc int epicsShareAPI dbGetMenuIndex(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbPutMenuIndex(DBENTRY *pdbentry, int index); +epicsShareFunc int epicsShareAPI dbGetNMenuChoices(DBENTRY *pdbentry); +epicsShareFunc char * epicsShareAPI dbGetMenuStringFromIndex(DBENTRY *pdbentry, + int index); +epicsShareFunc int epicsShareAPI dbGetMenuIndexFromString(DBENTRY *pdbentry, + const char *choice); + +epicsShareFunc drvSup * epicsShareAPI dbFindDriver(dbBase *pdbbase, + const char *name); + +epicsShareFunc int epicsShareAPI dbAllocForm(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbFreeForm(DBENTRY *pdbentry); +epicsShareFunc char ** epicsShareAPI dbGetFormPrompt(DBENTRY *pdbentry); +epicsShareFunc char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbPutForm(DBENTRY *pdbentry, char **value); +epicsShareFunc char ** epicsShareAPI dbVerifyForm(DBENTRY *pdbentry, + char **value); +epicsShareFunc char * epicsShareAPI dbGetRelatedField(DBENTRY *pdbentry); + +epicsShareFunc int epicsShareAPI dbGetNLinks(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbGetLinkField(DBENTRY *pdbentry, int index); +epicsShareFunc int epicsShareAPI dbGetLinkType(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbCvtLinkToConstant(DBENTRY *pdbentry); +epicsShareFunc long epicsShareAPI dbCvtLinkToPvlink(DBENTRY *pdbentry); + +/* Dump routines */ +epicsShareFunc void epicsShareAPI dbDumpPath(DBBASE *pdbbase); +epicsShareFunc void epicsShareAPI dbDumpRecord(DBBASE *pdbbase, + const char *precordTypename, int level); +epicsShareFunc void epicsShareAPI dbDumpMenu(DBBASE *pdbbase, + const char *menuName); +epicsShareFunc void epicsShareAPI dbDumpRecordType(DBBASE *pdbbase, + const char *recordTypeName); +epicsShareFunc void epicsShareAPI dbDumpField(DBBASE *pdbbase, + const char *recordTypeName, const char *fname); +epicsShareFunc void epicsShareAPI dbDumpDevice(DBBASE *pdbbase, + const char *recordTypeName); +epicsShareFunc void epicsShareAPI dbDumpDriver(DBBASE *pdbbase); +epicsShareFunc void epicsShareAPI dbDumpRegistrar(DBBASE *pdbbase); +epicsShareFunc void epicsShareAPI dbDumpFunction(DBBASE *pdbbase); +epicsShareFunc void epicsShareAPI dbDumpVariable(DBBASE *pdbbase); +epicsShareFunc void epicsShareAPI dbDumpBreaktable(DBBASE *pdbbase, + const char *name); +epicsShareFunc void epicsShareAPI dbPvdDump(DBBASE *pdbbase, int verbose); +epicsShareFunc void epicsShareAPI dbReportDeviceConfig(DBBASE *pdbbase, + FILE *report); + +/* Misc useful routines*/ +#define dbCalloc(nobj,size) callocMustSucceed(nobj,size,"dbCalloc") +#define dbMalloc(size) mallocMustSucceed(size,"dbMalloc") +epicsShareFunc void epicsShareAPI dbCatString(char **string, int *stringLength, + char *pnew, char *separator); + +extern int dbStaticDebug; + +#define S_dbLib_recordTypeNotFound (M_dbLib|1) /* Record Type does not exist */ +#define S_dbLib_recExists (M_dbLib|3) /* Record Already exists */ +#define S_dbLib_recNotFound (M_dbLib|5) /* Record Not Found */ +#define S_dbLib_flddesNotFound (M_dbLib|7) /* Field Description Not Found */ +#define S_dbLib_fieldNotFound (M_dbLib|9) /* Field Not Found */ +#define S_dbLib_badField (M_dbLib|11) /* Bad Field value */ +#define S_dbLib_menuNotFound (M_dbLib|13) /* Menu not found */ +#define S_dbLib_badLink (M_dbLib|15) /* Bad Link Field */ +#define S_dbLib_nameLength (M_dbLib|17) /* Record Name is too long */ +#define S_dbLib_noRecSup (M_dbLib|19) /* Record support not found */ +#define S_dbLib_strLen (M_dbLib|21) /* String is too long */ +#define S_dbLib_noSizeOffset (M_dbLib|23) /* Missing SizeOffset Routine - No record support? */ +#define S_dbLib_noForm (M_dbLib|25) /* dbAllocForm was not called */ +#define S_dbLib_outMem (M_dbLib|27) /* Out of memory */ +#define S_dbLib_infoNotFound (M_dbLib|29) /* Info item Not Found */ + +#ifdef __cplusplus +} +#endif + +#endif /*INCdbStaticLibh*/ diff --git a/src/dbStatic/dbStaticNoRun.c b/src/dbStatic/dbStaticNoRun.c new file mode 100644 index 000000000..33e3e452f --- /dev/null +++ b/src/dbStatic/dbStaticNoRun.c @@ -0,0 +1,338 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*dbStaticNoRun.c*/ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errMdef.h" + +#define epicsExportSharedSymbols +#include "dbFldTypes.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" + + +long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes; + void **papField; + int i; + char *pstr; + + if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); + if(!precnode) return(S_dbLib_recNotFound); + precnode->precord = dbCalloc(pdbRecordType->no_fields,sizeof(void *)); + papField = (void **)precnode->precord; + for(i=0; ino_fields; i++) { + pflddes = pdbRecordType->papFldDes[i]; + if(!pflddes) continue; + pdbentry->pflddes = pflddes; + switch(pflddes->field_type) { + case DBF_STRING: + if(pflddes->size <= 0) { + fprintf(stderr,"size=0 for %s.%s\n", + pdbRecordType->name,pflddes->name); + pflddes->size = 1; + } + papField[i] = dbCalloc(pflddes->size,sizeof(char)); + if(pflddes->initial) { + if(strlen(pflddes->initial) >= (unsigned)(pflddes->size)) { + fprintf(stderr,"initial size > size for %s.%s\n", + pdbRecordType->name,pflddes->name); + } else { + strcpy((char *)papField[i],pflddes->initial); + } + } + break; + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_USHORT: + case DBF_LONG: + case DBF_ULONG: + case DBF_FLOAT: + case DBF_DOUBLE: + case DBF_ENUM: + if(pflddes->initial) { + papField[i] = + dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); + strcpy((char *)papField[i],pflddes->initial); + } + break; + case DBF_MENU: + case DBF_DEVICE: {/*Must allow initial value that is choice or index*/ + long value; + char *endp; + char *pchoice = NULL; + + if(!pflddes->initial) break; + if(dbGetMenuIndexFromString(pdbentry,pflddes->initial)!=-1) { + pchoice=pflddes->initial; + } else { + value = strtol(pflddes->initial,&endp,0); + if(*endp=='\0') pchoice = + dbGetMenuStringFromIndex(pdbentry,(int)value); + } + if(!pchoice) { + epicsPrintf("%s.%s dbAllocRecord. Bad initial value\n", + precordName,pflddes->name); + break; + } + papField[i] = dbCalloc(strlen(pchoice)+1,sizeof(char)); + strcpy(papField[i],pchoice); + } + break; + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: { + struct link *plink; + + papField[i] = plink = dbCalloc(1,sizeof(struct link)); + plink->type = CONSTANT; + if(pflddes->initial) { + plink->value.constantStr = + dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); + strcpy(plink->value.constantStr,pflddes->initial); + } + } + break; + case DBF_NOACCESS: + break; + default: + fprintf(stderr,"dbAllocRecord: Illegal field type\n"); + } + } + pstr = (char *)papField[0]; + strcpy(pstr,precordName); + return(0); +} + +long dbFreeRecord(DBENTRY *pdbentry) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes = pdbentry->pflddes; + void **pap; + int i,field_type; + + if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); + if(!precnode) return(S_dbLib_recNotFound); + if(!precnode->precord) return(S_dbLib_recNotFound); + pap = (void **)precnode->precord; + precnode->precord = NULL; + for(i=0; ino_fields; i++) { + pflddes = pdbRecordType->papFldDes[i]; + field_type = pflddes->field_type; + if(field_type==DBF_INLINK + || field_type==DBF_OUTLINK + || field_type==DBF_FWDLINK) + dbFreeLinkContents((struct link *)pap[i]); + free(pap[i]); + } + free((void *)pap); + return(0); +} + +long dbGetFieldAddress(DBENTRY *pdbentry) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes = pdbentry->pflddes; + void **pap; + + if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); + if(!precnode) return(S_dbLib_recNotFound); + if(!pflddes) return(S_dbLib_flddesNotFound); + if(!precnode->precord) return(0); + pap = (void **)precnode->precord; + pdbentry->pfield = pap[pflddes->indRecordType]; + return(0); +} + +char *dbRecordName(DBENTRY *pdbentry) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + void **pap; + + if(!pdbRecordType) return(0); + if(!precnode) return(0); + if(!precnode->precord) return(0); + pap = (void **)precnode->precord; + return((char *)pap[0]); +} + +int dbIsMacroOk(DBENTRY *pdbentry) { return(TRUE);} + +epicsShareFunc int epicsShareAPI dbIsDefaultValue(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + + if(!pflddes) return(FALSE); + if(pflddes->field_type==DBF_DEVICE) return(FALSE); + if(!pfield) return(TRUE); + switch (pflddes->field_type) { + case DBF_STRING: + if(!pflddes->initial) + return((*(char *)pfield =='\0') ? TRUE : FALSE); + return(strcmp((char *)pfield,(char *)pflddes->initial)==0); + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_USHORT: + case DBF_LONG: + case DBF_ULONG: + case DBF_FLOAT: + case DBF_DOUBLE: + case DBF_ENUM: + if(!pflddes->initial) { + return((strlen((char *)pfield)==0)?TRUE:FALSE); + } + return(strcmp((char *)pfield,(char *)pflddes->initial)==0); + case DBF_MENU: { + long value; + char *endp; + char *pinitial = NULL; + + if(pflddes->initial) { + if(dbGetMenuIndexFromString(pdbentry,pflddes->initial)!=-1){ + pinitial=pflddes->initial; + } else { + value = strtol(pflddes->initial,&endp,0); + if(*endp=='\0') pinitial = + dbGetMenuStringFromIndex(pdbentry,(int)value); + } + } else { + pinitial = dbGetMenuStringFromIndex(pdbentry,0); + } + if(!pinitial) return(FALSE); + return(strcmp(pinitial,pfield)==0); + } + case DBF_DEVICE: /*Should never reach this state*/ + return(FALSE); + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: { + struct link *plink = (struct link *)pfield; + + if(!plink) return(FALSE); + if(plink->type!=CONSTANT) return(FALSE); + if(!plink->value.constantStr) return(TRUE); + if(!pflddes->initial) + return((strlen((char *)plink->value.constantStr)==0)?TRUE:FALSE); + if(strcmp(plink->value.constantStr,pflddes->initial)==0) + return(TRUE); + return(FALSE); + } + case DBF_NOACCESS: + return(TRUE); + } + return(FALSE); +} + +char *dbGetStringNum(DBENTRY *pdbentry) +{ + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + void **pap; + static char zero[] = "0"; + + if(!precnode) return(0); + if(!precnode->precord) return(0); + if(!pflddes) return(0); + if(!pfield) switch(pflddes->field_type) { + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_USHORT: + case DBF_LONG: + case DBF_ULONG: + case DBF_FLOAT: + case DBF_DOUBLE: + case DBF_ENUM: + return(zero); + case DBF_MENU: + case DBF_DEVICE: + return(dbGetMenuStringFromIndex(pdbentry,0)); + default: + epicsPrintf("dbGetStringNum. Illegal Field Type\n"); + return(NULL); + } + pap = (void **)precnode->precord; + return((char *)pap[pflddes->indRecordType]); +} + +long dbPutStringNum(DBENTRY *pdbentry,const char *pstring) +{ + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes = pdbentry->pflddes; + char *pfield = (char *)pdbentry->pfield; + void **pap; + + if(!precnode) return(S_dbLib_recNotFound); + if(!precnode->precord) return(S_dbLib_recNotFound); + if(!pflddes) return(S_dbLib_flddesNotFound); + if(pfield) { + if((unsigned)strlen(pfield) < (unsigned)strlen(pstring)) { + free((void *)pfield); + pfield = NULL; + } + } + if(!pfield) { + pfield = dbCalloc(strlen(pstring)+1,sizeof(char)); + strcpy(pfield,pstring); + pdbentry->pfield = pfield; + pap = (void **)precnode->precord; + pap[pflddes->indRecordType] = pfield; + } + strcpy(pfield,pstring); + return(0); +} + +epicsShareFunc int epicsShareAPI dbGetMenuIndex(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + int nChoices,choice; + char **menuChoices; + char *pfield; + + if(!pflddes) return(-1); + pfield = dbGetStringNum(pdbentry); + if(!pfield) return(-1); + nChoices = dbGetNMenuChoices(pdbentry); + menuChoices = dbGetMenuChoices(pdbentry); + if(nChoices<=0 || !menuChoices) return(-1); + for(choice=0; choice=nChoices) return(S_dbLib_badField); + dbPutStringNum(pdbentry,menuChoices[index]); + return(0); +} diff --git a/src/dbStatic/dbStaticPvt.h b/src/dbStatic/dbStaticPvt.h new file mode 100644 index 000000000..26c583b0f --- /dev/null +++ b/src/dbStatic/dbStaticPvt.h @@ -0,0 +1,63 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbStaticPvt.h */ +/* + * Author: Marty Kraimer + * Date: 06Jun95 + */ + +#ifndef INCdbStaticPvth +#define INCdbStaticPvth 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*Following are not intended for client code */ +dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry); +void dbFreeLinkContents(struct link *plink); +void dbFreePath(DBBASE *pdbbase); +int dbIsMacroOk(DBENTRY *pdbentry); + +/*The following routines have different versions for run-time no-run-time*/ +long dbAllocRecord(DBENTRY *pdbentry,const char *precordName); +long dbFreeRecord(DBENTRY *pdbentry); + +long dbGetFieldAddress(DBENTRY *pdbentry); +char *dbRecordName(DBENTRY *pdbentry); + +char *dbGetStringNum(DBENTRY *pdbentry); +long dbPutStringNum(DBENTRY *pdbentry,const char *pstring); + +/* The following is for path */ +typedef struct dbPathNode { + ELLNODE node; + char *directory; +} dbPathNode; + +/*The following are in dbPvdLib.c*/ +/*directory*/ +typedef struct{ + ELLNODE node; + dbRecordType *precordType; + dbRecordNode *precnode; +}PVDENTRY; +epicsShareFunc int dbPvdTableSize(int size); +extern int dbStaticDebug; +void dbPvdInitPvt(DBBASE *pdbbase); +PVDENTRY *dbPvdFind(DBBASE *pdbbase,const char *name,int lenname); +PVDENTRY *dbPvdAdd(DBBASE *pdbbase,dbRecordType *precordType,dbRecordNode *precnode); +void dbPvdDelete(DBBASE *pdbbase,dbRecordNode *precnode); +void dbPvdFreeMem(DBBASE *pdbbase); + +#ifdef __cplusplus +} +#endif +#endif /*INCdbStaticPvth*/ diff --git a/src/dbStatic/dbStaticRun.c b/src/dbStatic/dbStaticRun.c new file mode 100644 index 000000000..c15b698a4 --- /dev/null +++ b/src/dbStatic/dbStaticRun.c @@ -0,0 +1,695 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*dbStaticLibRun.c*/ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "errMdef.h" +#include "epicsPrint.h" +#include "ellLib.h" +#include "cvtFast.h" +#include "epicsTypes.h" +#include "epicsStdlib.h" + +#define epicsExportSharedSymbols +#include "dbBase.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "devSup.h" +#include "special.h" + + +static char hex_digit_to_ascii[16]={'0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f'}; + +static void ulongToHexString(epicsUInt32 source,char *pdest) +{ + epicsUInt32 val,temp; + char digit[10]; + int i,j; + + if(source==0) { + strcpy(pdest,"0x0"); + return; + } + *pdest++ = '0'; *pdest++ = 'x'; + val = source; + for(i=0; val!=0; i++) { + temp = val/16; + digit[i] = hex_digit_to_ascii[val - temp*16]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return; +} + +static double delta[2] = {1e-6, 1e-15}; +static int precision[2] = {6, 14}; +static void realToString(double value, char *preturn, int isdouble) +{ + double absvalue; + int logval,prec,end; + char tstr[30]; + char *ptstr = &tstr[0]; + int round; + int ise = FALSE; + char *loce = NULL; + + if (value == 0) { + strcpy(preturn, "0"); + return; + } + + absvalue = value < 0 ? -value : value; + if (absvalue < (double)INT_MAX) { + epicsInt32 intval = (epicsInt32) value; + double diff = value - intval; + + if (diff < 0) diff = -diff; + if (diff < absvalue * delta[isdouble]) { + cvtLongToString(intval, preturn); + return; + } + } + + /*Now starts the hard cases*/ + if (value < 0) { + *preturn++ = '-'; + value = -value; + } + + logval = (int)log10(value); + if (logval > 6 || logval < -2) { + int nout; + + ise = TRUE; + prec = precision[isdouble]; + nout = sprintf(ptstr, "%.*e", prec, value); + loce = strchr(ptstr, 'e'); + + if (!loce) { + ptstr[nout] = 0; + strcpy(preturn, ptstr); + return; + } + + *loce++ = 0; + } else { + prec = precision[isdouble] - logval; + if ( prec < 0) prec = 0; + sprintf(ptstr, "%.*f", prec, value); + } + + if (prec > 0) { + end = strlen(ptstr) - 1; + round = FALSE; + while (end > 0) { + if (tstr[end] == '.') {end--; break;} + if (tstr[end] == '0') {end--; continue;} + if (!round && end < precision[isdouble]) break; + if (!round && tstr[end] < '8') break; + if (tstr[end-1] == '.') { + if (round) end = end-2; + break; + } + if (tstr[end-1] != '9') break; + round = TRUE; + end--; + } + tstr[end+1] = 0; + while (round) { + if (tstr[end] < '9') {tstr[end]++; break;} + if (end == 0) { *preturn++ = '1'; tstr[end] = '0'; break;} + tstr[end--] = '0'; + } + } + strcpy(preturn, &tstr[0]); + if (ise) { + if (!(strchr(preturn, '.'))) strcat(preturn, ".0"); + strcat(preturn, "e"); + strcat(preturn, loce); + } +} + +static void floatToString(float value,char *preturn) +{ + realToString((double)value,preturn,0); + return; +} + +static void doubleToString(double value,char *preturn) +{ + realToString(value,preturn,1); + return; +} + + +static long do_nothing(struct dbCommon *precord) { return 0; } + +/* Dummy DSXT used for soft device supports */ +struct dsxt devSoft_DSXT = { + do_nothing, + do_nothing +}; + +static devSup *pthisDevSup = NULL; + +void dbInitDevSup(devSup *pdevSup, dset *pdset) +{ + pdevSup->pdset = pdset; + if (pdevSup->link_type == CONSTANT) + pdevSup->pdsxt = &devSoft_DSXT; + + if (pdset->init) { + pthisDevSup = pdevSup; + pdset->init(0); + pthisDevSup = NULL; + } +} + +void devExtend(dsxt *pdsxt) +{ + if (!pthisDevSup) + errlogPrintf("devExtend() called outside of dbInitDevSup()\n"); + else { + pthisDevSup->pdsxt = pdsxt; + } +} + +long dbAllocRecord(DBENTRY *pdbentry,const char *precordName) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes; + int i; + char *precord; + char *pfield; + + if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); + if(!precnode) return(S_dbLib_recNotFound); + if(pdbRecordType->rec_size == 0) { + printf("\t*** Did you run x_RegisterRecordDeviceDriver(pdbbase) yet? ***\n"); + epicsPrintf("dbAllocRecord(%s) with %s rec_size = 0\n", + precordName, pdbRecordType->name); + return(S_dbLib_noRecSup); + } + precnode->precord = dbCalloc(1,pdbRecordType->rec_size); + precord = (char *)precnode->precord; + pflddes = pdbRecordType->papFldDes[0]; + if(!pflddes) { + epicsPrintf("dbAllocRecord pflddes for NAME not found\n"); + return(S_dbLib_flddesNotFound); + } + if(strlen(precordName)>=pflddes->size) { + epicsPrintf("dbAllocRecord: NAME(%s) too long\n",precordName); + return(S_dbLib_nameLength); + } + pfield = precord + pflddes->offset; + strcpy(pfield,precordName); + for(i=1; ino_fields; i++) { + + pflddes = pdbRecordType->papFldDes[i]; + if(!pflddes) continue; + pfield = precord + pflddes->offset; + pdbentry->pfield = (void *)pfield; + pdbentry->pflddes = pflddes; + pdbentry->indfield = i; + switch(pflddes->field_type) { + case DBF_STRING: + if(pflddes->initial) { + if(strlen(pflddes->initial) >= pflddes->size) { + epicsPrintf("initial size > size for %s.%s\n", + pdbRecordType->name,pflddes->name); + } else { + strcpy(pfield,pflddes->initial); + } + } + break; + case DBF_CHAR: + case DBF_UCHAR: + case DBF_SHORT: + case DBF_USHORT: + case DBF_LONG: + case DBF_ULONG: + case DBF_FLOAT: + case DBF_DOUBLE: + case DBF_ENUM: + case DBF_MENU: + if(pflddes->initial) { + long status; + + status = dbPutStringNum(pdbentry,pflddes->initial); + if(status) + epicsPrintf("Error initializing %s.%s initial %s\n", + pdbRecordType->name,pflddes->name,pflddes->initial); + } + break; + case DBF_DEVICE: + if(!pflddes->ftPvt) dbGetDeviceMenu(pdbentry); + break; + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: { + DBLINK *plink = (DBLINK *)pfield; + + plink->type = CONSTANT; + if(pflddes->initial) { + plink->value.constantStr = + dbCalloc(strlen(pflddes->initial)+1,sizeof(char)); + strcpy(plink->value.constantStr,pflddes->initial); + } + } + break; + case DBF_NOACCESS: + break; + default: + epicsPrintf("dbAllocRecord: Illegal field type\n"); + } + } + return(0); +} + +long dbFreeRecord(DBENTRY *pdbentry) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + + if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); + if(!precnode) return(S_dbLib_recNotFound); + if(!precnode->precord) return(S_dbLib_recNotFound); + free(precnode->precord); + precnode->precord = NULL; + return(0); +} + +long dbGetFieldAddress(DBENTRY *pdbentry) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes = pdbentry->pflddes; + + if(!pdbRecordType) return(S_dbLib_recordTypeNotFound); + if(!precnode) return(S_dbLib_recNotFound); + if(!pflddes) return(S_dbLib_flddesNotFound); + if(!precnode->precord) return(0); + pdbentry->pfield = ((char *)precnode->precord) + pflddes->offset; + return(0); +} + +char *dbRecordName(DBENTRY *pdbentry) +{ + dbRecordType *pdbRecordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbFldDes *pflddes; + char *precord; + + if(!pdbRecordType) return(0); + if(!precnode) return(0); + if(!precnode->precord) return(0); + precord = (char *)precnode->precord; + pflddes = pdbRecordType->papFldDes[0]; + if(!pflddes) return(NULL); + return(precord + pflddes->offset); +} + +int dbIsMacroOk(DBENTRY *pdbentry) { return(FALSE); } + +epicsShareFunc int epicsShareAPI dbIsDefaultValue(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + + if(!pflddes) return(FALSE); + if(!pfield) return(FALSE); + switch (pflddes->field_type) { + case (DBF_STRING) : { + char *p = (char *)pfield; + + if(!pflddes->initial) return((strlen(p)==0) ? TRUE : FALSE); + return((strcmp(pflddes->initial,p)==0) ? TRUE : FALSE); + } + case DBF_CHAR: { + char field = *(char *)pfield; + long ltemp; + if(pflddes->initial) { + ltemp = strtol(pflddes->initial,NULL,0); + return((field==ltemp)); + } + return((field==0)); + } + case DBF_UCHAR: { + unsigned char field = *(unsigned char *)pfield; + unsigned long ltemp; + + if(pflddes->initial) { + ltemp = strtoul(pflddes->initial,NULL,0); + return((field==ltemp)); + } + return((field==0)); + } + case DBF_SHORT: { + short field = *(short *)pfield; + long ltemp; + + if(pflddes->initial) { + ltemp = strtol(pflddes->initial,NULL,0); + return((field==ltemp)); + } + return((field==0)); + } + case DBF_USHORT: { + unsigned short field = *(unsigned short *)pfield; + unsigned long ltemp; + + if(pflddes->initial) { + ltemp = strtoul(pflddes->initial,NULL,0); + return((field==ltemp)); + } + return((field==0)); + } + case DBF_LONG: { + long field = *(long *)pfield; + long ltemp; + + if(pflddes->initial) { + ltemp = strtol(pflddes->initial,NULL,0); + return((field==ltemp)); + } + return((field==0)); + } + case DBF_ULONG: { + unsigned long field = *(unsigned long *)pfield; + unsigned long ltemp; + + if(pflddes->initial) { + ltemp = strtoul(pflddes->initial,NULL,0); + return((field==ltemp)); + } + return((field==0)); + } + case DBF_FLOAT: { + float field = *(float *)pfield; + double dtemp; + + if(pflddes->initial) { + dtemp = epicsStrtod(pflddes->initial,NULL); + return((field==dtemp)); + } + return((field==0.0)); + } + case DBF_DOUBLE: { + double field = *(double *)pfield; + double dtemp; + + if(pflddes->initial) { + dtemp = epicsStrtod(pflddes->initial,NULL); + return((field==dtemp)); + } + return((field==0.0)); + } + case DBF_ENUM: { + unsigned short field = *(unsigned short *)pfield; + unsigned long ltemp; + + if(pflddes->initial) { + ltemp = strtoul(pflddes->initial,NULL,0); + return((field==ltemp)); + } + return((field==0)); + } + case DBF_MENU: { + unsigned short field = *(unsigned short *)pfield; + long value; + char *endp; + + if(pflddes->initial) { + value = dbGetMenuIndexFromString(pdbentry,pflddes->initial); + if(value==-1) { + value = strtol(pflddes->initial,&endp,0); + if(*endp!='\0') return(FALSE); + } + } else { + value = 0; + } + if((unsigned short)value == field) return(TRUE); + return(FALSE); + } + case DBF_DEVICE: { + dbRecordType *precordType = pdbentry->precordType; + + if(!precordType) { + epicsPrintf("dbIsDefaultValue: pdbRecordType is NULL??\n"); + return(FALSE); + } + if(ellCount(&precordType->devList)==0) return(TRUE); + return(FALSE); + } + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: { + struct link *plink = (struct link *)pfield; + + if(!plink) return(FALSE); + if(plink->type!=CONSTANT) return(FALSE); + if(plink->value.constantStr == 0) return(TRUE); + if(!pflddes->initial) return(FALSE); + if(strcmp(plink->value.constantStr,pflddes->initial)==0) + return(TRUE); + return(FALSE); + } + default: + return(TRUE); + } + return(FALSE); +} + +char *dbGetStringNum(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + char *message; + unsigned char cvttype; + + if(!pdbentry->message) pdbentry->message = dbCalloc(1,50); + message = pdbentry->message; + cvttype = pflddes->base; + switch (pflddes->field_type) { + case DBF_CHAR: + if(cvttype==CT_DECIMAL) + cvtCharToString(*(char*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(char*)pfield),message); + break; + case DBF_UCHAR: + if(cvttype==CT_DECIMAL) + cvtUcharToString(*(unsigned char*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(unsigned char*)pfield),message); + break; + case DBF_SHORT: + if(cvttype==CT_DECIMAL) + cvtShortToString(*(short*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(short*)pfield),message); + break; + case DBF_USHORT: + case DBF_ENUM: + if(cvttype==CT_DECIMAL) + cvtUshortToString(*(unsigned short*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(unsigned short*)pfield),message); + break; + case DBF_LONG: + if(cvttype==CT_DECIMAL) + cvtLongToString(*(epicsInt32*)pfield, message); + else + ulongToHexString((epicsUInt32)(*(epicsInt32*)pfield), message); + break; + case DBF_ULONG: + if(cvttype==CT_DECIMAL) + cvtUlongToString(*(epicsUInt32 *)pfield, message); + else + ulongToHexString(*(epicsUInt32*)pfield, message); + break; + case DBF_FLOAT: + floatToString(*(float *)pfield,message); + break; + case DBF_DOUBLE: + doubleToString(*(double *)pfield,message); + break; + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + short choice_ind; + char *pchoice; + + if(!pfield) {strcpy(message,"Field not found"); return(message);} + choice_ind = *((short *) pdbentry->pfield); + if(!pdbMenu || choice_ind<0 || choice_ind>=pdbMenu->nChoice) + return(NULL); + pchoice = pdbMenu->papChoiceValue[choice_ind]; + strcpy(message, pchoice); + } + break; + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + char *pchoice; + short choice_ind; + + if(!pfield) {strcpy(message,"Field not found"); return(message);} + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(NULL); + choice_ind = *((short *) pdbentry->pfield); + if(choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice) + return(NULL); + pchoice = pdbDeviceMenu->papChoice[choice_ind]; + strcpy(message, pchoice); + } + break; + default: + return(NULL); + } + return (message); +} + +long dbPutStringNum(DBENTRY *pdbentry,const char *pstring) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + long status=0; + + if(!pfield) return(S_dbLib_fieldNotFound); + switch (pflddes->field_type) { + case DBF_CHAR : + case DBF_SHORT : + case DBF_LONG:{ + long value; + char *endp; + + value = strtol(pstring,&endp,0); + if(*endp!=0) status = S_dbLib_badField; + switch (pflddes->field_type) { + case DBF_CHAR : *(char *)pfield = value; break; + case DBF_SHORT : *(short *)pfield = value; break; + case DBF_LONG : *(epicsInt32 *)pfield = value; break; + default: epicsPrintf("Logic error in dbPutStringNum\n"); + } + } + break; + case DBF_UCHAR: + case DBF_USHORT: + case DBF_ULONG: + case DBF_ENUM:{ + unsigned long value; + char *endp; + + value = strtoul(pstring,&endp,0); + if(*endp!=0) status = S_dbLib_badField; + switch (pflddes->field_type) { + case DBF_UCHAR : *(unsigned char *)pfield = value; break; + case DBF_USHORT: + case DBF_ENUM: *(unsigned short *)pfield=value; break; + case DBF_ULONG : *(epicsUInt32 *)pfield = value; break; + default: epicsPrintf("Logic error in dbPutStringNum\n"); + } + } + break; + case DBF_FLOAT: + case DBF_DOUBLE: { + double value; + char *endp; + + value = epicsStrtod(pstring,&endp); + if(*endp!=0) status = S_dbLib_badField; + if(pflddes->field_type == DBF_FLOAT) + *(float *)pfield = (float)value; + else + *(double *)pfield = value; + } + break; + case DBF_MENU: + case DBF_DEVICE: {/*Must allow value that is choice or index*/ + unsigned short *field= (unsigned short*)pfield; + int ind; + long value; + char *endp; + + ind = dbGetMenuIndexFromString(pdbentry,pstring); + if(ind==-1) { + value = strtol(pstring,&endp,0); + if(*endp!='\0') return(S_dbLib_badField); + ind = value; + /*Check that ind is withing range*/ + if(!dbGetMenuStringFromIndex(pdbentry,ind)) + return(S_dbLib_badField); + } + *field = (unsigned short)ind; + } + return (0); + default: + return (S_dbLib_badField); + } + return(status); +} + +epicsShareFunc int epicsShareAPI dbGetMenuIndex(DBENTRY *pdbentry) +{ + dbFldDes *pflddes = pdbentry->pflddes; + void *pfield = pdbentry->pfield; + + if(!pflddes) return(-1); + if(!pfield) return(-1); + switch (pflddes->field_type) { + case (DBF_MENU): + case (DBF_DEVICE): + return((int)(*(unsigned short *)pfield)); + default: + errPrintf(-1,__FILE__, __LINE__,"Logic Error\n"); + } + return(-1); +} + +epicsShareFunc long epicsShareAPI dbPutMenuIndex(DBENTRY *pdbentry,int index) +{ + dbFldDes *pflddes = pdbentry->pflddes; + unsigned short *pfield = pdbentry->pfield; + + if(!pflddes) return(S_dbLib_flddesNotFound); + if(!pfield) return(S_dbLib_fieldNotFound); + switch (pflddes->field_type) { + case DBF_MENU: { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + + if(!pdbMenu) return(S_dbLib_menuNotFound); + if(index<0 || index>=pdbMenu->nChoice) return(S_dbLib_badField); + *pfield = (unsigned short)index; + return(0); + } + case DBF_DEVICE: { + dbDeviceMenu *pdbDeviceMenu; + + pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + if(!pdbDeviceMenu) return(S_dbLib_menuNotFound); + if(index<0 || index>=pdbDeviceMenu->nChoice) + return(S_dbLib_badField); + return(dbPutString(pdbentry,pdbDeviceMenu->papChoice[index])); + } + default: + break; + } + return (S_dbLib_badField); +} diff --git a/src/dbStatic/dbToMenuH.c b/src/dbStatic/dbToMenuH.c new file mode 100644 index 000000000..e910d1a78 --- /dev/null +++ b/src/dbStatic/dbToMenuH.c @@ -0,0 +1,124 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbToMenu.c */ +/* Author: Marty Kraimer Date: 11Sep95 */ +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errMdef.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "dbBase.h" +#include "gpHash.h" +#include "osiFileName.h" + +DBBASE *pdbbase = NULL; + +int main(int argc,char **argv) +{ + dbMenu *pdbMenu; + char *outFilename; + char *pext; + FILE *outFile; + char *plastSlash; + int i; + int strip; + char *path = NULL; + char *sub = NULL; + int pathLength = 0; + int subLength = 0; + char **pstr; + char *psep; + int *len; + long status; + static char *pathSep = OSI_PATH_LIST_SEPARATOR; + static char *subSep = ","; + + /*Look for options*/ + if(argc<2) { + fprintf(stderr,"usage: dbToMenu -Idir -Idir file.dbd [outfile]\n"); + exit(0); + } + while((strncmp(argv[1],"-I",2)==0)||(strncmp(argv[1],"-S",2)==0)) { + if(strncmp(argv[1],"-I",2)==0) { + pstr = &path; + psep = pathSep; + len = &pathLength; + } else { + pstr = ⊂ + psep = subSep; + len = &subLength; + } + if(strlen(argv[1])==2) { + dbCatString(pstr,len,argv[2],psep); + strip = 2; + } else { + dbCatString(pstr,len,argv[1]+2,psep); + strip = 1; + } + argc -= strip; + for(i=1; iignoreMissingMenus = TRUE; + status = dbReadDatabase(&pdbbase,argv[1],path,sub); + if (status) { + errlogFlush(); + fprintf(stderr, "dbToMenuH: Input errors, no output generated\n"); + exit(1); + } + outFile = fopen(outFilename, "w"); + if (!outFile) { + epicsPrintf("Error creating output file \"%s\"\n", outFilename); + exit(1); + } + pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList); + while(pdbMenu) { + fprintf(outFile,"#ifndef INC%sH\n",pdbMenu->name); + fprintf(outFile,"#define INC%sH\n",pdbMenu->name); + fprintf(outFile,"typedef enum {\n"); + for(i=0; inChoice; i++) { + fprintf(outFile,"\t%s",pdbMenu->papChoiceName[i]); + if(i < (pdbMenu->nChoice - 1)) fprintf(outFile,","); + fprintf(outFile,"\n"); + } + fprintf(outFile,"}%s;\n",pdbMenu->name); + fprintf(outFile,"#endif /*INC%sH*/\n",pdbMenu->name); + pdbMenu = (dbMenu *)ellNext(&pdbMenu->node); + } + fclose(outFile); + free((void *)outFilename); + return(0); +} diff --git a/src/dbStatic/dbToRecordtypeH.c b/src/dbStatic/dbToRecordtypeH.c new file mode 100644 index 000000000..a97a44f87 --- /dev/null +++ b/src/dbStatic/dbToRecordtypeH.c @@ -0,0 +1,267 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* dbToRecordtypeH.c */ +/* Author: Marty Kraimer Date: 11Sep95 */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "errMdef.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "dbBase.h" +#include "gpHash.h" +#include "osiFileName.h" + +DBBASE *pdbbase = NULL; + +int main(int argc,char **argv) +{ + int i; + char *outFilename; + char *pext; + FILE *outFile; + dbMenu *pdbMenu; + dbRecordType *pdbRecordType; + dbFldDes *pdbFldDes; + dbText *pdbCdef; + int isdbCommonRecord = FALSE; + char *plastSlash; + int strip; + char *path = NULL; + char *sub = NULL; + int pathLength = 0; + int subLength = 0; + char **pstr; + char *psep; + int *len; + long status; + static char *pathSep = OSI_PATH_LIST_SEPARATOR; + static char *subSep = ","; + + /*Look for options*/ + if(argc<2) { + fprintf(stderr,"usage: dbToRecordtypeH -Idir -Idir file.dbd [outfile]\n"); + exit(0); + } + while((strncmp(argv[1],"-I",2)==0)||(strncmp(argv[1],"-S",2)==0)) { + if(strncmp(argv[1],"-I",2)==0) { + pstr = &path; + psep = pathSep; + len = &pathLength; + } else { + pstr = ⊂ + psep = subSep; + len = &subLength; + } + if(strlen(argv[1])==2) { + dbCatString(pstr,len,argv[2],psep); + strip = 2; + } else { + dbCatString(pstr,len,argv[1]+2,psep); + strip = 1; + } + argc -= strip; + for(i=1; iignoreMissingMenus = TRUE; + pdbbase->loadCdefs = TRUE; + status = dbReadDatabase(&pdbbase,argv[1],path,sub); + if(status) { + errlogFlush(); + fprintf(stderr, "dbToMenuH: Input errors, no output generated\n"); + exit(1); + } + outFile = fopen(outFilename,"w"); + if(!outFile) { + epicsPrintf("Error creating output file \"%s\"\n", outFilename); + exit(1); + } + + pdbMenu = (dbMenu *)ellFirst(&pdbbase->menuList); + while(pdbMenu) { + fprintf(outFile,"\n#ifndef INC%sH\n",pdbMenu->name); + fprintf(outFile,"#define INC%sH\n",pdbMenu->name); + fprintf(outFile,"typedef enum {\n"); + for(i=0; inChoice; i++) { + fprintf(outFile,"\t%s",pdbMenu->papChoiceName[i]); + if(i < (pdbMenu->nChoice - 1)) fprintf(outFile,","); + fprintf(outFile,"\n"); + } + fprintf(outFile,"}%s;\n",pdbMenu->name); + fprintf(outFile,"#endif /*INC%sH*/\n",pdbMenu->name); + pdbMenu = (dbMenu *)ellNext(&pdbMenu->node); + } + pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + while(pdbRecordType) { + fprintf(outFile,"#ifndef INC%sH\n",pdbRecordType->name); + fprintf(outFile,"#define INC%sH\n",pdbRecordType->name); + pdbCdef = (dbText *)ellFirst(&pdbRecordType->cdefList); + while (pdbCdef) { + fprintf(outFile,"%s\n",pdbCdef->text); + pdbCdef = (dbText *)ellNext(&pdbCdef->node); + } + fprintf(outFile,"typedef struct %s",pdbRecordType->name); + if(!isdbCommonRecord) fprintf(outFile,"Record"); + fprintf(outFile," {\n"); + for(i=0; ino_fields; i++) { + char name[256]; + int j; + + pdbFldDes = pdbRecordType->papFldDes[i]; + for(j=0; j< (int)strlen(pdbFldDes->name); j++) + name[j] = tolower(pdbFldDes->name[j]); + name[strlen(pdbFldDes->name)] = 0; + switch(pdbFldDes->field_type) { + case DBF_STRING : + fprintf(outFile, "\tchar\t\t%s[%d];\t/* %s */\n", + name, pdbFldDes->size, pdbFldDes->prompt); + break; + case DBF_CHAR : + fprintf(outFile, "\tepicsInt8\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_UCHAR : + fprintf(outFile, "\tepicsUInt8\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_SHORT : + fprintf(outFile, "\tepicsInt16\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_USHORT : + fprintf(outFile, "\tepicsUInt16\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_LONG : + fprintf(outFile, "\tepicsInt32\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_ULONG : + fprintf(outFile, "\tepicsUInt32\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_FLOAT : + fprintf(outFile, "\tepicsFloat32\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_DOUBLE : + fprintf(outFile, "\tepicsFloat64\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_ENUM : + case DBF_MENU : + case DBF_DEVICE : + fprintf(outFile, "\tepicsEnum16\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_INLINK : + case DBF_OUTLINK : + case DBF_FWDLINK : + fprintf(outFile, "\tDBLINK\t\t%s;\t/* %s */\n", + name, pdbFldDes->prompt); + break; + case DBF_NOACCESS: + fprintf(outFile, "\t%s;\t/* %s */\n", + pdbFldDes->extra, pdbFldDes->prompt); + break; + default: + fprintf(outFile,"ILLEGAL FIELD TYPE\n"); + } + } + fprintf(outFile,"} %s",pdbRecordType->name); + if(!isdbCommonRecord) fprintf(outFile,"Record"); + fprintf(outFile,";\n"); + if(!isdbCommonRecord) { + for(i=0; ino_fields; i++) { + pdbFldDes = pdbRecordType->papFldDes[i]; + fprintf(outFile,"#define %sRecord%s\t%d\n", + pdbRecordType->name,pdbFldDes->name,pdbFldDes->indRecordType); + } + } + fprintf(outFile,"#endif /*INC%sH*/\n",pdbRecordType->name); + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node); + if(pdbRecordType) fprintf(outFile,"\n"); + } + if(!isdbCommonRecord) { + fprintf(outFile,"#ifdef GEN_SIZE_OFFSET\n"); + fprintf(outFile,"#ifdef __cplusplus\n"); + fprintf(outFile,"extern \"C\" {\n"); + fprintf(outFile,"#endif\n"); + fprintf(outFile,"#include \n"); + pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + while(pdbRecordType) { + fprintf(outFile,"static int %sRecordSizeOffset(dbRecordType *pdbRecordType)\n{\n", + pdbRecordType->name); + fprintf(outFile," %sRecord *prec = 0;\n",pdbRecordType->name); + for(i=0; ino_fields; i++) { + char name[256]; + int j; + + pdbFldDes = pdbRecordType->papFldDes[i]; + for(j=0; j< (int)strlen(pdbFldDes->name); j++) + name[j] = tolower(pdbFldDes->name[j]); + name[strlen(pdbFldDes->name)] = 0; + fprintf(outFile, + " pdbRecordType->papFldDes[%d]->size=sizeof(prec->%s);\n", + i,name); + fprintf(outFile," pdbRecordType->papFldDes[%d]->offset=",i); + fprintf(outFile, + "(short)((char *)&prec->%s - (char *)prec);\n",name); + } + fprintf(outFile," pdbRecordType->rec_size = sizeof(*prec);\n"); + fprintf(outFile," return(0);\n"); + fprintf(outFile,"}\n"); + fprintf(outFile,"epicsExportRegistrar(%sRecordSizeOffset);\n", + pdbRecordType->name); + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node); + } + fprintf(outFile,"#ifdef __cplusplus\n"); + fprintf(outFile,"}\n"); + fprintf(outFile,"#endif\n"); + fprintf(outFile,"#endif /*GEN_SIZE_OFFSET*/\n"); + } + fclose(outFile); + free((void *)outFilename); + return(0); +} diff --git a/src/dbStatic/dbYacc.y b/src/dbStatic/dbYacc.y new file mode 100644 index 000000000..b61ab26ea --- /dev/null +++ b/src/dbStatic/dbYacc.y @@ -0,0 +1,285 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +%{ +static int yyerror(); +static int yy_start; +static long pvt_yy_parse(void); +static int yyFailed = 0; +static int yyAbort = 0; +#include "dbLexRoutines.c" +%} + +%start database + +%token tokenINCLUDE tokenPATH tokenADDPATH +%token tokenALIAS tokenMENU tokenCHOICE tokenRECORDTYPE +%token tokenFIELD tokenINFO tokenREGISTRAR +%token tokenDEVICE tokenDRIVER tokenBREAKTABLE +%token tokenRECORD tokenGRECORD tokenVARIABLE tokenFUNCTION +%token tokenSTRING tokenCDEFS + +%union +{ + char *Str; +} + +%% + +database: database database_item | database_item; + +database_item: include + | path + | addpath + | tokenMENU menu_head menu_body + | tokenRECORDTYPE recordtype_head recordtype_body + | device + | driver + | registrar + | function + | variable + | tokenBREAKTABLE break_head break_body + | tokenRECORD record_head record_body + | tokenGRECORD grecord_head record_body + | alias + ; + +include: tokenINCLUDE tokenSTRING +{ + if(dbStaticDebug>2) printf("include : %s\n",$2); + dbIncludeNew($2); dbmfFree($2); +}; + +path: tokenPATH tokenSTRING +{ + if(dbStaticDebug>2) printf("path : %s\n",$2); + dbPathCmd($2); dbmfFree($2); +}; + +addpath: tokenADDPATH tokenSTRING +{ + if(dbStaticDebug>2) printf("addpath : %s\n",$2); + dbAddPathCmd($2); dbmfFree($2); +}; + +menu_head: '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("menu_head %s\n",$2); + dbMenuHead($2); dbmfFree($2); +}; + +menu_body: '{' choice_list '}' +{ + if(dbStaticDebug>2) printf("menu_body\n"); + dbMenuBody(); +}; + +choice_list: choice_list choice | choice; + +choice: tokenCHOICE '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("choice %s %s\n",$3,$5); + dbMenuChoice($3,$5); dbmfFree($3); dbmfFree($5); +} + | include; + +recordtype_head: '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("recordtype_head %s\n",$2); + dbRecordtypeHead($2); dbmfFree($2); +}; + +recordtype_body: '{' recordtype_field_list '}' +{ + if(dbStaticDebug>2) printf("recordtype_body\n"); + dbRecordtypeBody(); +}; + +recordtype_field_list: recordtype_field_list recordtype_field + | recordtype_field; + +recordtype_field: tokenFIELD recordtype_field_head recordtype_field_body + | tokenCDEFS +{ + if(dbStaticDebug>2) printf("recordtype_cdef %s", $1); + dbRecordtypeCdef($1); +} + | include ; + +recordtype_field_head: '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("recordtype_field_head %s %s\n",$2,$4); + dbRecordtypeFieldHead($2,$4); dbmfFree($2); dbmfFree($4); +}; + +recordtype_field_body: '{' recordtype_field_item_list '}' ; + +recordtype_field_item_list: recordtype_field_item_list recordtype_field_item + | recordtype_field_item; + +recordtype_field_item: tokenSTRING '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("recordtype_field_item %s %s\n",$1,$3); + dbRecordtypeFieldItem($1,$3); dbmfFree($1); dbmfFree($3); +} + | tokenMENU '(' tokenSTRING ')' +{ + + if(dbStaticDebug>2) printf("recordtype_field_item %s (%s)\n","menu",$3); + dbRecordtypeFieldItem("menu",$3); dbmfFree($3); +}; + + +device: tokenDEVICE '(' + tokenSTRING ',' tokenSTRING ',' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("device %s %s %s %s\n",$3,$5,$7,$9); + dbDevice($3,$5,$7,$9); + dbmfFree($3); dbmfFree($5); + dbmfFree($7); dbmfFree($9); +}; + + +driver: tokenDRIVER '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("driver %s\n",$3); + dbDriver($3); dbmfFree($3); +}; + +registrar: tokenREGISTRAR '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("registrar %s\n",$3); + dbRegistrar($3); dbmfFree($3); +}; + +function: tokenFUNCTION '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("function %s\n",$3); + dbFunction($3); dbmfFree($3); +}; + +variable: tokenVARIABLE '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("variable %s\n",$3); + dbVariable($3,"int"); dbmfFree($3); +} + | tokenVARIABLE '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("variable %s, %s\n",$3,$5); + dbVariable($3,$5); dbmfFree($3); dbmfFree($5); +}; + +break_head: '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("break_head %s\n",$2); + dbBreakHead($2); dbmfFree($2); +}; + +break_body : '{' break_list '}' +{ + if(dbStaticDebug>2) printf("break_body\n"); + dbBreakBody(); +}; + +break_list: break_list ',' break_item + | break_list break_item + | break_item; + +break_item: tokenSTRING +{ + if(dbStaticDebug>2) printf("break_item tokenSTRING %s\n",$1); + dbBreakItem($1); dbmfFree($1); +}; + + +grecord_head: '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("grecord_head %s %s\n",$2,$4); + dbRecordHead($2,$4,1); dbmfFree($2); dbmfFree($4); +}; + +record_head: '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("record_head %s %s\n",$2,$4); + dbRecordHead($2,$4,0); dbmfFree($2); dbmfFree($4); +}; + +record_body: /*Null*/ +{ + if(dbStaticDebug>2) printf("null record_body\n"); + dbRecordBody(); +} + | '{' '}' +{ + if(dbStaticDebug>2) printf("record_body - no fields\n"); + dbRecordBody(); +} + | '{' record_field_list '}' +{ + if(dbStaticDebug>2) printf("record_body\n"); + dbRecordBody(); +}; + +record_field_list: record_field_list record_field + | record_field; + +record_field: tokenFIELD '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$5); + dbRecordField($3,$5); dbmfFree($3); dbmfFree($5); +} + | tokenINFO '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$5); + dbRecordInfo($3,$5); dbmfFree($3); dbmfFree($5); +} + | tokenALIAS '(' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("record_alias %s\n",$3); + dbRecordAlias($3); dbmfFree($3); +} + | include ; + +alias: tokenALIAS '(' tokenSTRING ',' tokenSTRING ')' +{ + if(dbStaticDebug>2) printf("alias %s %s\n",$3,$5); + dbAlias($3,$5); dbmfFree($3); dbmfFree($5); +}; + + +%% + +#include "dbLex.c" + + +static int yyerror(char *str) +{ + if (str) + epicsPrintf("Error: %s\n ", str); + else + epicsPrintf("Error"); + epicsPrintf(" at or before \"%s\"", yytext); + dbIncludePrint(); + yyFailed = TRUE; + return(0); +} +static long pvt_yy_parse(void) +{ + static int FirstFlag = 1; + long rtnval; + + if (!FirstFlag) { + yyAbort = FALSE; + yyFailed = FALSE; + yyreset(); + yyrestart(NULL); + } + FirstFlag = 0; + rtnval = yyparse(); + if(rtnval!=0 || yyFailed) return(-1); else return(0); +} diff --git a/src/dbStatic/devSup.h b/src/dbStatic/devSup.h new file mode 100644 index 000000000..6b80e9ae8 --- /dev/null +++ b/src/dbStatic/devSup.h @@ -0,0 +1,71 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devSup.h Device Support */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INCdevSuph +#define INCdevSuph 1 + +#include "errMdef.h" +#include "shareLib.h" + +/* structures defined elsewhere */ +struct dbCommon; +struct devSup; + +#ifdef __cplusplus +extern "C" { + typedef long (*DEVSUPFUN)(void *); /* ptr to device support function*/ +#else + typedef long (*DEVSUPFUN)(); /* ptr to device support function*/ +#endif + +typedef struct dset { /* device support entry table */ + long number; /*number of support routines*/ + DEVSUPFUN report; /*print report*/ + DEVSUPFUN init; /*init support layer*/ + DEVSUPFUN init_record; /*init device for particular record*/ + DEVSUPFUN get_ioint_info; /* get io interrupt information*/ + /*other functions are record dependent*/ +} dset; + +typedef struct dsxt { /* device support extension table */ + long (*add_record)(struct dbCommon *precord); + long (*del_record)(struct dbCommon *precord); + /* Recordtypes are *not* allowed to extend this table */ +} dsxt; + +epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */ + +epicsShareFunc void devExtend(dsxt *pdsxt); +epicsShareFunc void dbInitDevSup(struct devSup *pdevSup, dset *pdset); + + +#define S_dev_noDevSup (M_devSup| 1) /*SDR_DEVSUP: Device support missing*/ +#define S_dev_noDSET (M_devSup| 3) /*Missing device support entry table*/ +#define S_dev_missingSup (M_devSup| 5) /*Missing device support routine*/ +#define S_dev_badInpType (M_devSup| 7) /*Bad INP link type*/ +#define S_dev_badOutType (M_devSup| 9) /*Bad OUT link type*/ +#define S_dev_badInitRet (M_devSup|11) /*Bad init_rec return value */ +#define S_dev_badBus (M_devSup|13) /*Illegal bus type*/ +#define S_dev_badCard (M_devSup|15) /*Illegal or nonexistant module*/ +#define S_dev_badSignal (M_devSup|17) /*Illegal signal*/ +#define S_dev_NoInit (M_devSup|19) /*No init*/ +#define S_dev_Conflict (M_devSup|21) /*Multiple records accessing same signal*/ +#define S_dev_noDeviceFound (M_devSup|23) /*No device found at specified address*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/dbStatic/drvSup.h b/src/dbStatic/drvSup.h new file mode 100644 index 000000000..94d4a7574 --- /dev/null +++ b/src/dbStatic/drvSup.h @@ -0,0 +1,35 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* drvSup.h Driver Support */ +/* share/epicsH Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INCdrvSuph +#define INCdrvSuph 1 + +#include "errMdef.h" + +typedef long (*DRVSUPFUN) (); /* ptr to driver support function*/ + +typedef struct drvet { /* driver entry table */ + long number; /*number of support routines*/ + DRVSUPFUN report; /*print report*/ + DRVSUPFUN init; /*init support*/ + /*other functions are device dependent*/ +}drvet; +#define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) ) + +#define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/ +#define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/ + +#endif diff --git a/src/dbStatic/guigroup.h b/src/dbStatic/guigroup.h new file mode 100644 index 000000000..80aee72a5 --- /dev/null +++ b/src/dbStatic/guigroup.h @@ -0,0 +1,83 @@ +/*************************************************************************\ +* 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 + these are used in the pmt (prompt) field of the record support + ascii files. They represent field groupings for dct tools +*/ + +#ifndef __gui_group_h__ +#define __gui_group_h__ + +#define GUI_COMMON 1 +#define GUI_ALARMS 2 +#define GUI_BITS1 3 +#define GUI_BITS2 4 +#define GUI_CALC 5 +#define GUI_CLOCK 6 +#define GUI_COMPRESS 7 +#define GUI_CONVERT 8 +#define GUI_DISPLAY 9 +#define GUI_HIST 10 +#define GUI_INPUTS 11 +#define GUI_LINKS 12 +#define GUI_MBB 13 +#define GUI_MOTOR 14 +#define GUI_OUTPUT 15 +#define GUI_PID 16 +#define GUI_PULSE 17 +#define GUI_SELECT 18 +#define GUI_SEQ1 19 +#define GUI_SEQ2 20 +#define GUI_SEQ3 21 +#define GUI_SUB 22 +#define GUI_TIMER 23 +#define GUI_WAVE 24 +#define GUI_SCAN 25 +#define GUI_NTYPES 25 + +typedef struct mapguiGroup{ + char *strvalue; + int value; +}mapguiGroup; + +#ifndef GUIGROUPS_GBLSOURCE +extern mapguiGroup pamapguiGroup[]; +#else +mapguiGroup pamapguiGroup[GUI_NTYPES] = { + {"GUI_COMMON",GUI_COMMON}, + {"GUI_ALARMS",GUI_ALARMS}, + {"GUI_BITS1",GUI_BITS1}, + {"GUI_BITS2",GUI_BITS2}, + {"GUI_CALC",GUI_CALC}, + {"GUI_CLOCK",GUI_CLOCK}, + {"GUI_COMPRESS",GUI_COMPRESS}, + {"GUI_CONVERT",GUI_CONVERT}, + {"GUI_DISPLAY",GUI_DISPLAY}, + {"GUI_HIST",GUI_HIST}, + {"GUI_INPUTS",GUI_INPUTS}, + {"GUI_LINKS",GUI_LINKS}, + {"GUI_MBB",GUI_MBB}, + {"GUI_MOTOR",GUI_MOTOR}, + {"GUI_OUTPUT",GUI_OUTPUT}, + {"GUI_PID",GUI_PID}, + {"GUI_PULSE",GUI_PULSE}, + {"GUI_SELECT",GUI_SELECT}, + {"GUI_SEQ1",GUI_SEQ1}, + {"GUI_SEQ2",GUI_SEQ2}, + {"GUI_SEQ3",GUI_SEQ3}, + {"GUI_SUB",GUI_SUB}, + {"GUI_TIMER",GUI_TIMER}, + {"GUI_WAVE",GUI_WAVE}, + {"GUI_SCAN",GUI_SCAN} +}; +#endif /*GUIGROUPS_GBLSOURCE*/ + +#endif /*__gui_group_h__*/ diff --git a/src/dbStatic/link.h b/src/dbStatic/link.h new file mode 100644 index 000000000..8d1877077 --- /dev/null +++ b/src/dbStatic/link.h @@ -0,0 +1,194 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* link.h */ +/* base/include Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * Original Author: Bob Dalesio + * Current Author: Marty Kraimer + * Date: 6-1-90 + */ + +#include "dbDefs.h" +#include "shareLib.h" + +#ifndef INClinkh +#define INClinkh 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* link types */ +#define CONSTANT 0 +#define PV_LINK 1 +#define VME_IO 2 +#define CAMAC_IO 3 +#define AB_IO 4 +#define GPIB_IO 5 +#define BITBUS_IO 6 +#define MACRO_LINK 7 +#define DB_LINK 10 +#define CA_LINK 11 +#define INST_IO 12 /* instrument */ +#define BBGPIB_IO 13 /* bitbus -> gpib */ +#define RF_IO 14 +#define VXI_IO 15 +#define LINK_NTYPES 14 +typedef struct maplinkType{ + char *strvalue; + int value; +}maplinkType; + +epicsShareExtern maplinkType pamaplinkType[]; + +#define VXIDYNAMIC 0 +#define VXISTATIC 1 + +/* structure of a PV_LINK DB_LINK and a CA_LINK */ +/*Options defined by pvlMask */ +#define pvlOptMsMode 0x3 /*Maximize Severity mode selection*/ +#define pvlOptNMS 0 /*Dont Maximize Severity*/ +#define pvlOptMS 1 /*Maximize Severity allways*/ +#define pvlOptMSI 2 /*Maximize Severity if INVALID*/ +#define pvlOptMSS 3 /*Maximize Severity and copy Status*/ +#define pvlOptPP 0x4 /*Process Passive*/ +#define pvlOptCA 0x8 /*Always make it a CA link*/ +#define pvlOptCP 0x10 /*CA + process on monitor*/ +#define pvlOptCPP 0x20 /*CA + process passive record on monitor*/ +#define pvlOptFWD 0x40 /*Generate ca_put for forward link*/ +#define pvlOptInpNative 0x80 /*Input native*/ +#define pvlOptInpString 0x100 /*Input as string*/ +#define pvlOptOutNative 0x200 /*Output native*/ +#define pvlOptOutString 0x400 /*Output as string*/ +#define pvlOptTSELisTime 0x800 /*Field TSEL is getting timeStamp*/ + +typedef long (*LINKCVT)(); + +struct macro_link { + char *macroStr; +}; + +struct dbCommon; + +struct pv_link { + char *pvname; /*pvname to link to*/ + struct dbCommon *precord; /*Address of record containing link*/ + void *pvt; /*CA or DB private*/ + LINKCVT getCvt; /*input conversion function*/ + short pvlMask; /*Options mask*/ + short lastGetdbrType; /*last dbrType for DB or CA get*/ +}; + +/* structure of a VME io channel */ +struct vmeio { + short card; + short signal; + char *parm; +}; + +/* structure of a CAMAC io channel */ +struct camacio { + short b; + short c; + short n; + short a; + short f; + char *parm; +}; + +/* structure of a RF io channel */ +struct rfio { + short branch; + short cryo; + short micro; + short dataset; + short element; + long ext; +}; + +/* structure of a Allen-Bradley io channel */ +struct abio { + short link; + short adapter; + short card; + short signal; + char *parm; +}; + +/* structure of a gpib io channel */ +struct gpibio { + short link; + short addr; /* device address */ + char *parm; +}; + +/* structure of a bitbus io channel */ +struct bitbusio { + unsigned char link; + unsigned char node; + unsigned char port; + unsigned char signal; + char *parm; +}; + +/* structure of a bitbus to gpib io channel */ +struct bbgpibio { + unsigned char link; + unsigned char bbaddr; + unsigned char gpibaddr; + unsigned char pad; + char *parm; +}; + +/* structure of an instrument io link */ +struct instio { + char *string; /* the cat of location. + signal.parameter */ +}; + +/* structure of a vxi link */ +struct vxiio{ + short flag; /* 0 = frame/slot, 1 = SA */ + short frame; + short slot; + short la; /* logical address if flag =1 */ + short signal; + char *parm; +}; + +/* union of possible address structures */ +union value{ + char *constantStr; /*constant string*/ + struct macro_link macro_link; /* link containing macro substitution*/ + struct pv_link pv_link; /* link to process variable*/ + struct vmeio vmeio; /* vme io point */ + struct camacio camacio; /* camac io point */ + struct rfio rfio; /* CEBAF RF buffer interface */ + struct abio abio; /* allen-bradley io point */ + struct gpibio gpibio; + struct bitbusio bitbusio; + struct instio instio; /* instrument io link */ + struct bbgpibio bbgpibio; /* bitbus to gpib io link */ + struct vxiio vxiio; /* vxi io */ +}; + +struct link{ + union value value; + short type; + char *text; /* original INP/OUT link text */ +}; + +typedef struct link DBLINK; + +#ifdef __cplusplus +} +#endif +#endif /*INClinkh*/ diff --git a/src/dbStatic/recSup.h b/src/dbStatic/recSup.h new file mode 100644 index 000000000..0bea17411 --- /dev/null +++ b/src/dbStatic/recSup.h @@ -0,0 +1,87 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* recSup.h + * Record Support + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INCrecSuph +#define INCrecSuph 1 + +#include "errMdef.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long (*RECSUPFUN) (); /* ptr to record support function*/ + +typedef struct rset { /* record support entry table */ + long number; /*number of support routines */ + RECSUPFUN report; /*print report */ + RECSUPFUN init; /*init support */ + RECSUPFUN init_record; /*init record */ + RECSUPFUN process; /*process record */ + RECSUPFUN special; /*special processing */ + RECSUPFUN get_value; /*get value field */ + RECSUPFUN cvt_dbaddr; /*cvt dbAddr */ + RECSUPFUN get_array_info; + RECSUPFUN put_array_info; + RECSUPFUN get_units; + RECSUPFUN get_precision; + RECSUPFUN get_enum_str; /*get string from enum item*/ + RECSUPFUN get_enum_strs; /*get all enum strings */ + RECSUPFUN put_enum_str; /*put string from enum item*/ + RECSUPFUN get_graphic_double; + RECSUPFUN get_control_double; + RECSUPFUN get_alarm_double; +}rset; + +#define RSETNUMBER ( (sizeof(struct rset) - sizeof(long))/sizeof(RECSUPFUN) ) + + +#define S_rec_noRSET (M_recSup| 1) /*Missing record support entry table*/ +#define S_rec_noSizeOffset (M_recSup| 2) /*Missing SizeOffset Routine*/ +#define S_rec_outMem (M_recSup| 3) /*Out of Memory*/ + + +/* Definition os structure for routine get_value */ + +typedef struct valueDes { + long field_type; + long no_elements; + void * pvalue; +}valueDes; + +/************************************************************************ + * report(FILE fp,void *precord); + * init(); + * init_record(precord,pass); + * process(void *precord); + * special(struct dbAddr *paddr, after); + * get_value(precord,struct valueDes *p); + * cvt_dbaddr(struct dbAddr *paddr); + * get_array_info(paddr,long *no_elements,long *offset); + * put_array_info(paddr,nNew); + * get_units(paddr,char units[8]); + * get_precision(struct dbAddr *paddr,long *precision); + * get_enum_str(paddr,pbuffer); + * get_enum_strs(paddr,struct dbr_enumStrs *p); + * put_enum_str(paddr,pbuffer); + * get_graphic_double(paddr,struct dbr_grDouble *p); + * get_control_double(paddr,struct dbr_ctrlDouble *p); + * get_alarm_double(paddr,struct dbr_ctrlDouble *p); + ***********************************************************************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*INCrecSuph*/ diff --git a/src/dbStatic/special.h b/src/dbStatic/special.h new file mode 100644 index 000000000..e187b83bc --- /dev/null +++ b/src/dbStatic/special.h @@ -0,0 +1,69 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* special.h */ +/* share/epicsH Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INCspecialh +#define INCspecialh 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*NOTE Do NOT add aditional definitions with out modifying dbLexRoutines.c */ +/* types 1-99 are global. Record specific must start with 100 */ +#define SPC_NOMOD 1 /*Field must not be modified */ +#define SPC_DBADDR 2 /*db_name_to_addr must call cvt_dbaddr */ +#define SPC_SCAN 3 /*A scan related field is being changed */ +#define SPC_ALARMACK 5 /*Special Alarm Acknowledgement*/ +#define SPC_AS 6 /* Access Security*/ +#define SPC_ATTRIBUTE 7 /* psuedo field, i.e. attribute field*/ +/* useful when record support must be notified of a field changing value*/ +#define SPC_MOD 100 +/* used by all records that support a reset field */ +#define SPC_RESET 101 /*The res field is being modified*/ +/* Specific to conversion (Currently only ai */ +#define SPC_LINCONV 102 /*A linear conversion field is being changed*/ +/* Specific to calculation records */ +#define SPC_CALC 103 /*The CALC field is being changed*/ + + +#define SPC_NTYPES 9 +typedef struct mapspcType{ + char *strvalue; + int value; +}mapspcType; + +#ifndef SPECIAL_GBLSOURCE +extern mapspcType pamapspcType[]; +#else +mapspcType pamapspcType[SPC_NTYPES] = { + {"SPC_NOMOD",SPC_NOMOD}, + {"SPC_DBADDR",SPC_DBADDR}, + {"SPC_SCAN",SPC_SCAN}, + {"SPC_ALARMACK",SPC_ALARMACK}, + {"SPC_AS",SPC_AS}, + {"SPC_MOD",SPC_MOD}, + {"SPC_RESET",SPC_RESET}, + {"SPC_LINCONV",SPC_LINCONV}, + {"SPC_CALC",SPC_CALC} +}; +#endif /*SPECIAL_GBLSOURCE*/ + +#ifdef __cplusplus +} +#endif + +#endif /*INCspecialh*/ diff --git a/src/dbtools/Makefile b/src/dbtools/Makefile new file mode 100644 index 000000000..38ed52c9e --- /dev/null +++ b/src/dbtools/Makefile @@ -0,0 +1,44 @@ +#************************************************************************* +# 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 + +INC += dbLoadTemplate.h +INC += dbtoolsIocRegister.h + +LIB_SRCS += dbLoadTemplate.c +LIB_SRCS += dbtoolsIocRegister.c + +LIBRARY_IOC = dbtoolsIoc + +dbtoolsIoc_LIBS = dbIoc dbStaticIoc Com + +dbtoolsIoc_RCS = dbtoolsIoc.rc + +HTMLS += dbLoadTemplate.html + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=dbtoolsIoc +OBJLIB_SRCS = $(LIB_SRCS) +endif + +include $(TOP)/configure/RULES + +# +# These lex sources are included in some C sources, +# so they have to be created in time: +# +dbLoadTemplate.c: dbLoadTemplate_lex.c ../dbLoadTemplate.h + +clean:: + @$(RM) dbLoadTemplate_lex.c dbLoadTemplate.c + diff --git a/src/dbtools/dbLoadTemplate.h b/src/dbtools/dbLoadTemplate.h new file mode 100644 index 000000000..a341b8098 --- /dev/null +++ b/src/dbtools/dbLoadTemplate.h @@ -0,0 +1,22 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* dbLoadTemplate.h */ + +#ifndef INCdbLoadTemplateh +#define INCdbLoadTemplateh + +#include "shareLib.h" +epicsShareFunc int epicsShareAPI dbLoadTemplate(char* sub_file); + +#endif /*INCdbLoadTemplateh*/ + + + + diff --git a/src/dbtools/dbLoadTemplate.html b/src/dbtools/dbLoadTemplate.html new file mode 100644 index 000000000..4943b8c6f --- /dev/null +++ b/src/dbtools/dbLoadTemplate.html @@ -0,0 +1,137 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +

    +
    +
    +
    +

    NAME

    +     dbLoadRecords, dbLoadTemplate - load ascii database  records
    +     into an IOC
    +
    +
    +
    +

    SYNOPSIS

    +     dbLoadRecords(char* db_file, char* substitutions)
    +
    +     dbLoadTemplate(char* template_file)
    +
    +
    +
    +

    DESCRIPTION

    +     These routines are available from IOC core  on  the  vxWorks
    +     command  line.  Both provide a way to load ascii ".db" files
    +     (usually created by gdct(1) ) into the IOC. The ".db"  files
    +     contain ascii versions of record instances and are described
    +     in more detail in dbfile(5).  In  addition  to  loading  the
    +     ".db"  ascii  files  into  the  IOC, both routines provide a
    +     method of performing variable substitution on  record  names
    +     and field values.
    +
    +     dbLoadRecords() reads the ".db" file db_file performing sub-
    +     stitutions  specified in string substitutions. The substitu-
    +     tion must be a string specified as follows:
    +
    +     "var1=sub1,var2=sub3,..."
    +
    +     Variables   are   specified   in   the   ".db"    file    as
    +     $(variable_name).      If     the     substitution    string
    +     "a=1,b=2,c=\"this is a  test\""  were  used,  any  variables
    +     $(a),  $(b), or $(c) would be substituted with the appropri-
    +     ate data.  See the EXAMPLES section for more details.
    +
    +     dbLoadTemplate()   will   read    a    template_file.    The
    +     template_file  resides  in  the  your IOC boot directory and
    +     contains rules about loading ".db" files and performing sub-
    +     stitutions.   The  template_file must be in the form used by
    +     an IOC and is described in  templatefile(5).   The  EXAMPLES
    +     section descibes how it can be used.
    +
    +
    +
    +

    EXAMPLES

    +     The next two  examples  of  dbLoadRecords()  and  dbLoadTem-
    +     plate() will use the following ".db" file named test.db :
    +
    +     database(test)
    +     {
    +          record(ai,"$(pre)testrec1")
    +          record(ai,"$(pre)testrec2")
    +          record(stringout,"$(pre)testrec3")
    +          {
    +               field(VAL,"$(STRING)")
    +               field(SCAN,"$(SCAN)")
    +          }
    +     }
    +     Running dbLoadRecords ("test.db","pre=TEST,STRING=\"this  is
    +     a  test\",SCAN=Passive")  will produce the following records
    +     in the IOC's database:
    +
    +          TESTtestrec1
    +          TESTtestrec2
    +          TESTtestrec3
    +
    +     The third record will have VAL set to "this is a  test"  and
    +     SCAN set to "Passive".
    +
    +     Running dbLoadTemplate ("test.template") with  test.template
    +     containing:
    +     file test.db
    +     {
    +          {pre=TEST1, STRING = "this is a test two", SCAN="1 Second" }
    +          {pre=TEST2, STRING = "this is a test one", SCAN=Passive }
    +          {pre=TEST3, STRING = "this is a test three", SCAN=Passive }
    +     }
    +     will produce a total of nine records in the IOC's database:
    +          TEST1testrec1
    +          TEST1testrec2
    +          TEST1testrec3 - (VAL="this is a test two", SCAN="1 Second")
    +          TEST2testrec1
    +          TEST2testrec2
    +          TEST2testrec3 - (VAL="this is a test one", SCAN="Passive")
    +          TEST3testrec1
    +          TEST3testrec2
    +          TEST3testrec3 - (VAL="this is a test three", SCAN="Passive")
    +
    +
    +
    +

    NOTES

    +     The binary file default.dctsdr must be loaded prior to  run-
    +     ning either of these routines.  This file contains the rules
    +     on how to construct records and change field values.
    +
    +     After the default.dctsdr file is loaded, these routines  can
    +     be run as many times as desired until iocInit is run.
    +
    +
    +
    +

    SEE ALSO

    +     gdct(1), templatefile(5), dbfile(5)
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +Man(1) output converted with +man2html +
    + + diff --git a/src/dbtools/dbLoadTemplate.y b/src/dbtools/dbLoadTemplate.y new file mode 100644 index 000000000..0a919dda6 --- /dev/null +++ b/src/dbtools/dbLoadTemplate.y @@ -0,0 +1,315 @@ +%{ + +/*************************************************************************\ +* Copyright (c) 2006 UChicago, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include + +#include "osiUnistd.h" +#include "macLib.h" +#include "dbAccess.h" +#include "dbmf.h" +#include "epicsVersion.h" + +#define epicsExportSharedSymbols +#include "dbLoadTemplate.h" + +static int line_num; +static int yyerror(); + +#define VAR_MAX_VAR_STRING 5000 +#define VAR_MAX_VARS 100 + +static char *sub_collect = NULL; +static char** vars = NULL; +static char* db_file_name = NULL; +static int var_count,sub_count; + +%} + +%start template + +%token WORD QUOTE +%token DBFILE +%token PATTERN +%token EQUALS COMMA +%left O_PAREN C_PAREN +%left O_BRACE C_BRACE + +%union +{ + int Int; + char Char; + char *Str; + double Real; +} + +%% + +template: templs + | subst + ; + +templs: templs templ + | templ + ; + +templ: templ_head O_BRACE subst C_BRACE + | templ_head + { + if(db_file_name) + dbLoadRecords(db_file_name,NULL); + else + fprintf(stderr,"Error: no db file name given\n"); + } + ; + +templ_head: DBFILE WORD + { + var_count=0; + if(db_file_name) dbmfFree(db_file_name); + db_file_name = dbmfMalloc(strlen($2)+1); + strcpy(db_file_name,$2); + dbmfFree($2); + } + | DBFILE QUOTE + { + var_count=0; + if(db_file_name) dbmfFree(db_file_name); + db_file_name = dbmfMalloc(strlen($2)+1); + strcpy(db_file_name,$2); + dbmfFree($2); + } + ; + +subst: PATTERN pattern subs + | PATTERN pattern + | var_subs + ; + +pattern: O_BRACE vars C_BRACE + { +#ifdef ERROR_STUFF + int i; + for(i=0;i;] + +%% + +"pattern" { return(PATTERN); } +"file" { return(DBFILE); } + +{doublequote}({dstringchar}|{escape})*{doublequote} | +{singlequote}({sstringchar}|{escape})*{singlequote} { + yylval.Str = dbmfStrdup(yytext+1); + yylval.Str[strlen(yylval.Str)-1] = '\0'; + return(QUOTE); + } + +{bareword}+ { + yylval.Str = dbmfStrdup(yytext); + return(WORD); + } + +"=" { return(EQUALS); } +"," { return(COMMA); } +"{" { return(O_BRACE); } +"}" { return(C_BRACE); } + +{comment}.* ; +{whitespace} ; +{newline} { line_num++; } + +. { + char message[40]; + + sprintf(message,"invalid character '%c'", yytext[0]); + yyerror(message); + + /* Suppress compiler warning messages */ + if (0) yyunput('c',NULL); + if (0) yy_switch_to_buffer(NULL); + } + +%% diff --git a/src/dbtools/dbtoolsIoc.rc b/src/dbtools/dbtoolsIoc.rc new file mode 100755 index 000000000..5f3d70bf4 --- /dev/null +++ b/src/dbtools/dbtoolsIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Ioc Database Tools Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Ioc Database Tools Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "dbtoolsIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "dbtoolsIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/dbtools/dbtoolsIocRegister.c b/src/dbtools/dbtoolsIocRegister.c new file mode 100644 index 000000000..9fbe1096b --- /dev/null +++ b/src/dbtools/dbtoolsIocRegister.c @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "iocsh.h" + +#define epicsExportSharedSymbols +#include "dbtoolsIocRegister.h" +#include "dbLoadTemplate.h" + + +/* dbLoadTemplate */ +static const iocshArg dbLoadTemplateArg0 = { "file name",iocshArgString}; +static const iocshArg * const dbLoadTemplateArgs[1] = {&dbLoadTemplateArg0}; +static const iocshFuncDef dbLoadTemplateFuncDef = + {"dbLoadTemplate",1,dbLoadTemplateArgs}; +static void dbLoadTemplateCallFunc(const iocshArgBuf *args) +{ + dbLoadTemplate(args[0].sval); +} + + +void epicsShareAPI dbtoolsIocRegister(void) +{ + iocshRegister(&dbLoadTemplateFuncDef,dbLoadTemplateCallFunc); +} diff --git a/src/dbtools/dbtoolsIocRegister.h b/src/dbtools/dbtoolsIocRegister.h new file mode 100644 index 000000000..880f42d0f --- /dev/null +++ b/src/dbtools/dbtoolsIocRegister.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_dbtoolsIocRegister_H +#define INC_dbtoolsIocRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI dbtoolsIocRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_dbtoolsIocRegister_H */ diff --git a/src/dev/Makefile b/src/dev/Makefile new file mode 100644 index 000000000..64a9721ac --- /dev/null +++ b/src/dev/Makefile @@ -0,0 +1,19 @@ +#************************************************************************* +# 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 + +DIRS += softDev +DIRS += testDev + +include $(TOP)/configure/RULES_DIRS + diff --git a/src/dev/softDev/Makefile b/src/dev/softDev/Makefile new file mode 100644 index 000000000..6c4f11cb0 --- /dev/null +++ b/src/dev/softDev/Makefile @@ -0,0 +1,66 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../.. + +include $(TOP)/configure/CONFIG + +DBD += devSoft.dbd + +LIBSRCS += devAaiSoft.c +LIBSRCS += devAaoSoft.c +LIBSRCS += devAiSoft.c +LIBSRCS += devAiSoftRaw.c +LIBSRCS += devAoSoft.c +LIBSRCS += devAoSoftRaw.c +LIBSRCS += devBiSoft.c +LIBSRCS += devBiSoftRaw.c +LIBSRCS += devBoSoft.c +LIBSRCS += devBoSoftRaw.c +LIBSRCS += devCalcoutSoft.c +LIBSRCS += devEventSoft.c +LIBSRCS += devHistogramSoft.c +LIBSRCS += devLiSoft.c +LIBSRCS += devLoSoft.c +LIBSRCS += devMbbiDirectSoft.c +LIBSRCS += devMbbiDirectSoftRaw.c +LIBSRCS += devMbbiSoft.c +LIBSRCS += devMbbiSoftRaw.c +LIBSRCS += devMbboDirectSoft.c +LIBSRCS += devMbboDirectSoftRaw.c +LIBSRCS += devMbboSoft.c +LIBSRCS += devMbboSoftRaw.c +LIBSRCS += devSASoft.c +LIBSRCS += devSiSoft.c +LIBSRCS += devSoSoft.c +LIBSRCS += devWfSoft.c +LIBSRCS += devGeneralTime.c + +LIBSRCS += devAoSoftCallback.c +LIBSRCS += devBoSoftCallback.c +LIBSRCS += devCalcoutSoftCallback.c +LIBSRCS += devLoSoftCallback.c +LIBSRCS += devMbboSoftCallback.c +LIBSRCS += devMbboDirectSoftCallback.c +LIBSRCS += devSoSoftCallback.c + +LIBSRCS += devTimestamp.c +LIBSRCS += devSoStdio.c + +LIBRARY_IOC += softDevIoc +softDevIoc_LIBS += miscIoc recIoc asIoc dbIoc registryIoc dbStaticIoc ca Com +softDevIoc_RCS = softDevIoc.rc + +# For R3.13 compatability +ifeq ($(strip $(COMPAT_313)),YES) +OBJS_vxWorks = $(LIBSRCS:%.c=%) +endif + +include $(TOP)/configure/RULES + + diff --git a/src/dev/softDev/devAaiSoft.c b/src/dev/softDev/devAaiSoft.c new file mode 100644 index 000000000..c119508a9 --- /dev/null +++ b/src/dev/softDev/devAaiSoft.c @@ -0,0 +1,87 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * devAaiSoft.c - Device Support Routines for soft Waveform Records + * + * Original Author: Bob Dalesio + * Current Author: Dirk Zimoch + * Date: 27-MAY-2010 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "cantProceed.h" +#include "menuYesNo.h" +#include "aaiRecord.h" +#include "epicsExport.h" + +/* Create the dset for devAaiSoft */ +static long init_record(); +static long read_aai(); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_aai; +} devAaiSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_aai +}; +epicsExportAddress(dset,devAaiSoft); + +static long init_record(aaiRecord *prec) +{ + /* INP must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + prec->nord = 0; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default : + recGblRecordError(S_db_badField, (void *)prec, + "devAaiSoft (init_record) Illegal INP field"); + return(S_db_badField); + } + return 0; +} + +static long read_aai(aaiRecord *prec) +{ + long nRequest = prec->nelm; + + dbGetLink(prec->simm == menuYesNoYES ? &prec->siol : &prec->inp, + prec->ftvl, prec->bptr, 0, &nRequest); + if (nRequest > 0) { + prec->nord = nRequest; + prec->udf=FALSE; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + + return 0; +} diff --git a/src/dev/softDev/devAaoSoft.c b/src/dev/softDev/devAaoSoft.c new file mode 100644 index 000000000..835943583 --- /dev/null +++ b/src/dev/softDev/devAaoSoft.c @@ -0,0 +1,79 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * devAaoSoft.c - Device Support Routines for soft Waveform Records + * + * Original Author: Bob Dalesio + * Current Author: Dirk Zimoch + * Date: 27-MAY-2010 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "cantProceed.h" +#include "menuYesNo.h" +#include "aaoRecord.h" +#include "epicsExport.h" + +/* Create the dset for devAaoSoft */ +static long init_record(); +static long write_aao(); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_aao; +} devAaoSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + write_aao +}; +epicsExportAddress(dset,devAaoSoft); + +static long init_record(aaoRecord *prec) +{ + /* OUT must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/ + switch (prec->out.type) { + case CONSTANT: + prec->nord = 0; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default : + recGblRecordError(S_db_badField, prec, + "devAaoSoft (init_record) Illegal OUT field"); + return(S_db_badField); + } + return 0; +} + +static long write_aao(aaoRecord *prec) +{ + long nRequest = prec->nord; + dbPutLink(prec->simm == menuYesNoYES ? &prec->siol : &prec->out, + prec->ftvl, prec->bptr, nRequest); + + return 0; +} diff --git a/src/dev/softDev/devAiSoft.c b/src/dev/softDev/devAiSoft.c new file mode 100644 index 000000000..b0386422a --- /dev/null +++ b/src/dev/softDev/devAiSoft.c @@ -0,0 +1,96 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 3/6/91 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "aiRecord.h" +#include "epicsExport.h" + +/* Create the dset for devAiSoft */ +static long init_record(aiRecord *prec); +static long read_ai(aiRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_ai; + DEVSUPFUN special_linconv; +} devAiSoft = { + 6, + NULL, + NULL, + init_record, + NULL, + read_ai, + NULL +}; +epicsExportAddress(dset, devAiSoft); + +static long init_record(aiRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_DOUBLE, &prec->val)) + prec->udf = FALSE; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devAiSoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_ai(aiRecord *prec) +{ + double val; + + if (prec->inp.type == CONSTANT) + return 2; + + if (!dbGetLink(&prec->inp, DBR_DOUBLE, &val, 0, 0)) { + + /* Apply smoothing algorithm */ + if (prec->smoo != 0.0 && prec->dpvt) + prec->val = val * (1.00 - prec->smoo) + (prec->val * prec->smoo); + else + prec->val = val; + + prec->udf = FALSE; + prec->dpvt = &devAiSoft; /* Any non-zero value */ + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } else { + prec->dpvt = NULL; + } + return 2; +} diff --git a/src/dev/softDev/devAiSoftRaw.c b/src/dev/softDev/devAiSoftRaw.c new file mode 100644 index 000000000..c0211bcb8 --- /dev/null +++ b/src/dev/softDev/devAiSoftRaw.c @@ -0,0 +1,78 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "aiRecord.h" +#include "epicsExport.h" + +/* Create the dset for devAiSoftRaw */ +static long init_record(aiRecord *prec); +static long read_ai(aiRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_ai; + DEVSUPFUN special_linconv; +} devAiSoftRaw = { + 6, + NULL, + NULL, + init_record, + NULL, + read_ai, + NULL +}; +epicsExportAddress(dset, devAiSoftRaw); + +static long init_record(aiRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->rval); + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devAiSoftRaw (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_ai(aiRecord *prec) +{ + if (!dbGetLink(&prec->inp, DBR_LONG, &prec->rval, 0, 0) && + prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + + return 0; +} diff --git a/src/dev/softDev/devAoSoft.c b/src/dev/softDev/devAoSoft.c new file mode 100644 index 000000000..fc640cae1 --- /dev/null +++ b/src/dev/softDev/devAoSoft.c @@ -0,0 +1,75 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devAoSoft.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* Device Support Routines for soft Analog Output Records*/ +/* + * Original Author: Bob Dalesio + * Current Author: Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "special.h" +#include "aoRecord.h" +#include "epicsExport.h" + +/* added for Channel Access Links */ +static long init_record(aoRecord *prec); + +/* Create the dset for devAoSoft */ +static long write_ao(aoRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_ao; + DEVSUPFUN special_linconv; +}devAoSoft={ + 6, + NULL, + NULL, + init_record, + NULL, + write_ao, + NULL}; +epicsExportAddress(dset,devAoSoft); + + +static long init_record(aoRecord *prec) +{ + + long status=0; + status = 2; + return status; + +} /* end init_record() */ + +static long write_ao(aoRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out,DBR_DOUBLE, &prec->oval,1); + + return(status); +} diff --git a/src/dev/softDev/devAoSoftCallback.c b/src/dev/softDev/devAoSoftCallback.c new file mode 100644 index 000000000..35c3f6fe2 --- /dev/null +++ b/src/dev/softDev/devAoSoftCallback.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devAoSoftCallbackCallback.c */ +/* + * Author: Marty Kraimer + * Date: 04NOV2003 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "dbCa.h" +#include "link.h" +#include "special.h" +#include "aoRecord.h" +#include "epicsExport.h" + +/* Create the dset for devAoSoftCallback */ +static long write_ao(aoRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_ao; + DEVSUPFUN special_linconv; +}devAoSoftCallback={ + 6, + NULL, + NULL, + NULL, + NULL, + write_ao, + NULL}; +epicsExportAddress(dset,devAoSoftCallback); + +static long write_ao(aoRecord *prec) +{ + struct link *plink = &prec->out; + long status; + + if(prec->pact) return(0); + if(plink->type!=CA_LINK) { + status = dbPutLink(plink,DBR_DOUBLE,&prec->oval,1); + return(status); + } + status = dbCaPutLinkCallback(plink,DBR_DOUBLE,&prec->oval,1, + dbCaCallbackProcess,plink); + if(status) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + return(status); + } + prec->pact = TRUE; + return(0); +} diff --git a/src/dev/softDev/devAoSoftRaw.c b/src/dev/softDev/devAoSoftRaw.c new file mode 100644 index 000000000..dbae9fde3 --- /dev/null +++ b/src/dev/softDev/devAoSoftRaw.c @@ -0,0 +1,63 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devAoSoftRaw.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* Device Support Routines for soft raw Analog Output Records*/ +/* + * Author: Janet Anderson + * Date: 09-25-91 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "special.h" +#include "aoRecord.h" +#include "epicsExport.h" + +/* Create the dset for devAoSoftRaw */ +static long write_ao(aoRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_ao; + DEVSUPFUN special_linconv; +}devAoSoftRaw={ + 6, + NULL, + NULL, + NULL, + NULL, + write_ao, + NULL +}; +epicsExportAddress(dset,devAoSoftRaw); + +static long write_ao(aoRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out,DBR_LONG,&prec->rval,1); + + return(status); +} diff --git a/src/dev/softDev/devBiSoft.c b/src/dev/softDev/devBiSoft.c new file mode 100644 index 000000000..ca6c84e70 --- /dev/null +++ b/src/dev/softDev/devBiSoft.c @@ -0,0 +1,79 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "biRecord.h" +#include "epicsExport.h" + +/* Create the dset for devBiSoft */ +static long init_record(biRecord *prec); +static long read_bi(biRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_bi; +} devBiSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_bi +}; +epicsExportAddress(dset, devBiSoft); + +static long init_record(biRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val)) + prec->udf = FALSE; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devBiSoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_bi(biRecord *prec) +{ + if (!dbGetLink(&prec->inp, DBR_USHORT, &prec->val, 0, 0)) { + if (prec->inp.type != CONSTANT) + prec->udf = FALSE; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + return 2; +} diff --git a/src/dev/softDev/devBiSoftRaw.c b/src/dev/softDev/devBiSoftRaw.c new file mode 100644 index 000000000..9dffd7a95 --- /dev/null +++ b/src/dev/softDev/devBiSoftRaw.c @@ -0,0 +1,76 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "biRecord.h" +#include "epicsExport.h" + +/* Create the dset for devBiSoftRaw */ +static long init_record(biRecord *prec); +static long read_bi(biRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_bi; +} devBiSoftRaw = { + 5, + NULL, + NULL, + init_record, + NULL, + read_bi +}; +epicsExportAddress(dset, devBiSoftRaw); + +static long init_record(biRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval); + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devBiSoftRaw (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_bi(biRecord *prec) +{ + if (!dbGetLink(&prec->inp, DBR_ULONG, &prec->rval, 0, 0) && + prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + + return 0; +} diff --git a/src/dev/softDev/devBoSoft.c b/src/dev/softDev/devBoSoft.c new file mode 100644 index 000000000..e5b138442 --- /dev/null +++ b/src/dev/softDev/devBoSoft.c @@ -0,0 +1,74 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devBoSoft.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* devBoSoft.c - Device Support Routines for Soft Binary Output*/ +/* + * Original Author: Bob Dalesio + * Current Author: Marty Kraimer + * Date: 6-1-90 + */ + + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "boRecord.h" +#include "epicsExport.h" + +static long init_record(boRecord *prec); + +/* Create the dset for devBoSoft */ +static long write_bo(boRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_bo; +}devBoSoft={ + 5, + NULL, + NULL, + init_record, + NULL, + write_bo +}; +epicsExportAddress(dset,devBoSoft); + +static long init_record(boRecord *prec) +{ + + long status=0; + + /* dont convert */ + status=2; + return status; + +} /* end init_record() */ + +static long write_bo(boRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out,DBR_USHORT,&prec->val,1); + + return(status); +} diff --git a/src/dev/softDev/devBoSoftCallback.c b/src/dev/softDev/devBoSoftCallback.c new file mode 100644 index 000000000..4cdb51a29 --- /dev/null +++ b/src/dev/softDev/devBoSoftCallback.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* devBoCallbackSoft.c */ +/* + * Author: Marty Kraimer + * Date: 04NOV2003 + */ + + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbLock.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "boRecord.h" +#include "epicsExport.h" + +/* Create the dset for devBoCallbackSoft */ +static long write_bo(boRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_bo; +}devBoSoftCallback={ + 5, + NULL, + NULL, + NULL, + NULL, + write_bo +}; +epicsExportAddress(dset,devBoSoftCallback); + +static long write_bo(boRecord *prec) +{ + struct link *plink = &prec->out; + long status; + + if(prec->pact) return(0); + if(plink->type!=CA_LINK) { + status = dbPutLink(plink,DBR_USHORT,&prec->val,1); + return(status); + } + status = dbCaPutLinkCallback(plink,DBR_USHORT,&prec->val,1, + dbCaCallbackProcess,plink); + if(status) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + return(status); + } + prec->pact = TRUE; + return(0); +} diff --git a/src/dev/softDev/devBoSoftRaw.c b/src/dev/softDev/devBoSoftRaw.c new file mode 100644 index 000000000..84268aa4d --- /dev/null +++ b/src/dev/softDev/devBoSoftRaw.c @@ -0,0 +1,73 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devBoSoftRaw.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* devBoSoftRaw.c - Device Support Routines for SoftRaw Binary Output*/ +/* + * Author: Janet Anderson + * Date: 3-28-92 + */ + + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "boRecord.h" +#include "epicsExport.h" + +/* added for Channel Access Links */ +static long init_record(boRecord *prec); + +/* Create the dset for devBoSoftRaw */ +static long write_bo(boRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_bo; +}devBoSoftRaw={ + 5, + NULL, + NULL, + init_record, + NULL, + write_bo +}; +epicsExportAddress(dset,devBoSoftRaw); + +static long init_record(boRecord *prec) +{ + long status; + + /*Don't convert*/ + status = 2; + return status; + +} /* end init_record() */ + +static long write_bo(boRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out,DBR_LONG, &prec->rval,1); + + return(status); +} diff --git a/src/dev/softDev/devCalcoutSoft.c b/src/dev/softDev/devCalcoutSoft.c new file mode 100644 index 000000000..f931e6ac0 --- /dev/null +++ b/src/dev/softDev/devCalcoutSoft.c @@ -0,0 +1,49 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devCalcoutSoft.c */ + +/* + * Author: Marty Kraimer + * Date: 05DEC2003 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "special.h" +#include "postfix.h" +#include "calcoutRecord.h" +#include "epicsExport.h" + +static long write_calcout(calcoutRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write; +} devCalcoutSoft = { + 5, NULL, NULL, NULL, NULL, write_calcout +}; +epicsExportAddress(dset, devCalcoutSoft); + +static long write_calcout(calcoutRecord *prec) +{ + return dbPutLink(&prec->out, DBR_DOUBLE, &prec->oval, 1); +} diff --git a/src/dev/softDev/devCalcoutSoftCallback.c b/src/dev/softDev/devCalcoutSoftCallback.c new file mode 100644 index 000000000..0552df826 --- /dev/null +++ b/src/dev/softDev/devCalcoutSoftCallback.c @@ -0,0 +1,64 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devCalcoutSoftCallback.c */ + +/* + * Author: Marty Kraimer + * Date: 05DEC2003 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "special.h" +#include "postfix.h" +#include "calcoutRecord.h" +#include "epicsExport.h" + +static long write_calcout(calcoutRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write; +} devCalcoutSoftCallback = { + 5, NULL, NULL, NULL, NULL, write_calcout +}; +epicsExportAddress(dset, devCalcoutSoftCallback); + +static long write_calcout(calcoutRecord *prec) +{ + struct link *plink = &prec->out; + long status; + + if (prec->pact) return 0; + if (plink->type != CA_LINK) { + status = dbPutLink(plink, DBR_DOUBLE, &prec->oval, 1); + return status; + } + status = dbCaPutLinkCallback(plink, DBR_DOUBLE, &prec->oval, 1, + dbCaCallbackProcess, plink); + if (status) { + recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); + return status; + } + prec->pact = TRUE; + return 0; +} diff --git a/src/dev/softDev/devEventSoft.c b/src/dev/softDev/devEventSoft.c new file mode 100644 index 000000000..e276e0b82 --- /dev/null +++ b/src/dev/softDev/devEventSoft.c @@ -0,0 +1,81 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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: Janet Anderson + * Date: 04-21-91 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "eventRecord.h" +#include "epicsExport.h" + +/* Create the dset for devEventSoft */ +static long init_record(eventRecord *prec); +static long read_event(eventRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_event; +} devEventSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_event +}; +epicsExportAddress(dset, devEventSoft); + +static long init_record(eventRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_USHORT, &prec->val)) + prec->udf = FALSE; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devEventSoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_event(eventRecord *prec) +{ + long status; + + status = dbGetLink(&prec->inp, DBR_USHORT, &prec->val, 0, 0); + if (!status) { + prec->udf = FALSE; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + return status; +} diff --git a/src/dev/softDev/devGeneralTime.c b/src/dev/softDev/devGeneralTime.c new file mode 100644 index 000000000..d6a4fa563 --- /dev/null +++ b/src/dev/softDev/devGeneralTime.c @@ -0,0 +1,298 @@ +/*************************************************************************\ +* EPICS BASE is 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 */ +/* + * Original Author: Sheng Peng, ORNL / SNS Project + * Date: 07/2004 + * + * EPICS device support for general timestamp support + * + * Integrated into base by Peter Denison, Diamond Light Source + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "epicsString.h" +#include "epicsGeneralTime.h" +#include "epicsExport.h" + +#include "aiRecord.h" +#include "boRecord.h" +#include "longinRecord.h" +#include "stringinRecord.h" + + +/********* ai record **********/ +static int getCurrentTime(double * pseconds) +{ + epicsTimeStamp ts; + + if (epicsTimeERROR != epicsTimeGetCurrent(&ts)) { + *pseconds = ts.secPastEpoch + ((double)(ts.nsec)) * 1e-9; + return 0; + } + return -1; +} + +static struct ai_channel { + char *name; + int (*get)(double *); +} ai_channels[] = { + {"TIME", getCurrentTime}, +}; + +static long init_ai(aiRecord *prec) +{ + int i; + + if (prec->inp.type != INST_IO) { + recGblRecordError(S_db_badField, (void *)prec, + "devAiGeneralTime::init_ai: Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + + for (i = 0; i < NELEMENTS(ai_channels); i++) { + struct ai_channel *pchan = &ai_channels[i]; + if (!epicsStrCaseCmp(prec->inp.value.instio.string, pchan->name)) { + prec->dpvt = pchan; + return 0; + } + } + + recGblRecordError(S_db_badField, (void *)prec, + "devAiGeneralTime::init_ai: Bad parm"); + prec->pact = TRUE; + prec->dpvt = NULL; + return S_db_badField; +} + +static long read_ai(aiRecord *prec) +{ + struct ai_channel *pchan = (struct ai_channel *)prec->dpvt; + + if (!pchan) return -1; + + if (pchan->get(&prec->val) == 0) { + prec->udf = FALSE; + return 2; + } + prec->udf = TRUE; + recGblSetSevr(prec, READ_ALARM, INVALID_ALARM); + return -1; +} + +struct { + dset common; + DEVSUPFUN read_write; + DEVSUPFUN special_linconv; +} devAiGeneralTime = { + {6, NULL, NULL, init_ai, NULL}, read_ai, NULL +}; +epicsExportAddress(dset, devAiGeneralTime); + + +/********* bo record **********/ +static void resetErrors(void) +{ + generalTimeResetErrorCounts(); +} + +static struct bo_channel { + char *name; + void (*put)(void); +} bo_channels[] = { + {"RSTERRCNT", resetErrors}, +}; + +static long init_bo(boRecord *prec) +{ + int i; + + if (prec->out.type != INST_IO) { + recGblRecordError(S_db_badField, (void *)prec, + "devAiGeneralTime::init_ai: Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + + for (i = 0; i < NELEMENTS(bo_channels); i++) { + struct bo_channel *pchan = &bo_channels[i]; + if (!epicsStrCaseCmp(prec->out.value.instio.string, pchan->name)) { + prec->dpvt = pchan; + prec->mask = 0; + return 2; + } + } + + recGblRecordError(S_db_badField, (void *)prec, + "devBoGeneralTime::init_bo: Bad parm"); + prec->pact = TRUE; + prec->dpvt = NULL; + return S_db_badField; +} + +static long write_bo(boRecord *prec) +{ + struct bo_channel *pchan = (struct bo_channel *)prec->dpvt; + + if (!pchan) return -1; + + pchan->put(); + return 0; +} + +struct { + dset common; + DEVSUPFUN read_write; +} devBoGeneralTime = { + {5, NULL, NULL, init_bo, NULL}, write_bo +}; +epicsExportAddress(dset, devBoGeneralTime); + + +/******* longin record *************/ +static int errorCount(void) +{ + return generalTimeGetErrorCounts(); +} + +static struct li_channel { + char *name; + int (*get)(void); +} li_channels[] = { + {"GETERRCNT", errorCount}, +}; + +static long init_li(longinRecord *prec) +{ + int i; + + if (prec->inp.type != INST_IO) { + recGblRecordError(S_db_badField, (void *)prec, + "devLiGeneralTime::init_li: Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + + for (i = 0; i < NELEMENTS(li_channels); i++) { + struct li_channel *pchan = &li_channels[i]; + if (!epicsStrCaseCmp(prec->inp.value.instio.string, pchan->name)) { + prec->dpvt = pchan; + return 0; + } + } + + recGblRecordError(S_db_badField, (void *)prec, + "devLiGeneralTime::init_li: Bad parm"); + prec->pact = TRUE; + prec->dpvt = NULL; + return S_db_badField; +} + +static long read_li(longinRecord *prec) +{ + struct li_channel *pchan = (struct li_channel *)prec->dpvt; + + if (!pchan) return -1; + + prec->val = pchan->get(); + return 0; +} + +struct { + dset common; + DEVSUPFUN read_write; +} devLiGeneralTime = { + {5, NULL, NULL, init_li, NULL}, read_li +}; +epicsExportAddress(dset, devLiGeneralTime); + + +/********** stringin record **********/ +static const char * timeProvider(void) +{ + return generalTimeCurrentProviderName(); +} + +static const char * highestProvider(void) +{ + return generalTimeHighestCurrentName(); +} + +static const char * eventProvider(void) +{ + return generalTimeEventProviderName(); +} + +static struct si_channel { + char *name; + const char * (*get)(void); +} si_channels[] = { + {"BESTTCP", timeProvider}, + {"TOPTCP", highestProvider}, + {"BESTTEP", eventProvider}, +}; + +static long init_si(stringinRecord *prec) +{ + int i; + + if (prec->inp.type != INST_IO) { + recGblRecordError(S_db_badField, (void *)prec, + "devSiGeneralTime::init_si: Illegal INP field"); + prec->pact = TRUE; + return S_db_badField; + } + + for (i = 0; i < NELEMENTS(si_channels); i++) { + struct si_channel *pchan = &si_channels[i]; + if (!epicsStrCaseCmp(prec->inp.value.instio.string, pchan->name)) { + prec->dpvt = pchan; + return 0; + } + } + + recGblRecordError(S_db_badField, (void *)prec, + "devSiGeneralTime::init_si: Bad parm"); + prec->pact = TRUE; + prec->dpvt = NULL; + return S_db_badField; +} + +static long read_si(stringinRecord *prec) +{ + struct si_channel *pchan = (struct si_channel *)prec->dpvt; + const char *name; + + if (!pchan) return -1; + + name = pchan->get(); + if (name) { + strncpy(prec->val, name, sizeof(prec->val)); + prec->val[sizeof(prec->val) - 1] = '\0'; + } else { + strcpy(prec->val, "No working providers"); + recGblSetSevr(prec, READ_ALARM, MAJOR_ALARM); + } + prec->udf = FALSE; + return 0; +} + +struct { + dset common; + DEVSUPFUN read_write; +} devSiGeneralTime = { + {5, NULL, NULL, init_si, NULL}, read_si +}; +epicsExportAddress(dset, devSiGeneralTime); diff --git a/src/dev/softDev/devHistogramSoft.c b/src/dev/softDev/devHistogramSoft.c new file mode 100644 index 000000000..2658c318b --- /dev/null +++ b/src/dev/softDev/devHistogramSoft.c @@ -0,0 +1,81 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devHistogramSoft.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Janet Anderson + * Date: 07/02/91 + */ +#include +#include +#include + +#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 "histogramRecord.h" +#include "epicsExport.h" + +/* Create the dset for devHistogramSoft */ +static long init_record(histogramRecord *prec); +static long read_histogram(histogramRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_histogram; + DEVSUPFUN special_linconv; +}devHistogramSoft={ + 6, + NULL, + NULL, + init_record, + NULL, + read_histogram, + NULL +}; +epicsExportAddress(dset,devHistogramSoft); + +static long init_record(histogramRecord *prec) +{ + long status = 0; + + /* histogram.svl must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/ + switch (prec->svl.type) { + case (CONSTANT) : + if(recGblInitConstantLink(&prec->svl,DBF_DOUBLE,&prec->sgnl)) + prec->udf = FALSE; + break; + case (PV_LINK) : + case (DB_LINK) : + case (CA_LINK) : + break; + default : + recGblRecordError(S_db_badField,(void *)prec, + "devHistogramSoft (init_record) Illegal SVL field"); + return(S_db_badField); + } + return(status); +} + +static long read_histogram(histogramRecord *prec) +{ + long status; + + status = dbGetLink(&prec->svl,DBR_DOUBLE, &prec->sgnl,0,0); + return(0); /*add count*/ +} diff --git a/src/dev/softDev/devLiSoft.c b/src/dev/softDev/devLiSoft.c new file mode 100644 index 000000000..e8f7f5ed2 --- /dev/null +++ b/src/dev/softDev/devLiSoft.c @@ -0,0 +1,79 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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: Janet Anderson + * Date: 09-23-91 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "longinRecord.h" +#include "epicsExport.h" + +/* Create the dset for devLiSoft */ +static long init_record(longinRecord *prec); +static long read_longin(longinRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_longin; +} devLiSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_longin +}; +epicsExportAddress(dset, devLiSoft); + +static long init_record(longinRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val)) + prec->udf = FALSE; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devLiSoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_longin(longinRecord *prec) +{ + long status; + + status = dbGetLink(&prec->inp, DBR_LONG, &prec->val, 0, 0); + if (!status && + prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + return status; +} diff --git a/src/dev/softDev/devLoSoft.c b/src/dev/softDev/devLoSoft.c new file mode 100644 index 000000000..46d03ee4c --- /dev/null +++ b/src/dev/softDev/devLoSoft.c @@ -0,0 +1,60 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devLoSoft.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Janet Anderson + * Date: 09-23-91 +*/ +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "longoutRecord.h" +#include "epicsExport.h" + +/* Create the dset for devLoSoft */ +static long init_record(longoutRecord *prec); +static long write_longout(longoutRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_longout; +}devLoSoft={ + 5, + NULL, + NULL, + init_record, + NULL, + write_longout +}; +epicsExportAddress(dset,devLoSoft); + +static long init_record(longoutRecord *prec) +{ + return(0); +} /* end init_record() */ + +static long write_longout(longoutRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out,DBR_LONG, &prec->val,1); + return(0); +} diff --git a/src/dev/softDev/devLoSoftCallback.c b/src/dev/softDev/devLoSoftCallback.c new file mode 100644 index 000000000..2bc4a7e46 --- /dev/null +++ b/src/dev/softDev/devLoSoftCallback.c @@ -0,0 +1,67 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devLoSoftCallback.c */ +/* + * Author: Marty Kraimer + * Date: 04NOV2003 + */ + + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "longoutRecord.h" +#include "epicsExport.h" + +/* Create the dset for devLoSoftCallback */ +static long write_longout(longoutRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_longout; +}devLoSoftCallback={ + 5, + NULL, + NULL, + NULL, + NULL, + write_longout +}; +epicsExportAddress(dset,devLoSoftCallback); + +static long write_longout(longoutRecord *prec) +{ + struct link *plink = &prec->out; + long status; + + if(prec->pact) return(0); + if(plink->type!=CA_LINK) { + status = dbPutLink(plink,DBR_LONG,&prec->val,1); + return(status); + } + status = dbCaPutLinkCallback(plink,DBR_LONG,&prec->val,1, + dbCaCallbackProcess,plink); + if(status) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + return(status); + } + prec->pact = TRUE; + return(0); +} diff --git a/src/dev/softDev/devMbbiDirectSoft.c b/src/dev/softDev/devMbbiDirectSoft.c new file mode 100644 index 000000000..8f7a0b013 --- /dev/null +++ b/src/dev/softDev/devMbbiDirectSoft.c @@ -0,0 +1,79 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Matthew Needes + * Date: 10-08-93 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "mbbiDirectRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbbiDirectSoft */ +static long init_record(mbbiDirectRecord *prec); +static long read_mbbi(mbbiDirectRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_mbbi; +} devMbbiDirectSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_mbbi +}; +epicsExportAddress(dset, devMbbiDirectSoft); + +static long init_record(mbbiDirectRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val)) + prec->udf = FALSE; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devMbbiDirectSoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_mbbi(mbbiDirectRecord *prec) +{ + if (!dbGetLink(&prec->inp, DBR_USHORT, &prec->val, 0, 0)) { + if (prec->inp.type != CONSTANT) + prec->udf = FALSE; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + return 2; +} diff --git a/src/dev/softDev/devMbbiDirectSoftRaw.c b/src/dev/softDev/devMbbiDirectSoftRaw.c new file mode 100644 index 000000000..79e0b6f8b --- /dev/null +++ b/src/dev/softDev/devMbbiDirectSoftRaw.c @@ -0,0 +1,80 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Matthew Needes + * Date: 10-08-93 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "mbbiDirectRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbbiDirectSoftRaw */ +static long init_record(mbbiDirectRecord *prec); +static long read_mbbi(mbbiDirectRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_mbbi; +} devMbbiDirectSoftRaw = { + 5, + NULL, + NULL, + init_record, + NULL, + read_mbbi +}; +epicsExportAddress(dset, devMbbiDirectSoftRaw); + +static long init_record(mbbiDirectRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval); + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devMbbiDirectSoftRaw (init_record) Illegal INP field"); + return S_db_badField; + } + /*to preserve old functionality*/ + if (prec->nobt == 0) prec->mask = 0xffffffff; + prec->mask <<= prec->shft; + return 0; +} + +static long read_mbbi(mbbiDirectRecord *prec) +{ + if (!dbGetLink(&prec->inp, DBR_LONG, &prec->rval, 0, 0)) { + prec->rval &= prec->mask; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + return 0; +} diff --git a/src/dev/softDev/devMbbiSoft.c b/src/dev/softDev/devMbbiSoft.c new file mode 100644 index 000000000..52ef97184 --- /dev/null +++ b/src/dev/softDev/devMbbiSoft.c @@ -0,0 +1,79 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "mbbiRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbbiSoft */ +static long init_record(mbbiRecord *prec); +static long read_mbbi(mbbiRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_mbbi; +} devMbbiSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_mbbi +}; +epicsExportAddress(dset, devMbbiSoft); + +static long init_record(mbbiRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_ENUM, &prec->val)) + prec->udf = FALSE; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devMbbiSoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_mbbi(mbbiRecord *prec) +{ + if (!dbGetLink(&prec->inp, DBR_USHORT, &prec->val, 0, 0)) { + if (prec->inp.type != CONSTANT) + prec->udf = FALSE; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + return 2; +} diff --git a/src/dev/softDev/devMbbiSoftRaw.c b/src/dev/softDev/devMbbiSoftRaw.c new file mode 100644 index 000000000..332cda689 --- /dev/null +++ b/src/dev/softDev/devMbbiSoftRaw.c @@ -0,0 +1,80 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "mbbiRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbbiSoftRaw */ +static long init_record(mbbiRecord *prec); +static long read_mbbi(mbbiRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_mbbi; +} devMbbiSoftRaw = { + 5, + NULL, + NULL, + init_record, + NULL, + read_mbbi +}; +epicsExportAddress(dset, devMbbiSoftRaw); + +static long init_record(mbbiRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + recGblInitConstantLink(&prec->inp, DBF_ULONG, &prec->rval); + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devMbbiSoftRaw (init_record) Illegal INP field"); + return S_db_badField; + } + /*to preserve old functionality*/ + if (prec->nobt == 0) prec->mask = 0xffffffff; + prec->mask <<= prec->shft; + return 0; +} + +static long read_mbbi(mbbiRecord *prec) +{ + if (!dbGetLink(&prec->inp, DBR_LONG, &prec->rval, 0, 0)) { + prec->rval &= prec->mask; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + return 0; +} diff --git a/src/dev/softDev/devMbboDirectSoft.c b/src/dev/softDev/devMbboDirectSoft.c new file mode 100644 index 000000000..5e43f4593 --- /dev/null +++ b/src/dev/softDev/devMbboDirectSoft.c @@ -0,0 +1,66 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devMbboDirectSoft.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Original Author: Bob Dalesio + * Current Author: Matthew Needes + * Date: 10-08-93 + */ +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "mbboDirectRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbboSoft */ +static long init_record(mbboDirectRecord *prec); +static long write_mbbo(mbboDirectRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; +}devMbboDirectSoft={ + 5, + NULL, + NULL, + init_record, + NULL, + write_mbbo +}; +epicsExportAddress(dset,devMbboDirectSoft); + +static long init_record(mbboDirectRecord *prec) +{ + long status = 0; + + /* dont convert */ + status = 2; + return status; + +} /* end init_record() */ + +static long write_mbbo(mbboDirectRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out,DBR_USHORT,&prec->val,1); + return(0); +} diff --git a/src/dev/softDev/devMbboDirectSoftCallback.c b/src/dev/softDev/devMbboDirectSoftCallback.c new file mode 100644 index 000000000..c5639d4f5 --- /dev/null +++ b/src/dev/softDev/devMbboDirectSoftCallback.c @@ -0,0 +1,66 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devMbboDirectSoftCallback.c */ +/* + * Author: Marty Kraimer + * Date: 04NOV2003 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "mbboDirectRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbboSoft */ +static long write_mbbo(mbboDirectRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; +}devMbboDirectSoftCallback={ + 5, + NULL, + NULL, + NULL, + NULL, + write_mbbo +}; +epicsExportAddress(dset,devMbboDirectSoftCallback); + +static long write_mbbo(mbboDirectRecord *prec) +{ + struct link *plink = &prec->out; + long status; + + if(prec->pact) return(0); + if(plink->type!=CA_LINK) { + status = dbPutLink(plink,DBR_USHORT,&prec->val,1); + return(status); + } + status = dbCaPutLinkCallback(plink,DBR_USHORT,&prec->val,1, + dbCaCallbackProcess,plink); + if(status) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + return(status); + } + prec->pact = TRUE; + return(0); +} diff --git a/src/dev/softDev/devMbboDirectSoftRaw.c b/src/dev/softDev/devMbboDirectSoftRaw.c new file mode 100644 index 000000000..1b0b6700e --- /dev/null +++ b/src/dev/softDev/devMbboDirectSoftRaw.c @@ -0,0 +1,71 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devMbboDirectSoftRaw.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Janet Anderson + * Current Author: Matthew Needes + * Date: 10-08-93 + */ +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "mbboDirectRecord.h" +#include "epicsExport.h" + + +/* Create the dset for devMbboDirectSoftRaw */ +static long init_record(mbboDirectRecord *prec); +static long write_mbbo(mbboDirectRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; +}devMbboDirectSoftRaw={ + 5, + NULL, + NULL, + init_record, + NULL, + write_mbbo +}; +epicsExportAddress(dset,devMbboDirectSoftRaw); + +static long init_record(mbboDirectRecord *prec) +{ + long status = 0; + + if (prec->out.type != PV_LINK) + status = 2; + /*to preserve old functionality*/ + if(prec->nobt == 0) prec->mask = 0xffffffff; + prec->mask <<= prec->shft; + return status; +} /* end init_record() */ + +static long write_mbbo(mbboDirectRecord *prec) +{ + long status; + unsigned long data; + + data = prec->rval & prec->mask; + status = dbPutLink(&prec->out,DBR_LONG, &data,1); + return(0); +} diff --git a/src/dev/softDev/devMbboSoft.c b/src/dev/softDev/devMbboSoft.c new file mode 100644 index 000000000..d50ef4e5a --- /dev/null +++ b/src/dev/softDev/devMbboSoft.c @@ -0,0 +1,67 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devMbboSoft.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Original Author: Bob Dalesio + * Current Author: Marty Kraimer + * Date: 6-1-90 + */ +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "mbboRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbboSoft */ +static long init_record(mbboRecord *prec); +static long write_mbbo(mbboRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; +}devMbboSoft={ + 5, + NULL, + NULL, + init_record, + NULL, + write_mbbo +}; +epicsExportAddress(dset,devMbboSoft); + +static long init_record(mbboRecord *prec) +{ + + long status=0; + + /*dont convert*/ + status=2; + return status; + +} /* end init_record() */ + +static long write_mbbo(mbboRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out,DBR_USHORT, &prec->val,1); + return(0); +} diff --git a/src/dev/softDev/devMbboSoftCallback.c b/src/dev/softDev/devMbboSoftCallback.c new file mode 100644 index 000000000..705308163 --- /dev/null +++ b/src/dev/softDev/devMbboSoftCallback.c @@ -0,0 +1,66 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devMbboSoftCallback.c */ +/* + * Author: Marty Kraimer + * Date: 04NOV2003 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "mbboRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbboSoftCallback */ +static long write_mbbo(mbboRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; +}devMbboSoftCallback={ + 5, + NULL, + NULL, + NULL, + NULL, + write_mbbo +}; +epicsExportAddress(dset,devMbboSoftCallback); + +static long write_mbbo(mbboRecord *prec) +{ + struct link *plink = &prec->out; + long status; + + if(prec->pact) return(0); + if(plink->type!=CA_LINK) { + status = dbPutLink(plink,DBR_USHORT,&prec->val,1); + return(status); + } + status = dbCaPutLinkCallback(plink,DBR_USHORT,&prec->val,1, + dbCaCallbackProcess,plink); + if(status) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + return(status); + } + prec->pact = TRUE; + return(0); +} diff --git a/src/dev/softDev/devMbboSoftRaw.c b/src/dev/softDev/devMbboSoftRaw.c new file mode 100644 index 000000000..a1a164b80 --- /dev/null +++ b/src/dev/softDev/devMbboSoftRaw.c @@ -0,0 +1,71 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devMbboSoftRaw.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Janet Anderson + * Date: 3-28-92 + */ +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "mbboRecord.h" +#include "epicsExport.h" + +/* Create the dset for devMbboSoftRaw */ +static long init_record(mbboRecord *prec); +static long write_mbbo(mbboRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; +}devMbboSoftRaw={ + 5, + NULL, + NULL, + init_record, + NULL, + write_mbbo +}; +epicsExportAddress(dset,devMbboSoftRaw); + +static long init_record(mbboRecord *prec) +{ + + long status; + + /*to preserve old functionality*/ + if(prec->nobt == 0) prec->mask = 0xffffffff; + prec->mask <<= prec->shft; + /*dont convert*/ + status = 2; + return status; + +} /* end init_record() */ + +static long write_mbbo(mbboRecord *prec) +{ + long status; + unsigned long data; + + data = prec->rval & prec->mask; + status = dbPutLink(&prec->out,DBR_LONG, &data,1); + return(0); +} diff --git a/src/dev/softDev/devSASoft.c b/src/dev/softDev/devSASoft.c new file mode 100644 index 000000000..f2cb91408 --- /dev/null +++ b/src/dev/softDev/devSASoft.c @@ -0,0 +1,100 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems +* Group, Systems Engineering Department +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Revision-Id: anj@aps.anl.gov-20101112220909-zd1pn4qnqsafag0l + * + * Author: Carl Lionberger + * Date: 9-2-93 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "subArrayRecord.h" +#include "epicsExport.h" + +/* Create the dset for devSASoft */ +static long init_record(subArrayRecord *prec); +static long read_sa(subArrayRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_sa; +} devSASoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_sa +}; +epicsExportAddress(dset, devSASoft); + +static long init_record(subArrayRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + prec->nord = 0; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devSASoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_sa(subArrayRecord *prec) +{ + long nRequest = prec->indx + prec->nelm; + long ecount; + + if (nRequest > prec->malm) + nRequest = prec->malm; + + if (prec->inp.type == CONSTANT) + nRequest = prec->nord; + else + dbGetLink(&prec->inp, prec->ftvl, prec->bptr, 0, &nRequest); + + ecount = nRequest - prec->indx; + if (ecount > 0) { + int esize = dbValueSize(prec->ftvl); + + if (ecount > prec->nelm) + ecount = prec->nelm; + memmove(prec->bptr, (char *)prec->bptr + prec->indx * esize, + ecount * esize); + } else + ecount = 0; + + prec->nord = ecount; + + if (nRequest > 0 && + prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + + return 0; +} diff --git a/src/dev/softDev/devSiSoft.c b/src/dev/softDev/devSiSoft.c new file mode 100644 index 000000000..62d2996c5 --- /dev/null +++ b/src/dev/softDev/devSiSoft.c @@ -0,0 +1,84 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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: Janet Anderson + * Date: 04-21-91 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "epicsTime.h" +#include "recGbl.h" +#include "devSup.h" +#include "link.h" +#include "stringinRecord.h" +#include "epicsExport.h" + +/* Create the dset for devSiSoft */ +static long init_record(stringinRecord *prec); +static long read_stringin(stringinRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_stringin; +} devSiSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_stringin +}; +epicsExportAddress(dset, devSiSoft); + +static long init_record(stringinRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + if (recGblInitConstantLink(&prec->inp, DBF_STRING, prec->val)) + prec->udf = FALSE; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devSiSoft (init_record) Illegal INP field"); + return S_db_badField; + } + return 0; +} + +static long read_stringin(stringinRecord *prec) +{ + long status; + + status = dbGetLink(&prec->inp, DBR_STRING, prec->val, 0, 0); + if (!status) { + if (prec->inp.type != CONSTANT) + prec->udf = FALSE; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + return status; +} diff --git a/src/dev/softDev/devSoSoft.c b/src/dev/softDev/devSoSoft.c new file mode 100644 index 000000000..ca0c27ead --- /dev/null +++ b/src/dev/softDev/devSoSoft.c @@ -0,0 +1,54 @@ +/*************************************************************************\ +* 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: Janet Anderson + * Date: 21APR1991 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "stringoutRecord.h" +#include "epicsExport.h" + +/* Create the dset for devSoSoft */ +static long write_stringout(stringoutRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_stringout; +} devSoSoft = { + 5, + NULL, + NULL, + NULL, + NULL, + write_stringout +}; +epicsExportAddress(dset, devSoSoft); + +static long write_stringout(stringoutRecord *prec) +{ + long status; + + status = dbPutLink(&prec->out, DBR_STRING, prec->val, 1); + return status; +} diff --git a/src/dev/softDev/devSoSoftCallback.c b/src/dev/softDev/devSoSoftCallback.c new file mode 100644 index 000000000..5b22347fa --- /dev/null +++ b/src/dev/softDev/devSoSoftCallback.c @@ -0,0 +1,68 @@ +/*************************************************************************\ +* 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: Marty Kraimer + * Date: 04NOV2003 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "stringoutRecord.h" +#include "epicsExport.h" + +/* Create the dset for devSoSoftCallback */ +static long write_stringout(stringoutRecord *prec); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_stringout; +} devSoSoftCallback = { + 5, + NULL, + NULL, + NULL, + NULL, + write_stringout +}; +epicsExportAddress(dset, devSoSoftCallback); + +static long write_stringout(stringoutRecord *prec) +{ + struct link *plink = &prec->out; + long status; + + if (prec->pact) return 0; + + if (plink->type != CA_LINK) { + return dbPutLink(plink, DBR_STRING, &prec->val, 1); + } + + status = dbCaPutLinkCallback(plink, DBR_STRING, &prec->val, 1, + dbCaCallbackProcess, plink); + if (status) { + recGblSetSevr(prec, LINK_ALARM, INVALID_ALARM); + return status; + } + + prec->pact = TRUE; + return 0; +} diff --git a/src/dev/softDev/devSoStdio.c b/src/dev/softDev/devSoStdio.c new file mode 100644 index 000000000..cfdde0318 --- /dev/null +++ b/src/dev/softDev/devSoStdio.c @@ -0,0 +1,109 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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 */ + +#include +#include + +#include "dbCommon.h" +#include "devSup.h" +#include "errlog.h" +#include "recGbl.h" +#include "recSup.h" +#include "epicsExport.h" + +#include "stringoutRecord.h" + +typedef int (*PRINTFFUNC)(const char *fmt, ...); + +static int stderrPrintf(const char *fmt, ...); +static int logPrintf(const char *fmt, ...); + + +static struct outStream { + const char *name; + PRINTFFUNC print; +} outStreams[] = { + {"stdout", printf}, + {"stderr", stderrPrintf}, + {"errlog", logPrintf}, + {NULL, NULL} +}; + +static int stderrPrintf(const char *fmt, ...) { + va_list pvar; + int retval; + + va_start(pvar, fmt); + retval = vfprintf(stderr, fmt, pvar); + va_end (pvar); + + return retval; +} + +static int logPrintf(const char *fmt, ...) { + va_list pvar; + int retval; + + va_start(pvar, fmt); + retval = errlogVprintf(fmt, pvar); + va_end (pvar); + + return retval; +} + +static long add(dbCommon *pcommon) { + stringoutRecord *prec = (stringoutRecord *) pcommon; + struct outStream *pstream; + + if (prec->out.type != INST_IO) + return S_dev_badOutType; + + for (pstream = outStreams; pstream->name; ++pstream) { + if (strcmp(prec->out.value.instio.string, pstream->name) == 0) { + prec->dpvt = pstream; + return 0; + } + } + prec->dpvt = NULL; + return -1; +} + +static long del(dbCommon *pcommon) { + stringoutRecord *prec = (stringoutRecord *) pcommon; + + prec->dpvt = NULL; + return 0; +} + +static struct dsxt dsxtSoStdio = { + add, del +}; + +static long init(int pass) +{ + if (pass == 0) devExtend(&dsxtSoStdio); + return 0; +} + +static long write_string(stringoutRecord *prec) +{ + struct outStream *pstream = (struct outStream *)prec->dpvt; + if (pstream) + pstream->print("%s\n", prec->val); + return 0; +} + +/* Create the dset for devSoStdio */ +static struct { + dset common; + DEVSUPFUN write; +} devSoStdio = { + {5, NULL, init, NULL, NULL}, write_string +}; +epicsExportAddress(dset, devSoStdio); diff --git a/src/dev/softDev/devSoft.dbd b/src/dev/softDev/devSoft.dbd new file mode 100644 index 000000000..763ac47d5 --- /dev/null +++ b/src/dev/softDev/devSoft.dbd @@ -0,0 +1,45 @@ +device(aai,CONSTANT,devAaiSoft,"Soft Channel") +device(aao,CONSTANT,devAaoSoft,"Soft Channel") +device(ai,CONSTANT,devAiSoft,"Soft Channel") +device(ao,CONSTANT,devAoSoft,"Soft Channel") +device(bi,CONSTANT,devBiSoft,"Soft Channel") +device(bo,CONSTANT,devBoSoft,"Soft Channel") +device(calcout,CONSTANT,devCalcoutSoft,"Soft Channel") +device(event,CONSTANT,devEventSoft,"Soft Channel") +device(longin,CONSTANT,devLiSoft,"Soft Channel") +device(longout,CONSTANT,devLoSoft,"Soft Channel") +device(mbbi,CONSTANT,devMbbiSoft,"Soft Channel") +device(mbbiDirect,CONSTANT,devMbbiDirectSoft,"Soft Channel") +device(mbbo,CONSTANT,devMbboSoft,"Soft Channel") +device(mbboDirect,CONSTANT,devMbboDirectSoft,"Soft Channel") +device(stringin,CONSTANT,devSiSoft,"Soft Channel") +device(stringout,CONSTANT,devSoSoft,"Soft Channel") +device(subArray,CONSTANT,devSASoft,"Soft Channel") +device(waveform,CONSTANT,devWfSoft,"Soft Channel") + +device(ai,CONSTANT,devAiSoftRaw,"Raw Soft Channel") +device(ao,CONSTANT,devAoSoftRaw,"Raw Soft Channel") +device(bi,CONSTANT,devBiSoftRaw,"Raw Soft Channel") +device(bo,CONSTANT,devBoSoftRaw,"Raw Soft Channel") +device(mbbi,CONSTANT,devMbbiSoftRaw,"Raw Soft Channel") +device(mbbiDirect,CONSTANT,devMbbiDirectSoftRaw,"Raw Soft Channel") +device(mbbo,CONSTANT,devMbboSoftRaw,"Raw Soft Channel") +device(mbboDirect,CONSTANT,devMbboDirectSoftRaw,"Raw Soft Channel") + +device(ao,CONSTANT,devAoSoftCallback,"Async Soft Channel") +device(bo,CONSTANT,devBoSoftCallback,"Async Soft Channel") +device(calcout,CONSTANT,devCalcoutSoftCallback,"Async Soft Channel") +device(longout,CONSTANT,devLoSoftCallback,"Async Soft Channel") +device(mbbo,CONSTANT,devMbboSoftCallback,"Async Soft Channel") +device(mbboDirect,CONSTANT,devMbboDirectSoftCallback,"Async Soft Channel") +device(stringout,CONSTANT,devSoSoftCallback,"Async Soft Channel") + +device(ai, INST_IO,devTimestampAI,"Soft Timestamp") +device(stringin,INST_IO,devTimestampSI,"Soft Timestamp") + +device(ai, INST_IO,devAiGeneralTime,"General Time") +device(bo, INST_IO,devBoGeneralTime,"General Time") +device(longin, INST_IO,devLiGeneralTime,"General Time") +device(stringin,INST_IO,devSiGeneralTime,"General Time") + +device(stringout,INST_IO,devSoStdio,"stdio") diff --git a/src/dev/softDev/devTimestamp.c b/src/dev/softDev/devTimestamp.c new file mode 100644 index 000000000..497c05cfd --- /dev/null +++ b/src/dev/softDev/devTimestamp.c @@ -0,0 +1,78 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Device support for EPICS time stamps + * + * Original Author: Eric Norum + */ + +#include "dbDefs.h" +#include "epicsTime.h" +#include "alarm.h" +#include "devSup.h" +#include "recGbl.h" +#include "epicsExport.h" + +#include "aiRecord.h" +#include "stringinRecord.h" + + +/* Extended device support to allow INP field changes */ + +static long initAllow(int pass) { + if (pass == 0) devExtend(&devSoft_DSXT); + return 0; +} + + +/* ai record */ + +static long read_ai(aiRecord *prec) +{ + recGblGetTimeStamp(prec); + prec->val = prec->time.secPastEpoch + (double)prec->time.nsec * 1e-9; + prec->udf = FALSE; + return 2; +} + +struct { + dset common; + DEVSUPFUN read_write; + DEVSUPFUN special_linconv; +} devTimestampAI = { + {6, NULL, initAllow, NULL, NULL}, read_ai, NULL +}; +epicsExportAddress(dset, devTimestampAI); + + +/* stringin record */ + +static long read_stringin (stringinRecord *prec) +{ + int len; + + recGblGetTimeStamp(prec); + len = epicsTimeToStrftime(prec->val, sizeof prec->val, + prec->inp.value.instio.string, &prec->time); + if (len >= sizeof prec->val) { + prec->udf = TRUE; + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return -1; + } + prec->udf = FALSE; + return 0; +} + +struct { + dset common; + DEVSUPFUN read_stringin; +} devTimestampSI = { + {5, NULL, initAllow, NULL, NULL}, read_stringin +}; +epicsExportAddress(dset, devTimestampSI); diff --git a/src/dev/softDev/devWfSoft.c b/src/dev/softDev/devWfSoft.c new file mode 100644 index 000000000..ad70f0b57 --- /dev/null +++ b/src/dev/softDev/devWfSoft.c @@ -0,0 +1,81 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Original Authors: Bob Dalesio and Marty Kraimer + * Date: 6-1-90 + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "devSup.h" +#include "waveformRecord.h" +#include "epicsExport.h" + +/* Create the dset for devWfSoft */ +static long init_record(waveformRecord *prec); +static long read_wf(waveformRecord *prec); + +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_wf; +} devWfSoft = { + 5, + NULL, + NULL, + init_record, + NULL, + read_wf +}; +epicsExportAddress(dset, devWfSoft); + +static long init_record(waveformRecord *prec) +{ + /* INP must be CONSTANT, PV_LINK, DB_LINK or CA_LINK*/ + switch (prec->inp.type) { + case CONSTANT: + prec->nord = 0; + break; + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + default: + recGblRecordError(S_db_badField, (void *)prec, + "devWfSoft (init_record) Illegal INP field"); + return(S_db_badField); + } + return 0; +} + +static long read_wf(waveformRecord *prec) +{ + long nRequest = prec->nelm; + + dbGetLink(&prec->inp, prec->ftvl, prec->bptr, 0, &nRequest); + if (nRequest > 0) { + prec->nord = nRequest; + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + } + + return 0; +} diff --git a/src/dev/softDev/softDevIoc.rc b/src/dev/softDev/softDevIoc.rc new file mode 100755 index 000000000..addbe1708 --- /dev/null +++ b/src/dev/softDev/softDevIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Soft Device Support Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Soft Device Support Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "softDevIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "softDevIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/dev/testDev/Makefile b/src/dev/testDev/Makefile new file mode 100644 index 000000000..19c30b242 --- /dev/null +++ b/src/dev/testDev/Makefile @@ -0,0 +1,27 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../.. + +include $(TOP)/configure/CONFIG + +DBD += devTestAsyn.dbd + +LIBSRCS += devHistogramTestAsyn.c +LIBSRCS += devTestAsyn.c + +LIBRARY_IOC = testDevIoc +testDevIoc_LIBS += miscIoc recIoc asIoc dbIoc registryIoc dbStaticIoc ca Com +testDevIoc_RCS = testDevIoc.rc + +# For R3.13 compatability +ifeq ($(strip $(COMPAT_313)),YES) +OBJS_vxWorks = $(LIBSRCS:%.c=%) +endif + +include $(TOP)/configure/RULES diff --git a/src/dev/testDev/devHistogramTestAsyn.c b/src/dev/testDev/devHistogramTestAsyn.c new file mode 100644 index 000000000..4022f2d67 --- /dev/null +++ b/src/dev/testDev/devHistogramTestAsyn.c @@ -0,0 +1,107 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* devHistogramTestAsyn.c */ +/* base/src/dev Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Janet Anderson + * Date: 07/02/91 + */ +#include +#include +#include +#include + +#include "alarm.h" +#include "callback.h" +#include "cvtTable.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "recSup.h" +#include "devSup.h" +#include "link.h" +#include "dbCommon.h" +#include "histogramRecord.h" +#include "epicsExport.h" + +/* Create the dset for devHistogramTestAsyn */ +static long init_record(struct histogramRecord *phistogram); +static long read_histogram(struct histogramRecord *phistogram); +struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_histogram; + DEVSUPFUN special_linconv; +}devHistogramTestAsyn={ + 6, + NULL, + NULL, + init_record, + NULL, + read_histogram, + NULL +}; +epicsExportAddress(dset,devHistogramTestAsyn); + +static long init_record(struct histogramRecord *prec) +{ + CALLBACK *pcallback; + + /* histogram.svl must be a CONSTANT*/ + switch (prec->svl.type) { + case (CONSTANT) : + pcallback = (CALLBACK *)(calloc(1,sizeof(CALLBACK))); + prec->dpvt = (void *)pcallback; + if(recGblInitConstantLink(&prec->svl,DBF_DOUBLE,&prec->sgnl)) + prec->udf = FALSE; + break; + default : + recGblRecordError(S_db_badField,(void *)prec, + "devHistogramTestAsyn (init_record) Illegal SVL field"); + prec->pact=TRUE; + return(S_db_badField); + } + return(0); +} + +static long read_histogram(struct histogramRecord *prec) +{ + CALLBACK *pcallback=(CALLBACK *)(prec->dpvt); + + /* histogram.svl must be a CONSTANT*/ + switch (prec->svl.type) { + case (CONSTANT) : + if(prec->pact) { + printf("Completed asynchronous processing: %s\n", + prec->name); + return(0); /*add count*/ + } else { + if(prec->disv<=0) return(2); + printf("Starting asynchronous processing: %s\n", + prec->name); + prec->pact=TRUE; + callbackRequestProcessCallbackDelayed( + pcallback,prec->prio,prec, + (double)prec->disv); + return(0); + } + default : + if(recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM)){ + if(prec->stat!=SOFT_ALARM) { + recGblRecordError(S_db_badField,(void *)prec, + "devHistogramTestAsyn (read_histogram) Illegal SVL field"); + } + } + } + return(0); +} diff --git a/src/dev/testDev/devTestAsyn.c b/src/dev/testDev/devTestAsyn.c new file mode 100644 index 000000000..a1b21e56b --- /dev/null +++ b/src/dev/testDev/devTestAsyn.c @@ -0,0 +1,75 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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 */ + +/* Device Support for testing asynchronous processing + * + * This is a universal device support, it should work with + * almost any record type, but it doesn't actually do any + * I/O operations so it's not particularly useful other than + * for testing asynchronous record processing. + */ + +#include +#include + +#include "callback.h" +#include "devSup.h" +#include "dbCommon.h" +#include "epicsExport.h" + +static long addRec(struct dbCommon *prec) +{ + prec->dpvt = calloc(1, sizeof(CALLBACK)); + return !prec->dpvt; +} + +static long delRec(struct dbCommon *prec) +{ + if (prec->dpvt) { + callbackCancelDelayed((CALLBACK *)prec->dpvt); + free(prec->dpvt); + } + return 0; +} + +static struct dsxt dsxtTestAsyn = { + addRec, delRec +}; + +static long init(int pass) +{ + if (!pass) devExtend(&dsxtTestAsyn); + return 0; +} + +static long process(struct dbCommon *prec) +{ + if (prec->pact) { + printf("Completed asynchronous processing: %s\n", prec->name); + return 0; + } + if (prec->disv <= 0) return 2; + + printf("Starting asynchronous processing: %s\n", prec->name); + prec->pact = TRUE; + if (prec->dpvt) { + callbackRequestProcessCallbackDelayed((CALLBACK *)prec->dpvt, + prec->prio, prec, (double)prec->disv); + } + return 0; +} + +/* Create the dset for devTestAsyn */ +struct { + dset common; + DEVSUPFUN read; + DEVSUPFUN misc; +} devTestAsyn = { + {6, NULL, init, NULL, NULL}, process, NULL +}; +epicsExportAddress(dset, devTestAsyn); diff --git a/src/dev/testDev/devTestAsyn.dbd b/src/dev/testDev/devTestAsyn.dbd new file mode 100644 index 000000000..499cad763 --- /dev/null +++ b/src/dev/testDev/devTestAsyn.dbd @@ -0,0 +1,20 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +device(ai,CONSTANT,devTestAsyn,"Test Asyn") +device(aai,CONSTANT,devTestAsyn,"Test Asyn") +device(ao,CONSTANT,devTestAsyn,"Test Asyn") +device(aao,CONSTANT,devTestAsyn,"Test Asyn") +device(bi,CONSTANT,devTestAsyn,"Test Asyn") +device(bo,CONSTANT,devTestAsyn,"Test Asyn") +device(calcout,CONSTANT,devTestAsyn,"Test Asyn") +device(event,CONSTANT,devTestAsyn,"Test Asyn") +device(longin,CONSTANT,devTestAsyn,"Test Asyn") +device(longout,CONSTANT,devTestAsyn,"Test Asyn") +device(mbbi,CONSTANT,devTestAsyn,"Test Asyn") +device(mbbiDirect,CONSTANT,devTestAsyn,"Test Asyn") +device(mbbo,CONSTANT,devTestAsyn,"Test Asyn") +device(mbboDirect,CONSTANT,devTestAsyn,"Test Asyn") +device(stringin,CONSTANT,devTestAsyn,"Test Asyn") +device(stringout,CONSTANT,devTestAsyn,"Test Asyn") +device(subArray,CONSTANT,devTestAsyn,"Test Asyn") +device(waveform,CONSTANT,devTestAsyn,"Test Asyn") diff --git a/src/dev/testDev/testDevIoc.rc b/src/dev/testDev/testDevIoc.rc new file mode 100755 index 000000000..04c9dbdae --- /dev/null +++ b/src/dev/testDev/testDevIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Test Device Support Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Test Device Support Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "testDevIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "testDevIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/excas/Makefile b/src/excas/Makefile new file mode 100644 index 000000000..0d4d51638 --- /dev/null +++ b/src/excas/Makefile @@ -0,0 +1,37 @@ +#************************************************************************* +# 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) + +SRC_DIRS += $(TOP)/src/makeBaseApp/top/caServerApp + +# +# Added ws2_32 winmm user32 for the non-dll build +# +SYS_PROD_LIBS_WIN32 += ws2_32 advapi32 user32 + + +PROD_HOST = excas + +excas_SRCS += main.cc +excas_SRCS += exServer.cc +excas_SRCS += exPV.cc +excas_SRCS += exVectorPV.cc +excas_SRCS += exScalarPV.cc +excas_SRCS += exAsyncPV.cc +excas_SRCS += exChannel.cc + +include $(TOP)/configure/RULES + + diff --git a/src/gdd/Makefile b/src/gdd/Makefile new file mode 100644 index 000000000..e6c58c1d2 --- /dev/null +++ b/src/gdd/Makefile @@ -0,0 +1,97 @@ +#************************************************************************* +# 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP=../.. + +include $(TOP)/configure/CONFIG + +INC += gdd.h +INC += gddI.h +INC += gddContainer.h +INC += gddContainerI.h +INC += gddArray.h +INC += gddArrayI.h +INC += gddScalar.h +INC += gddScalarI.h +INC += gddNewDel.h +INC += gddUtils.h +INC += gddUtilsI.h +INC += gddErrorCodes.h +INC += aitTypes.h +INC += aitConvert.h +INC += aitHelpers.h +INC += dbMapper.h +INC += gddAppTable.h +INC += gddAppFuncTable.h +INC += smartGDDPointer.h +INC += gddApps.h +INC += gddEnumStringTable.h + +HTMLS += gdd.html +HTMLS += gddref.html +HTMLS += gddref2.html + +AITGENSRCS := gdd.cc gddTest.cc gddAppTable.cc gddNewDel.cc \ + gddAppDefs.cc aitTypes.c aitConvert.cc aitHelpers.cc \ + gddArray.cc gddContainer.cc gddErrorCodes.cc gddUtils.cc \ + gddEnumStringTable.cc + +gdd_SRCS := gdd.cc gddTest.cc gddAppTable.cc gddNewDel.cc \ + gddAppDefs.cc aitTypes.c aitConvert.cc aitHelpers.cc dbMapper.cc \ + gddArray.cc gddContainer.cc gddErrorCodes.cc gddUtils.cc \ + gddEnumStringTable.cc + +LIBRARY = gdd + +gdd_LIBS = Com + +gdd_RCS = gdd.rc + +PROD_HOST = aitGen genApps +genApps_SRCS = genApps.cc $(AITGENSRCS) +aitGen_SRCS = aitTypes.c aitGen.c +PROD_LIBS = Com +genApps_SYS_LIBS_WIN32 = ws2_32 + +# aitGen.c doesn't compile for linux-arm at -O3 when using gcc-3.4.5 +aitGen_CFLAGS_linux-arm = -O2 + +# Switch off potentially bogus warnings on HPUX 11 - detailed warning +# suppression in the source code would be too much effort with respect +# to gdd's limited future + +HPWARNFLAGS_NO = +W361 +W392 +W655 +W749 +W818 +W930 +USR_CXXFLAGS_hpux = $(HPWARNFLAGS_$(GNU)) + +include $(TOP)/configure/RULES + +# cannot generate these dependencies automatically +# +# Problem: Some dependencies are include files that may +# not have been installed if we are building for +# the first time +# -> use explicit reference to the uninstalled files in '..' + +aitConvert$(OBJ): $(COMMON_DIR)/aitConvertGenerated.cc + +dbMapper$(OBJ): $(COMMON_DIR)/gddApps.h + +# Rules for generated files + +$(COMMON_DIR)/aitConvertGenerated.cc: $(TOOLS)/aitGen$(HOSTEXE) + $(TOOLS)/aitGen$(HOSTEXE) $@ + +$(COMMON_DIR)/gddApps.h : $(TOOLS)/genApps$(HOSTEXE) + $(TOOLS)/genApps$(HOSTEXE) $@ + +clean:: + @$(RM) $(COMMON_DIR)/aitConvertGenerated.cc + +# EOF base/src/gdd/Makefile + diff --git a/src/gdd/README b/src/gdd/README new file mode 100644 index 000000000..693d6a7f3 --- /dev/null +++ b/src/gdd/README @@ -0,0 +1,160 @@ +# +# Author: Jim Kowalkowski +# Date: 2/96 +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# + +Some Notes: + +****** +The following function described in gddAppTable.h and defined in gddAppDefs.cc: + + gddApplicationTypeTable* gddGenerateApplicationTypeTable(void); + +is designed to be called in an application to create the type table +with a default set of attributes already registered. The current mechanism +for generating the default registered attributes is the C++ code in +gddAppDefs.cc. See this file for a list of registered attributes. + +Creating am application type table using new will perform the same function. + +****** +The gddCleanUp object will automatically clean up the free list storage +when it is destructed. There should only be one of these in an application. +It is not required. + +****** +To build for vxWorks, define an environment variable "VX_BUILD=vw" and type +make. It will build for the HOST_ARCH architecture and then vxWorks. + +You must have gcc/g++ 2.5.8 or later available to cross compile to 68k object +code and vxWorks. You can get the gcc 2.5.8 package from the HiDEOS home +page along with instructions on building it. + +The vxldscript.MRI file instructs the gnu loader on how to propare +object files to load under vxWorks. + +You must use the ldpp program under vxWorks shell to load g++ generated +object modules. The ldpp program is available in the EPICS base package. +Contact me if you want a copy of it. + +It is really not difficult to get g++ object files to load under vxWorks +or to get the g++ compiled installed and running. The makefile is set up +to handle builds for vxWorks. + +****** +aitTypes.h: Definitions of the architecture independant types. +aitTypes.c: definitions of several lookup tables +aitConvert.h: conversion table description,byte ordering routines +aitConvert.c: conversion information not generated +aitGen.c: creates the general conversion functions +aitConvertGenerated.c: file of generated conversion functions + +dbMapper.cc: mappings from DBR types to ait types and GDDs +dbMapper.h: mappings from DBR types to ait types and GDDs + +gddApps.h: + This file contains "#define" statements for quick indexing into + managed containers. Generated by program genApps.cc. + +******* +Still needed: + +1) AppTable method to map application type to offset (index) into a + managed container. +* mostly complete + +2) Function to map DBRxxxx types to Application types and back. +* still working on mapping method + +3) Methods for managing network byte order conversions in gdd class. + +4) Method to extract data (and bounds information) from a gdd into a + user's buffer. + +6) #defines to remove extra checks and make the programs run faster. + +************ 6/13/96 ************* + +ref_cnt should be an unsigned short instead of char + +menus need to be fixed - use aitFixedString or aitString + +get/operator=() should be smarter, do conversion if types do not match + +************ 6/20/96 ************** + +fix the flatten functions so they work properly with aitString and fixed +string + +fix the aitString class, add the install string method and change others + +*********** 6/21/96 ************ + +fixe the aitString::compact function, and all other stuff that works with +string info so that if the string that the aitString is holding is NULL, +then the system will act correctly. The flatten functions have trouble +with this when the described string is NULL. + +*********** 6/24/96 ************* + +still need to fix the transfer of aitString data into a pre-made gdd. +If only array of 5 aitString is placed into a pre-made array of 16, +then the last 11 should be initialized to NULLs or something, same for +fixed string - especially the fixed string + +************** 6/26/96 ************ + +aitString:copy() still has troubles. Need to correct the +aitConvertStringFloat64() and others. How are they supposed to work? +Should they be calling aitString::installString() instead of copy()? Yes, +that is the answer. In the convert functions, the temp variable +used to hold a value (character value) is always a stack variable and +needs to be copied. Maybe the temp char variable should be copied over +the existing string if it will fit. No. + +************** 8/28/96 **************** + +network/host byte ordering issue: + +Change isNetworkByteOrder() in class gdd to: + isLocalDataFormat() - true if data format is local host format + isNetworkDataFormat() - true if data format is network format + + Both the above can be true at the same time if local host data format + is the same as network data format + +Add method to class gdd: + markLocalDataFormat() + markNotLocalDataFormat() + +Modify putRef() functions of gdd class: + add third argument to specify the byte order that data is in. + default mode to local data format. this turns out to be ugly + because the second argument (gddDestructor*) defaults to NULL. + But this is probably OK, since this will not be used often. + +Modify getRef() functions of gdd class: + add another argument that specifies the data format the user desires. + default to local data format, first access to an array that is not + in the correct format will cause the entire array to be converted + to the desired format. + +putConvert() and getConvert() notes: + getConvert will always return values in local data format + putConvert will always take local data format values as arguments + +put() and get() notes: + get always returns data in local data format + put always takes data in local data format + +Modify dbMapper.cc: + add dbMapperToDbrMode({to_network_byte_order,keep_in_local_order}) + change all function to honor this mode setting + any gdd converted to a dbr type could be change to network byte order + depending on this mode. + +Jim + + diff --git a/src/gdd/aitConvert.cc b/src/gdd/aitConvert.cc new file mode 100644 index 000000000..18c02892c --- /dev/null +++ b/src/gdd/aitConvert.cc @@ -0,0 +1,451 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#define AIT_CONVERT_SOURCE 1 + +#include +#include +#include +#include +#include "epicsStdio.h" +#include "cvtFast.h" + +#define epicsExportSharedSymbols +#include "aitConvert.h" + +int aitNoConvert(void* /*dest*/,const void* /*src*/,aitIndex /*count*/, const gddEnumStringTable *) {return -1;} + +#ifdef AIT_CONVERT +#undef AIT_CONVERT +#endif +#ifdef AIT_TO_NET_CONVERT +#undef AIT_TO_NET_CONVERT +#endif +#ifdef AIT_FROM_NET_CONVERT +#undef AIT_FROM_NET_CONVERT +#endif + +/* put the fixed conversion functions here (ones not generated) */ + +bool getStringAsDouble ( const char * pString, + const gddEnumStringTable * pEST, double & result ) +{ + if ( ! pString ) { + return false; + } + double ftmp; + unsigned itmp; + if ( pEST && pEST->getIndex ( pString, itmp ) ) { + ftmp = itmp; + } + else { + int j = epicsScanDouble( pString, &ftmp ); + if ( j != 1 ) { + j = sscanf ( pString,"%x", &itmp ); + if ( j == 1 ) { + ftmp = itmp; + } + else { + return false; + } + } + } + result = ftmp; + return true; +} + +bool putDoubleToString ( + const double in, const gddEnumStringTable * pEST, + char * pString, size_t strSize ) +{ + if ( strSize <= 1u ) { + return false; + } + if ( pEST && in >= 0 && in <= UINT_MAX ) { + unsigned utmp = static_cast < unsigned > ( in ); + pEST->getString ( utmp, pString, strSize ); + if ( pString[0] != '\0' ) { + return true; + } + } +#if 1 + bool cvtDoubleToStringInRange = + ( in < 1.e4 && in > 1.e-4 ) || + ( in > -1.e4 && in < -1.e-4 ) || + in == 0.0; + // conservative + static const unsigned cvtDoubleToStringSizeMax = 15; + int nChar; + if ( cvtDoubleToStringInRange && + strSize > cvtDoubleToStringSizeMax ) { + nChar = cvtDoubleToString ( in, pString, 4 ); + } + else { + nChar = epicsSnprintf ( + pString, strSize-1, "%g", in ); + } + if ( nChar < 1 ) { + return false; + } + assert ( size_t(nChar) < strSize ); +#else + int nChar = epicsSnprintf ( + pString, strSize-1, "%g", in ); + if ( nChar <= 0 ) { + return false; + } +#endif + size_t nCharU = static_cast < size_t > ( nChar ); + nChar = epicsMin ( nCharU, strSize-1 ) + 1; + memset ( &pString[nChar], '\0', strSize - nChar ); + return true; +} + +/* ------- extra string conversion functions --------- */ +static int aitConvertStringString(void* d,const void* s, + aitIndex c, const gddEnumStringTable *) +{ + // does not work - need to be fixed + aitIndex i; + aitString *in=(aitString*)s, *out=(aitString*)d; + + for(i=0;inumberOfStrings() ) { + unsigned nChar = pEnumStringTable->getStringLength ( in[i] ); + if ( nChar < static_cast ( INT_MAX - status ) ) { + out[i].copy( pEnumStringTable->getString ( in[i] ), nChar ); + status += static_cast ( nChar );; + } + else { + return -1; + } + } + else { + char temp[AIT_FIXED_STRING_SIZE]; + int tmpStatus = sprintf ( temp, "%hu", in[i] ); + if ( tmpStatus >= 0 && tmpStatus < INT_MAX - status ) { + out[i].copy ( temp, static_cast < unsigned > ( tmpStatus ) ); + status += tmpStatus; + } + else { + return -1; + } + } + } + return status; +} + +#ifdef AIT_NEED_BYTE_SWAP +static int aitConvertToNetStringEnum16(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertStringEnum16(d,s,c,pEnumStringTable); +} + +static int aitConvertFromNetStringEnum16(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertStringEnum16(d,s,c,pEnumStringTable); +} +#endif + +static int aitConvertFixedStringEnum16(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + aitIndex i; + int status=0; + aitFixedString* out=(aitFixedString*)d; + aitEnum16* in=(aitEnum16*)s; + for (i=0;inumberOfStrings() ) { + unsigned nChar = pEnumStringTable->getStringLength ( in[i] ); + if ( nChar < static_cast < unsigned > ( INT_MAX - status ) ) { + pEnumStringTable->getString ( + in[i], + out[i].fixed_string, + sizeof( out[i].fixed_string ) ); + status += static_cast < int > ( nChar ); + } + else { + return -1; + } + } + else { + int tmpStatus = sprintf ( out[i].fixed_string, "%hu", in[i] ); + if ( tmpStatus > 0 && tmpStatus < INT_MAX - status ) { + status += tmpStatus; + } + else { + return -1; + } + } + } + return status; +} + +#ifdef AIT_NEED_BYTE_SWAP +static int aitConvertToNetFixedStringEnum16(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertFixedStringEnum16(d,s,c,pEnumStringTable); +} + +static int aitConvertFromNetFixedStringEnum16(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertFixedStringEnum16(d,s,c,pEnumStringTable); +} +#endif + +static int aitConvertEnum16FixedString (void* d,const void* s,aitIndex c, + const gddEnumStringTable *pEnumStringTable) +{ + aitIndex i; + int status = 0; + aitEnum16* out = (aitEnum16*)d; + aitFixedString* in = (aitFixedString*)s; + aitEnum16 choice, nChoices; + + // + // convert only after a range check + // + if ( pEnumStringTable ) { + assert (pEnumStringTable->numberOfStrings()<=0xffff); + nChoices = static_cast(pEnumStringTable->numberOfStrings()); + } + else { + nChoices = 0; + } + + for (i=0;igetString(choice), in[i].fixed_string)==0) { + out[i] = choice; + status += sizeof(out[i]); + break; + } + } + // + // if no string matches then look for a numeric match + // + if (choice>=nChoices) { + int temp; + if ( sscanf ( in[i].fixed_string,"%i", &temp ) == 1 ) { + if ( temp >= 0 && temp < nChoices ) { + out[i] = (aitUint16) temp; + status += sizeof(out[i]); + } + else { + // + // no match, return an error + // + return -1; + } + } + else { + return -1; + } + } + } + return status; +} + +#ifdef AIT_NEED_BYTE_SWAP +static int aitConvertToNetEnum16FixedString(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertEnum16FixedString(d,s,c,pEnumStringTable); +} + +static int aitConvertFromNetEnum16FixedString(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertEnum16FixedString(d,s,c,pEnumStringTable); +} +#endif + +static int aitConvertEnum16String (void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + aitIndex i; + int status = 0; + aitEnum16* out = (aitEnum16*)d; + aitString* in = (aitString*)s; + aitEnum16 choice, nChoices; + + // + // convert only after a range check + // + if ( pEnumStringTable ) { + assert (pEnumStringTable->numberOfStrings()<=0xffff); + nChoices = static_cast(pEnumStringTable->numberOfStrings()); + } + else { + nChoices = 0u; + } + + for (i=0;igetString(choice), in[i].string())==0) { + out[i] = choice; + status += sizeof(out[i]); + break; + } + } + // + // if no string matches then look for a numeric match + // + if (choice>=nChoices) { + int temp; + if ( sscanf ( in[i].string(),"%i", &temp ) == 1 ) { + if ( temp >= 0 && temp < nChoices ) { + out[i] = (aitUint16) temp; + status += sizeof(out[i]); + } + else { + // + // no match, return an error + // + return -1; + } + } + else { + return -1; + } + } + } + return status; +} + +#ifdef AIT_NEED_BYTE_SWAP +static int aitConvertToNetEnum16String(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertEnum16String(d,s,c,pEnumStringTable); +} + +static int aitConvertFromNetEnum16String(void* d,const void* s, + aitIndex c, const gddEnumStringTable *pEnumStringTable) +{ + return aitConvertEnum16String(d,s,c,pEnumStringTable); +} +#endif + +#define AIT_CONVERT 1 +#include "aitConvertGenerated.cc" +#undef AIT_CONVERT + +/* include the network byte order functions if needed */ +#ifdef AIT_NEED_BYTE_SWAP + +#define AIT_TO_NET_CONVERT 1 +#include "aitConvertGenerated.cc" +#undef AIT_TO_NET_CONVERT + +#define AIT_FROM_NET_CONVERT 1 +#include "aitConvertGenerated.cc" +#undef AIT_FROM_NET_CONVERT + +#endif + diff --git a/src/gdd/aitConvert.h b/src/gdd/aitConvert.h new file mode 100644 index 000000000..898504568 --- /dev/null +++ b/src/gdd/aitConvert.h @@ -0,0 +1,172 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef AIT_CONVERT_H__ +#define AIT_CONVERT_H__ + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#include + +#if defined(_MSC_VER) && _MSC_VER < 1200 +# pragma warning ( push ) +# pragma warning ( disable:4786 ) +#endif + +#include "shareLib.h" +#include "osiSock.h" + +#include "aitTypes.h" +#include "gddEnumStringTable.h" + +#if defined(__i386) || defined(i386) +#define AIT_NEED_BYTE_SWAP 1 +#endif + +#ifdef AIT_NEED_BYTE_SWAP +#define aitLocalNetworkDataFormatSame 0 +#else +#define aitLocalNetworkDataFormatSame 1 +#endif + +typedef enum { aitLocalDataFormat=0, aitNetworkDataFormat } aitDataFormat; + +/* all conversion functions have this prototype */ +typedef int (*aitFunc)(void* dest,const void* src,aitIndex count,const gddEnumStringTable *pEnumStringTable); + +#ifdef __cplusplus +extern "C" { +#endif + +/* main conversion table */ +epicsShareExtern aitFunc aitConvertTable[aitTotal][aitTotal]; +/* do not make conversion table if not needed */ +#ifdef AIT_NEED_BYTE_SWAP +epicsShareExtern aitFunc aitConvertToNetTable[aitTotal][aitTotal]; +epicsShareExtern aitFunc aitConvertFromNetTable[aitTotal][aitTotal]; +#else +#define aitConvertToNetTable aitConvertTable +#define aitConvertFromNetTable aitConvertTable +#endif /* AIT_NEED_BYTE_SWAP */ + +#ifdef __cplusplus +} +#endif + +/* ---------- convenience routines for performing conversions ---------- */ + +#if defined(__cplusplus) + +inline int aitConvert(aitEnum desttype, void* dest, + aitEnum srctype, const void* src, aitIndex count, + const gddEnumStringTable *pEnumStringTable = 0 ) + { return (*aitConvertTable[desttype][srctype])(dest,src,count,pEnumStringTable); } + +inline int aitConvertToNet(aitEnum desttype, void* dest, + aitEnum srctype, const void* src, aitIndex count, + const gddEnumStringTable *pEnumStringTable = 0 ) + { return (*aitConvertToNetTable[desttype][srctype])(dest,src,count,pEnumStringTable); } + +inline int aitConvertFromNet(aitEnum desttype, void* dest, + aitEnum srctype, const void* src, aitIndex count, + const gddEnumStringTable *pEnumStringTable = 0 ) + { return (*aitConvertFromNetTable[desttype][srctype])(dest,src,count,pEnumStringTable); } + +#else + +#define aitConvert(DESTTYPE,DEST,SRCTYPE,SRC,COUNT) \ + (*aitConvertTable[DESTTYPE][SRCTYPE])(DEST,SRC,COUNT) + +#define aitConvertToNet(DESTTYPE,DEST,SRCTYPE,SRC,COUNT) \ + (*aitConvertToNetTable[DESTTYPE][SRCTYPE])(DEST,SRC,COUNT) + +#define aitConvertFromNet(DESTTYPE,DEST,SRCTYPE,SRC,COUNT) \ + (*aitConvertFromNetTable[DESTTYPE][SRCTYPE])(DEST,SRC,COUNT) + +#endif + +/* ----------- byte order conversion definitions ------------ */ + +#define aitUint64 aitUint32 + +#if defined(__cplusplus) + +inline void aitToNetOrder16(aitUint16* dest,aitUint16* src) + { *dest=htons(*src); } +inline void aitToNetOrder32(aitUint32* dest,aitUint32* src) + { *dest=htonl(*src); } +inline void aitFromNetOrder16(aitUint16* dest,aitUint16* src) + { *dest=ntohs(*src); } +inline void aitFromNetOrder32(aitUint32* dest,aitUint32* src) + { *dest=ntohl(*src); } +inline void aitToNetOrder64(aitUint64* dest, aitUint64* src) +{ + aitUint32 ait_temp_var_; + ait_temp_var_=(aitUint32)htonl(src[1]); // X aCC 392 + dest[1]=(aitUint32)htonl(src[0]); // X aCC 392 + dest[0]=ait_temp_var_; +} +inline void aitFromNetOrder64(aitUint64* dest, aitUint64* src) +{ + aitUint32 ait_temp_var_; + ait_temp_var_=(aitUint32)ntohl(src[1]); // X aCC 392 + dest[1]=(aitUint32)ntohl(src[0]); // X aCC 392 + dest[0]=ait_temp_var_; +} + +#else + +/* !The following generate code! */ +#define aitToNetOrder16(dest,src) (*(dest)=htons(*(src))) +#define aitToNetOrder32(dest,src) (*(dest)=htonl(*(src))) +#define aitFromNetOrder16(dest,src) (*(dest)=ntohs(*(src))) +#define aitFromNetOrder32(dest,src) (*(dest)=ntohl(*(src))) + +/* be careful using these because of brackets, these should be functions */ +#define aitToNetOrder64(dest,src) \ +{ \ + aitUint32 ait_temp_var_; \ + ait_temp_var_=(aitUint32)htonl((src)[1]); \ + (dest)[1]=(aitUint32)htonl((src)[0]); \ + (dest)[0]=ait_temp_var_; \ +} +#define aitFromNetOrder64(dest,src) \ +{ \ + aitUint32 ait_temp_var_; \ + ait_temp_var_=(aitUint32)ntohl((src)[1]); \ + (dest)[1]=(aitUint32)ntohl((src)[0]); \ + (dest)[0]=ait_temp_var_; \ +} + +#endif /* __cpluspluc */ + +#define aitToNetFloat64 aitToNetOrder64 +#define aitToNetFloat32 aitToNetOrder32 +#define aitFromNetFloat64 aitFromNetOrder64 +#define aitFromNetFloat32 aitFromNetOrder32 + +bool getStringAsDouble ( const char * pString, + const gddEnumStringTable *pEST, double & result ); + +bool putDoubleToString ( + const double in, const gddEnumStringTable * pEST, + char * pString, size_t strSize ); + +#if defined ( _MSC_VER ) && _MSC_VER < 1200 +# pragma warning ( pop ) +#endif + +#endif + diff --git a/src/gdd/aitGen.c b/src/gdd/aitGen.c new file mode 100644 index 000000000..825d4d46b --- /dev/null +++ b/src/gdd/aitGen.c @@ -0,0 +1,443 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "aitTypes.h" + +void initMinMaxAIT ( void ); + +/* + * maximum value for each type - joh + */ +double aitMax[aitTotal]; + +/* + * minimum value for each ait type - joh + */ +double aitMin[aitTotal]; + +/* + Still need to generate to "FromNet" matrix and rename the "Net" conversions + to "ToNet". +*/ + +void MakeNormalFunc(int i,int j,int k); +void MakeToFunc(int i,int j,int k); +void MakeFromFunc(int i,int j,int k); +void GenName(int i,int j,int k); +void GenVars(int i,int j); +void MakeStringFuncFrom(int i,int j,int k); +void MakeStringFuncTo(int i,int j,int k); +void MakeFStringFuncFrom(int i,int j,int k); +void MakeFStringFuncTo(int i,int j,int k); + +#define pr fprintf + +#define AIT_TO_NET 0 +#define AIT_FROM_NET 1 +#define AIT_NORMAL 2 + +#define AIT_SWAP_NONE 0 +#define AIT_SWAP_16 1 +#define AIT_SWAP_32 2 +#define AIT_SWAP_64 3 + +static const char* table_type[] = { + "aitConvertToNet", + "aitConvertFromNet", + "aitConvert" }; +static const char* table_def[] = { + "#if defined(AIT_TO_NET_CONVERT)", + "#elif defined(AIT_FROM_NET_CONVERT)", + "#else /* AIT_CONVERT */" }; + +static FILE *dfd; + +/* + * maximum, minimum value for each ait type - joh + */ +void initMinMaxAIT (void) +{ + unsigned i; + + for ( i = 0u; i < sizeof ( aitMax ) / sizeof ( aitMax [ 0 ] ); i++ ) { + aitMax [ i ] = -DBL_MAX; + } + + for ( i = 0u; i < sizeof ( aitMax ) / sizeof ( aitMax [ 0 ] ); i++ ) { + aitMin [ i ] = DBL_MAX; + } + + aitMax [ aitEnumInt8 ] = SCHAR_MAX; + aitMax [ aitEnumUint8 ] = UCHAR_MAX; + aitMax [ aitEnumInt16 ] = SHRT_MAX; + aitMax [ aitEnumUint16 ] = USHRT_MAX; + aitMax [ aitEnumEnum16 ] = USHRT_MAX; + aitMax [ aitEnumInt32 ] = INT_MAX; + aitMax [ aitEnumUint32 ] = UINT_MAX; + aitMax [ aitEnumFloat32 ] = FLT_MAX; + aitMax [ aitEnumFloat64 ] = DBL_MAX; + + aitMin [ aitEnumInt8 ] = SCHAR_MIN; + aitMin [ aitEnumUint8 ] = 0u; + aitMin [ aitEnumInt16 ] = SHRT_MIN; + aitMin [ aitEnumUint16 ] = 0u; + aitMin [ aitEnumEnum16 ] = 0u; + aitMin [ aitEnumInt32 ] = INT_MIN; + aitMin [ aitEnumUint32 ] = 0u; + aitMin [ aitEnumFloat32 ] = -FLT_MAX; + aitMin [ aitEnumFloat64 ] = -DBL_MAX; +} + +int main(int argc,char* argv[]) +{ + int i,j,k; + + initMinMaxAIT (); + + if(argc<2) + { + fprintf(stderr,"You must enter a file name on command line\n"); + return -1; + } + + if((dfd=fopen(argv[1],"w"))==NULL) + { + pr(stderr,"file %s failed to open\n",argv[1]); + return -1; + } + + /* -----------------------------------------------------------------*/ + /* generate basic conversion functions */ + + pr(dfd,"\n"); + + /* generate each group - to/from/normal */ + for(k=0;k<3;k++) + { + pr(dfd,"%s\n",table_def[k]); + for(i=aitConvertAutoFirst;i<=aitConvertAutoLast;i++) + for(j=aitConvertAutoFirst;j<=aitConvertAutoLast;j++) + { + switch(k) + { + case AIT_TO_NET: MakeToFunc(i,j,k); break; + case AIT_FROM_NET: MakeFromFunc(i,j,k); break; + case AIT_NORMAL: MakeNormalFunc(i,j,k); break; + default: break; + } + } + } + pr(dfd,"#endif\n\n"); + + for(k=0;k<3;k++) + { + pr(dfd,"%s\n",table_def[k]); + for(i=aitConvertAutoFirst;i<=aitConvertAutoLast;i++) + { + if (i!=aitEnumEnum16) { + MakeStringFuncTo(i,aitEnumString,k); + MakeStringFuncFrom(aitEnumString,i,k); + MakeFStringFuncTo(i,aitEnumFixedString,k); + MakeFStringFuncFrom(aitEnumFixedString,i,k); + } + } + } + pr(dfd,"#endif\n\n"); + + /* generate the three conversion table matrices */ + for(k=0;k<3;k++) + { + pr(dfd,"%s\n",table_def[k]); + pr(dfd,"aitFunc %sTable[aitTotal][aitTotal]={\n",table_type[k]); + + /* generate network conversion matrix */ + for(i=aitFirst;i<=aitLast;i++) + { + pr(dfd," {\n"); + for(j=aitFirst;j<=aitLast;j++) + { + if(iaitConvertLast || + jaitConvertLast) + pr(dfd,"aitNoConvert"); + else + pr(dfd,"%s%s%s",table_type[k], + &(aitName[i])[3],&(aitName[j])[3]); + + if(j=%g && ftmp<=%g) {\n", + aitMin[i], aitMax[i]); + pr(dfd,"\t\t\t\tout[i] = (%s) ftmp;\n", aitName[i]); + pr(dfd,"\t\t\t}\n"); + pr(dfd,"\t\t\telse {\n"); + pr(dfd,"\t\t\t\treturn -1;\n"); + pr(dfd,"\t\t\t}\n"); + pr(dfd,"\t\t}\n"); + pr(dfd,"\t\telse {\n"); + pr(dfd,"\t\t\treturn -1;\n"); + pr(dfd,"\t\t}\n"); + pr(dfd,"\t}\n"); + pr(dfd,"\treturn (int) (sizeof(%s)*c);\n}\n", aitName[i]); +} + +void MakeFStringFuncFrom(int i,int j,int k) +{ + /* assumes that void* d in an array of char pointers of length c */ + /* takes numeric data from source j and convert it to string in dest i */ + + pr(dfd,"static int %s%s%s(void* d,const void* s,aitIndex c, const gddEnumStringTable * pEST)\n", + table_type[k],&(aitName[i])[3],&(aitName[j])[3]); + pr(dfd,"{\n"); + pr(dfd,"\taitFixedString* out=(aitFixedString*)d;\n"); + pr(dfd,"\t%s* in=(%s*)s;\n",aitName[j],aitName[j]); + + pr(dfd,"\tfor(aitIndex i=0;i=%g && ftmp<=%g) {\n", + aitMin[i], aitMax[i]); + pr(dfd,"\t\t\t\tout[i] = (%s) ftmp;\n", aitName[i]); + pr(dfd,"\t\t\t}\n"); + pr(dfd,"\t\t\telse {\n"); + pr(dfd,"\t\t\t\treturn -1;\n"); + pr(dfd,"\t\t\t}\n"); + pr(dfd,"\t\t}\n"); + pr(dfd,"\t\telse {\n"); + pr(dfd,"\t\t\treturn -1;\n"); + pr(dfd,"\t\t}\n"); + pr(dfd,"\t}\n"); + pr(dfd,"\treturn (int) (sizeof(%s)*c);\n}\n", aitName[i]); +} + +void GenName(int i,int j,int k) +{ + const char* i_name = &((aitName[i])[3]); + const char* j_name = &((aitName[j])[3]); + + pr(dfd,"static int %s%s%s(void* d,const void* s,aitIndex c, const gddEnumStringTable *)\n", + table_type[k],i_name,j_name); +} + +void GenVars(int i,int j) +{ + pr(dfd,"\taitIndex i;\n"); + pr(dfd,"\t%s* d_val=(%s*)d;\n",aitName[i],aitName[i]); + pr(dfd,"\t%s* s_val=(%s*)s;\n\n",aitName[j],aitName[j]); +} + +void MakeFromFunc(int i,int j,int k) +{ + int conv_type; + char *len_msg,*conv_msg; + + /* destination = i, source = j, type = k */ + GenName(i,j,k); + pr(dfd,"{\n"); + + /* check if we need network byte swaps */ + if (strstr(aitName[j],"16")) + { len_msg="16"; conv_type=AIT_SWAP_16; } + else if(strstr(aitName[j],"32")) + { len_msg="32"; conv_type=AIT_SWAP_32; } + else if(strstr(aitName[j],"64")) + { len_msg="64"; conv_type=AIT_SWAP_64; } + else + { len_msg=""; conv_type=AIT_SWAP_NONE; } + + if (strstr(aitName[j],"Float")) + conv_msg="Float"; + else + conv_msg="Order"; + + GenVars(i,j); + + if(i==j || conv_type==AIT_SWAP_NONE) + { + if(conv_type!=AIT_SWAP_NONE) + { + pr(dfd,"\tfor(i=0;i + +#include "epicsTime.h" // define struct timespec if nec + +#define epicsExportSharedSymbols +#include "aitTypes.h" +#include "aitHelpers.h" + +// +// 1/1/90 20 yr (5 leap) of seconds +// +const unsigned aitTimeStamp::epicsEpochSecPast1970 = 7305 * 86400; + +void aitString::mallocFailure(void) +{ + str=(char *)""; + len=0u; + bufLen=1u; + type=aitStrRefConstImortal; + fprintf(stderr,"aitString: no pool => continuing with zero char str\n"); +} + +void aitString::dump(const char* p) const +{ + fprintf(stderr,"<%s>:",p); + dump(); +} + +void aitString::dump(void) const +{ + fprintf(stderr,"this=%p ", this); + if(str) fprintf(stderr,"string=%p<%s>, ",str,str); + else fprintf(stderr,"no string present, "); + fprintf(stderr,"length=%u, ",len); + fprintf(stderr,"buf length=%u, ",bufLen); + if(type==aitStrRefConstImortal) fprintf(stderr,"type=Imortal Constant Reference\n"); + else if(type==aitStrRefConst) fprintf(stderr,"type=Constant Reference\n"); + else if(type==aitStrRef) fprintf(stderr,"type=Reference\n"); + else if(type==aitStrCopy) fprintf(stderr,"type=Allocated\n"); + else fprintf(stderr,"type=Invalid\n"); +} + +aitIndex aitString::compact(aitString* array, aitIndex arraySize, + void* buf, aitIndex bufSize) +{ + aitIndex i; + aitUint32 pos; + char* ptr=(char*)buf; + aitString* str=(aitString*)buf; + + // copy the array first + pos=sizeof(aitString)*arraySize; + if(bufSizebufSize) break; // quick exit from loop + if(array[i].string()) + { + memcpy(&ptr[pos],array[i].string(),array[i].length()+1u); + str[i].installBuf(&ptr[pos], array[i].length(), array[i].length()+1u); + pos+=str[i].length()+1; + } + } + return pos; +} + +aitUint32 aitString::totalLength(aitString* array, aitIndex arraySize) +{ + aitIndex i; + aitUint32 tot; + + tot=sizeof(aitString)*arraySize; + for(i=0;i=bufSizeIn) { + return -1; + } + + if (this->type==aitStrRefConst || this->type==aitStrRefConstImortal + || bufSizeIn>this->bufLen) { + + char *pStrNew; + pStrNew = new char [bufSizeIn]; + if (!pStrNew) { + mallocFailure(); + return -1; + } + if (this->type==aitStrCopy) { + delete [] this->str; + } + this->str = pStrNew; + this->bufLen = bufSizeIn; + this->type = aitStrCopy; + } + // test above verifies that bufLen exceeds + // length of p + strncpy (this->str,p,this->bufLen); + this->len = newStrLength; + return 0; +} + +int aitString::init(const char* p, aitStrType typeIn, unsigned strLengthIn, unsigned bufSizeIn) +{ + int rc; + + this->init(); + switch (typeIn) { + case aitStrCopy: + rc = this->copy(p, strLengthIn, bufSizeIn); + break; + case aitStrRef: + rc = this->installBuf(p, strLengthIn, bufSizeIn); + break; + case aitStrRefConst: + rc = this->installConstBuf(p, strLengthIn, bufSizeIn); + break; + case aitStrRefConstImortal: + rc = this->installConstImortalBuf(p, strLengthIn, bufSizeIn); + break; + default: + rc=-1; + break; + } + return rc; +} + +aitTimeStamp::operator struct epicsTimeStamp () const +{ + epicsTimeStamp ts; + + if (this->tv_sec>aitTimeStamp::epicsEpochSecPast1970) { + ts.secPastEpoch = this->tv_sec - aitTimeStamp::epicsEpochSecPast1970; + ts.nsec = this->tv_nsec; + } + else { + ts.secPastEpoch = 0; + ts.nsec = 0; + } + return ts; +} + +void aitTimeStamp::get (struct epicsTimeStamp &ts) const +{ + if (this->tv_sec>aitTimeStamp::epicsEpochSecPast1970) { + ts.secPastEpoch = this->tv_sec - aitTimeStamp::epicsEpochSecPast1970; + ts.nsec = this->tv_nsec; + } + else { + ts.secPastEpoch = 0; + ts.nsec = 0; + } +} + +aitTimeStamp::aitTimeStamp (const struct epicsTimeStamp &ts) +{ + this->tv_sec = ts.secPastEpoch + aitTimeStamp::epicsEpochSecPast1970; + this->tv_nsec = ts.nsec; +} + +aitTimeStamp aitTimeStamp::operator = (const struct epicsTimeStamp &rhs) +{ + this->tv_sec = rhs.secPastEpoch + aitTimeStamp::epicsEpochSecPast1970; + this->tv_nsec = rhs.nsec; + return *this; +} + +aitTimeStamp :: aitTimeStamp ( const epicsTime & ts ) +{ + epicsTimeStamp ets = ts; + *this = ets; +} + +aitTimeStamp aitTimeStamp :: operator = ( const epicsTime & rhs ) +{ + epicsTimeStamp ets = rhs; + return *this = ets; +} + +aitTimeStamp :: operator epicsTime () const +{ + epicsTimeStamp ets = *this; + return epicsTime ( ets ); +} + +aitTimeStamp::operator struct timespec () const +{ + struct timespec ts; + ts.tv_sec = (unsigned long) this->tv_sec; + ts.tv_nsec = (unsigned long) this->tv_nsec; + return ts; +} + +void aitTimeStamp::get (struct timespec &ts) const +{ + ts.tv_sec = (unsigned long) this->tv_sec; + ts.tv_nsec = (unsigned long) this->tv_nsec; +} + +aitTimeStamp::aitTimeStamp (const struct timespec &ts) +{ + this->tv_sec = (aitUint32) ts.tv_sec; + this->tv_nsec = (aitUint32) ts.tv_nsec; +} + +aitTimeStamp aitTimeStamp::operator = (const struct timespec &rhs) +{ + this->tv_sec = (aitUint32) rhs.tv_sec; + this->tv_nsec = (aitUint32) rhs.tv_nsec; + return *this; +} diff --git a/src/gdd/aitHelpers.h b/src/gdd/aitHelpers.h new file mode 100644 index 000000000..2d252c0b5 --- /dev/null +++ b/src/gdd/aitHelpers.h @@ -0,0 +1,465 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef aitHelpersInclude +#define aitHelpersInclude + +/* + * Authors: Jeff Hill and Jim Kowalkowski + * Date: 6/20/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#include +#include +#ifndef assert // allows use of epicsAssert.h +#include +#endif + +#include "shareLib.h" + +#define NSecPerSec 1000000000u +#define NSecPerUSec 1000u +#define SecPerMin 60u + +inline char* strDup(const char* x) +{ + char* y = new char[strlen(x)+1]; + strcpy(y,x); + return y; +} + +struct timespec; +struct epicsTimeStamp; +class epicsTime; +class gdd; + +class epicsShareClass aitTimeStamp { + friend inline aitTimeStamp operator+ (const aitTimeStamp &lhs, const aitTimeStamp &rhs); + friend inline aitTimeStamp operator- (const aitTimeStamp &lhs, const aitTimeStamp &rhs); + friend inline int operator>= (const aitTimeStamp &lhs, const aitTimeStamp &rhs); + friend class gdd; +public: + aitTimeStamp () : tv_sec(0u), tv_nsec(0u) {} + aitTimeStamp (const aitTimeStamp &t) : tv_sec(t.tv_sec), tv_nsec(t.tv_nsec) {} + aitTimeStamp (const unsigned long tv_secIn, const unsigned long tv_nsecIn) : + tv_sec(tv_secIn), tv_nsec(tv_nsecIn) + { + if (tv_nsecIn>=NSecPerSec) { + this->tv_sec += this->tv_nsec/NSecPerSec; + this->tv_nsec = this->tv_nsec%NSecPerSec; + } + } + + // + // fetched as fields so this file is not required + // to include os dependent struct timeval + // + void getTV(long &tv_secOut, long &uSecOut) const + { + assert (this->tv_sec<=LONG_MAX); + tv_secOut = (long) this->tv_sec; + assert (this->tv_nsec<=LONG_MAX); + uSecOut = (long) this->tv_nsec/NSecPerUSec; + } + + // + // for use when loading struct timeval + // + void get(unsigned long &tv_secOut, unsigned long &tv_nsecOut) const + { + tv_secOut = this->tv_sec; + tv_nsecOut = this->tv_nsec; + } + + operator double() const + { + return ((double)this->tv_nsec)/NSecPerSec+this->tv_sec; + } + + operator float() const + { + return ((float)this->tv_nsec)/NSecPerSec+this->tv_sec; + } + + // + // convert to and from POSIX timespec format + // + operator struct timespec () const; + void get (struct timespec &) const; + aitTimeStamp (const struct timespec &ts); + aitTimeStamp operator = (const struct timespec &rhs); + + // + // convert to and from EPICS epicsTimeStamp format + // + operator struct epicsTimeStamp () const; + void get (struct epicsTimeStamp &) const; + aitTimeStamp (const epicsTimeStamp &ts); + aitTimeStamp operator = (const epicsTimeStamp &rhs); + + // conversion to from epicsTime + aitTimeStamp (const epicsTime &ts); + aitTimeStamp operator = (const epicsTime &rhs); + operator epicsTime () const; + + static aitTimeStamp getCurrent(); + +//private: + unsigned long tv_sec; + unsigned long tv_nsec; +private: + static const unsigned epicsEpochSecPast1970; +}; + +inline aitTimeStamp operator+ (const aitTimeStamp &lhs, const aitTimeStamp &rhs) +{ + return aitTimeStamp(lhs.tv_sec + rhs.tv_sec, lhs.tv_nsec + rhs.tv_nsec); +} + +// +// like data type unsigned this assumes that the lhs > rhs +// (otherwise we assume tv_sec wrap around) +// +inline aitTimeStamp operator- (const aitTimeStamp &lhs, const aitTimeStamp &rhs) +{ + unsigned long tv_nsec, tv_sec; + + if (lhs.tv_sec= (const aitTimeStamp &lhs, const aitTimeStamp &rhs) +{ + int rc=0; + + if (lhs.tv_sec>rhs.tv_sec) + rc=1; + else if(lhs.tv_sec==rhs.tv_sec) + if(lhs.tv_nsec>=rhs.tv_nsec) + rc=1; + return rc; +} + + +// --------------------------------------------------------------------------- +// very simple class for string storage (for now) +// +// +enum aitStrType { + aitStrRefConstImortal, // any constant string that always exists - ie "abc" + aitStrRefConst, // user provides constant string buffer for life of aitSting + aitStrRef, // user provides modifiable string buffer for life of aitSting + aitStrCopy}; // aitSting copies into internal storage +class epicsShareClass aitString +{ +public: + inline aitString(void); + inline aitString(const aitString* p); + inline aitString(const aitString& p); + inline aitString(const char* p, aitStrType type=aitStrCopy); + inline aitString(const char* p, aitStrType type, unsigned strLength); + inline aitString(const char* p, aitStrType type, unsigned strLength, unsigned bufSize); + + inline ~aitString(void); // free up string is required + + inline void clear(void); // clear everything, free string if required + void dump(void) const; + void dump(const char* id) const; + + // casts from aitString to other things - pulls info out of aitString + inline operator aitUint16(void) const { return (aitUint16)this->len; } + inline operator aitUint32(void) const { return (aitUint32)this->len; } + inline operator aitInt32(void) const { return (aitInt32)this->len; } + inline operator const char*(void) const { return this->str; } + inline operator char*(void) const + { + assert(type!=aitStrRefConst && type!=aitStrRefConstImortal); + return str; + } + inline int isConstant(void) const; + + inline aitUint32 length(void) const { return (aitUint32)this->len; } + inline const char* string(void) const { return this->str; } + + // completely reset the aitString to a new value + // - the same as copy() + inline aitString& operator=(const aitString& pString); + inline aitString& operator=(const aitString* pString); + inline aitString& operator=(const char* pString); + + inline int copy(const aitString* pString); + inline int copy(const aitString& pString); + inline int copy(const char* pString); + inline int copy(const char* pString, unsigned stringLength); + int copy(const char* pString, unsigned stringLength, unsigned bufSize); + + // + // make ait string point at string in application's modifiable buffer + // + inline int installBuf(const char* pString); + inline int installBuf(const char* pString, unsigned stringLength); + inline int installBuf(const char* pString, unsigned stringLength, unsigned bufSize); + + // + // make ait string point at constant string in application's constant buffer + // + inline int installConstBuf(const char* pString); + inline int installConstBuf(const char* pString, unsigned stringLength); + inline int installConstBuf(const char* pString, unsigned stringLength, unsigned bufSize); + + // + // make ait string point at constant string in application's constant buffer + // that exists forever (such as "abc") + // + inline int installConstImortalBuf(const char* pString); + inline int installConstImortalBuf(const char* pString, unsigned stringLength); + inline int installConstImortalBuf(const char* pString, unsigned stringLength, unsigned bufSize); + + inline void extractString(char* to_here, unsigned bufSize); + + // take the aitString array, and put it and all the string into buf, + // return the total length the data copied + static aitUint32 totalLength(aitString* array,aitIndex arraySize); + static aitUint32 stringsLength(aitString* array,aitIndex arraySize); + static aitIndex compact(aitString* array, aitIndex arraySize, + void* buf, aitIndex bufSize); + + // + // for gdd's use only! (to construct class inside union) + // (it is possible to leak memory if this is called on + // an already constructed aitString). This practice should + // be eliminated by deriving from aitString and replacing + // the new operator? + // + inline void init(void); + +private: + + char* str; + unsigned len:14; // actual length of string + unsigned bufLen:14; // length of string buffer + unsigned type:4; // aitStrType goes here + + void mallocFailure(void); + inline aitStrType getType(void) const { return (aitStrType)type; } + int init(const char* p, aitStrType type, unsigned strLength, unsigned bufSize); +}; + +inline void aitString::init(void) +{ + this->str=(char *)""; + this->len=0u; + this->bufLen=1u; + this->type=aitStrRefConstImortal; +} + +inline int aitString::isConstant(void) const +{ + return ( (getType()==aitStrRefConst||getType()==aitStrRefConstImortal) && this->str)?1:0; +} + +inline void aitString::clear(void) +{ + if(this->str && this->type==aitStrCopy) delete [] this->str; + this->init(); +} + +inline void aitString::extractString(char* p, unsigned bufLength) +{ + if (bufLength==0u) { + return; + } + else if (this->str) { + strncpy(p,this->str,bufLength); + p[bufLength-1u]='\0'; + } + else { + p[0u] = '\0'; + } +} + +// +// make ait string point at string in application's buffer +// +inline int aitString::installBuf(const char* pString, unsigned strLengthIn, unsigned bufSizeIn) +{ + if (this->type==aitStrCopy) { + delete [] this->str; + } + this->str = (char *) pString; + this->bufLen = bufSizeIn; + this->type = aitStrRef; + this->len = strLengthIn; + return 0; +} + +inline int aitString::installBuf(const char* pString) +{ + unsigned strLengthIn = strlen(pString); + return this->installBuf(pString, strLengthIn, strLengthIn+1u); +} + +inline int aitString::installBuf(const char* pString, unsigned strLengthIn) +{ + return this->installBuf(pString, strLengthIn, strLengthIn+1u); +} + + +// +// make ait string point at constant string in application's buffer +// +inline int aitString::installConstBuf(const char* pString, unsigned strLengthIn, unsigned bufSizeIn) +{ + if (this->type==aitStrCopy) { + delete [] this->str; + } + this->str = (char *) pString; + this->bufLen = bufSizeIn; + this->type = aitStrRefConst; + this->len = strLengthIn; + return 0; +} + +inline int aitString::installConstBuf(const char* pString) +{ + unsigned strLengthIn = strlen(pString); + return this->installConstBuf(pString, strLengthIn, strLengthIn+1u); +} + +inline int aitString::installConstBuf(const char* pString, unsigned strLengthIn) +{ + return this->installConstBuf(pString, strLengthIn, strLengthIn+1u); +} + +// +// make ait string point at constant string in application's buffer +// that always exists (such as "abc") +// +inline int aitString::installConstImortalBuf(const char* pString, + unsigned strLengthIn, unsigned bufSizeIn) +{ + if (this->type==aitStrCopy) { + delete [] this->str; + } + this->str = (char *) pString; + this->bufLen = bufSizeIn; + this->type = aitStrRefConstImortal; + this->len = strLengthIn; + return 0; +} + +inline int aitString::installConstImortalBuf(const char* pString) +{ + unsigned strLengthIn = strlen(pString); + return this->installConstImortalBuf(pString, strLengthIn, strLengthIn+1u); +} + +inline int aitString::installConstImortalBuf(const char* pString, unsigned strLengthIn) +{ + return this->installConstImortalBuf(pString, strLengthIn, strLengthIn+1u); +} + + +inline int aitString::copy(const char* pString, unsigned stringLength) +{ + return this->copy(pString, stringLength, + this->bufLen>(stringLength+1u) ? this->bufLen : (stringLength+1u) ); +} + +inline int aitString::copy(const char* p) +{ + return this->copy(p, strlen(p)); +} + +inline int aitString::copy(const aitString* p) +{ + if (p->type==aitStrRefConstImortal) { + // + // fast reference if the string is constant and it + // exists forever + // + return this->installConstImortalBuf(p->str, p->len, p->len+1u); + } + return this->copy(p->str, p->len); +} + +inline int aitString::copy(const aitString& p) +{ + return this->copy(&p); +} + +inline aitString& aitString::operator=(const aitString& p) + { this->copy(p); return *this; } +inline aitString& aitString::operator=(const aitString* p) + { this->copy(p); return *this; } +inline aitString& aitString::operator=(const char* p) + { this->copy(p); return *this; } + +inline aitString::~aitString(void) +{ + // dump("~aitString"); + this->clear(); +} + +inline aitString::aitString(void) +{ + this->init(); +} + +inline aitString::aitString(const char* p, aitStrType typeIn) +{ + unsigned strLengthIn = strlen(p); + this->init(p, typeIn, strLengthIn, strLengthIn+1u); +} + +inline aitString::aitString(const char* p, aitStrType typeIn, unsigned strLengthIn) +{ + this->init(p, typeIn, strLengthIn, strLengthIn+1u); +} + +inline aitString::aitString(const char* p, aitStrType typeIn, unsigned strLength, unsigned bufSize) +{ + this->init(p,typeIn,strLength,bufSize); +} + +inline aitString::aitString(const aitString* p) +{ + this->init(); + this->copy(p); +} + +inline aitString::aitString(const aitString& p) +{ + this->init(); + this->copy(p); +} + +#endif // aitHelpersInclude diff --git a/src/gdd/aitTypes.c b/src/gdd/aitTypes.c new file mode 100644 index 000000000..d05b63751 --- /dev/null +++ b/src/gdd/aitTypes.c @@ -0,0 +1,92 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "aitTypes.h" + +epicsShareDef const size_t aitSize[aitTotal] = { + 0, + sizeof(aitInt8), + sizeof(aitUint8), + sizeof(aitInt16), + sizeof(aitUint16), + sizeof(aitEnum16), + sizeof(aitInt32), + sizeof(aitUint32), + sizeof(aitFloat32), + sizeof(aitFloat64), + sizeof(aitFixedString), + sizeof(aitString), + 0 +}; + +epicsShareDef const char* aitName[aitTotal] = { + "aitInvalid", + "aitInt8", + "aitUint8", + "aitInt16", + "aitUint16", + "aitEnum16", + "aitInt32", + "aitUint32", + "aitFloat32", + "aitFloat64", + "aitFixedString", + "aitString", + "aitContainer" +}; + +/* + * conversion characters used with stdio lib + */ +epicsShareDef const char* aitPrintf[aitTotal] = { + 0, + "c", + "c", + "hd", + "hu", + "hu", + "d", + "u", + "g", + "g", + "s", + 0, /* printf doesnt know about aitString */ + 0 +}; +epicsShareDef const char* aitScanf[aitTotal] = { + 0, + "c", + "c", + "hd", + "hu", + "hu", + "d", + "u", + "g", + "lg", + "s", + 0, /* scanf doesnt know about aitString */ + 0 +}; + + + diff --git a/src/gdd/aitTypes.h b/src/gdd/aitTypes.h new file mode 100644 index 000000000..bfc6d14a6 --- /dev/null +++ b/src/gdd/aitTypes.h @@ -0,0 +1,140 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef AIT_TYPES_H +#define AIT_TYPES_H 1 + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +/* This is the file the user sets up for a given architecture */ + +#define AIT_FIXED_STRING_SIZE 40 + +#include "shareLib.h" + +typedef signed char aitInt8; +typedef unsigned char aitUint8; +typedef short aitInt16; +typedef unsigned short aitUint16; +typedef aitUint16 aitEnum16; +typedef int aitInt32; +typedef unsigned int aitUint32; +typedef float aitFloat32; +typedef double aitFloat64; +typedef aitUint32 aitIndex; +typedef void* aitPointer; + +typedef union { + struct { + aitUint16 aitStat; + aitUint16 aitSevr; + } s; + aitUint32 u; +} aitStatus; + +/* should the bool be added as a conversion type? it currently is not */ +typedef enum { + aitFalse=0, + aitTrue +} aitBool; + +typedef struct { + char fixed_string[AIT_FIXED_STRING_SIZE]; +} aitFixedString; + +#ifdef __cplusplus +#include "aitHelpers.h" +#else +/* need time stamp structure different from posix unfortunetly */ +typedef struct { + aitUint32 tv_sec; + aitUint32 tv_nsec; +} aitTimeStamp; + +/* strings are a struct so they are different then aitInt8 */ +typedef struct { + char* string; + aitUint32 len; +} aitString; +#endif + +/* all normal types */ +#define aitTotal 13 +#define aitFirst aitEnumInvalid +#define aitLast aitEnumContainer +#define aitValid(x) ((x)<=aitLast && (x)>aitFirst) + +/* all conversion types */ +#define aitConvertTotal 11 +#define aitConvertFirst aitEnumInt8 +#define aitConvertLast aitEnumString +#define aitConvertAutoFirst aitEnumInt8 +#define aitConvertAutoLast aitEnumFloat64 +#define aitConvertValid(x) ((x)>=aitConvertFirst && (x)<=aitConvertLast) + +/* currently no 64-bit integer support */ +typedef enum { + aitEnumInvalid=0, + aitEnumInt8, + aitEnumUint8, + aitEnumInt16, + aitEnumUint16, + aitEnumEnum16, + aitEnumInt32, + aitEnumUint32, + aitEnumFloat32, + aitEnumFloat64, + aitEnumFixedString, + aitEnumString, + aitEnumContainer +} aitEnum; + +typedef union { + aitInt8 Int8; + aitUint8 Uint8; + aitInt16 Int16; + aitUint16 Uint16; + aitEnum16 Enum16; + aitInt32 Int32; + aitUint32 Uint32; + aitFloat32 Float32; + aitFloat64 Float64; + aitIndex Index; + aitPointer Pointer; + aitFixedString* FString; + aitUint8 Dumb1[sizeof(aitString)]; /* aitString String; */ + aitUint8 Dumb3[sizeof(aitTimeStamp)]; /* aitTimeStamp Stamp; */ +} aitType; + +/* + classes are not allowed in union that required construction/destruction + so I am insuring that the size of aitType is large enough to hold + strings and timestamps in an obsure, horrible way with the Dumb variables +*/ + + +#ifdef __cplusplus +extern "C" { +#endif +epicsShareExtern const size_t aitSize[aitTotal]; +epicsShareExtern const char* aitName[aitTotal]; +epicsShareExtern const char* aitPrintf[aitTotal]; +epicsShareExtern const char* aitScanf[aitTotal]; +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/src/gdd/dbMapper.cc b/src/gdd/dbMapper.cc new file mode 100644 index 000000000..546756311 --- /dev/null +++ b/src/gdd/dbMapper.cc @@ -0,0 +1,1805 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#define DB_MAPPER_SOURCE 1 + +#include +#include + +#define epicsExportSharedSymbols +#include "gddApps.h" +#include "gddAppTable.h" +#include "dbMapper.h" +// #include "templates/dbMapperTempl.h" + +// hardcoded in same order as aitConvert.h +// no way to detect a string type!!!!!!! + +extern epicsShareDef const int gddAitToDbr[aitConvertLast+1] = { + DBR_STRING, + DBR_CHAR, + DBR_CHAR, + DBR_SHORT, + DBR_SHORT, + DBR_ENUM, + DBR_LONG, + DBR_LONG, + DBR_FLOAT, + DBR_DOUBLE, + DBR_STRING, + DBR_STRING +}; + +extern epicsShareDef const unsigned gddDbrToAitNElem = + sizeof(gddAitToDbr)/sizeof(gddAitToDbr[0]); + +/* + * application type ("app" field) is initialized + * at run tim using the "app_name" field. + */ +epicsShareDef gddDbrToAitTable gddDbrToAit[DBM_N_DBR_TYPES] = { + // Atomic + { aitEnumFixedString, 0, "value" }, // DBR_STRING + { aitEnumInt16, 0, "value" }, // DBR_SHORT + { aitEnumFloat32, 0, "value" }, // DBR_FLOAT + { aitEnumEnum16, 0, "value" }, // DBR_ENUM + { aitEnumInt8, 0, "value" }, // DBR_CHAR + { aitEnumInt32, 0, "value" }, // DBR_LONG + { aitEnumFloat64, 0, "value" }, // DBR_DOUBLE + // Status + { aitEnumFixedString, 0, "value" }, // DBR_STS_STRING + { aitEnumInt16, 0, "value" }, // DBR_STS_SHORT + { aitEnumFloat32, 0, "value" }, // DBR_STS_FLOAT + { aitEnumEnum16, 0, "value" }, // DBR_STS_ENUM + { aitEnumInt8, 0, "value" }, // DBR_STS_CHAR + { aitEnumInt32, 0, "value" }, // DBR_STS_LONG + { aitEnumFloat64, 0, "value" }, // DBR_STS_DOUBLE + // Time + { aitEnumFixedString, 0, "value" }, // DBR_TIME_STRING + { aitEnumInt16, 0, "value" }, // DBR_TIME_SHORT + { aitEnumFloat32, 0, "value" }, // DBR_TIME_FLOAT + { aitEnumEnum16, 0, "value" }, // DBR_TIME_ENUM + { aitEnumInt8, 0, "value" }, // DBR_TIME_CHAR + { aitEnumInt32, 0, "value" }, // DBR_TIME_LONG + { aitEnumFloat64, 0, "value" }, // DBR_TIME_DOUBLE + // Graphic + { aitEnumFixedString, 0, "value" }, // DBR_GR_STRING + { aitEnumInt16, 0, "dbr_gr_short" }, // DBR_GR_SHORT + { aitEnumFloat32, 0, "dbr_gr_float" }, // DBR_GR_FLOAT + { aitEnumEnum16, 0, "dbr_gr_enum" }, // DBR_GR_ENUM + { aitEnumInt8, 0, "dbr_gr_char" }, // DBR_GR_CHAR + { aitEnumInt32, 0, "dbr_gr_long" }, // DBR_GR_LONG + { aitEnumFloat64, 0, "dbr_gr_double" }, // DBR_GR_DOUBLE + // Control + { aitEnumFixedString, 0, "value" }, // DBR_CTRL_STRING + { aitEnumInt16, 0, "dbr_ctrl_short" }, // DBR_CTRL_SHORT + { aitEnumFloat32, 0, "dbr_ctrl_float" }, // DBR_CTRL_FLOAT + { aitEnumEnum16, 0, "dbr_ctrl_enum" }, // DBR_CTRL_ENUM + { aitEnumInt8, 0, "dbr_ctrl_char" }, // DBR_CTRL_CHAR + { aitEnumInt32, 0, "dbr_ctrl_long" }, // DBR_CTRL_LONG + { aitEnumFloat64, 0, "dbr_ctrl_double" }, // DBR_CTRL_DOUBLE + // Ack + { aitEnumUint16, 0, "ackt" }, // DBR_PUT_ACKT + { aitEnumUint16, 0, "acks" }, // DBR_PUT_ACKS + { aitEnumFixedString, 0, "dbr_stsack_string" }, // DBR_STSACK_STRING + // Class + { aitEnumFixedString, 0, "class" } // DBR_CLASS_NAME +}; + +#if DBM_N_DBR_TYPES != (LAST_BUFFER_TYPE+1) +#error db mapper is out of sync with db_access.h +#endif + +static gddApplicationTypeTable* type_table = NULL; +static aitDataFormat local_data_format=aitLocalDataFormat; + +extern epicsShareDef const unsigned gddAitToDbrNElem = + sizeof(gddDbrToAit)/sizeof(gddDbrToAit[0]); + +// +// special gddDestructor guarantees same form of new and delete +// +class dbMapperFixedStringDestructor: public gddDestructor { + virtual void run (void *); +}; + + +// I generated a container for each of the important DBR types. This +// includes all the control and graphic structures. The others are +// not needed become you can get time stamp and status in each gdd. +// Currently the containers are built here with c++ code. + +// string type needs to be written special and not use template +// maybe create a template for the string type +// the problem is that the value of a string structure is an array, +// not just a single element + +static smartGDDPointer mapStringToGdd(void* v,aitIndex count) { + aitFixedString* db = (aitFixedString*)v; + aitEnum to_type = gddDbrToAit[DBR_STRING].type; + aitUint16 to_app = gddDbrToAit[DBR_STRING].app; + + if(count<=1) + { + smartGDDPointer dd = new gddScalar(to_app,to_type); + dd->unreference(); + dd->put(*db); + return dd; + } + else + { + smartGDDPointer dd=new gddAtomic(to_app,to_type,1,count); + dd->unreference(); + aitFixedString* pCopy = new aitFixedString [count]; + memcpy (pCopy,db,sizeof(aitFixedString)*count); + dd->putRef(db,new dbMapperFixedStringDestructor); + return dd; + } +} + +static int mapGddToString(void* vd, aitIndex count, + const gdd & dd, const gddEnumStringTable &enumStringTable) { + aitFixedString* db = (aitFixedString*)vd; + aitIndex sz = dd.getDataSizeElements(); + const void* v = dd.dataVoid(); + int status; + + if (count>sz) { + memset (db+sz, '\0', sizeof(*db)*(count-sz)); + count = sz; + } + + if(local_data_format==aitLocalDataFormat) { + if((aitFixedString*)v!=db) { + status = aitConvert(aitEnumFixedString,db, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = sz*sizeof(aitFixedString); + } + } + else { + status = aitConvertToNet(aitEnumFixedString, + db,dd.primitiveType(),v,count, &enumStringTable); + } + + return status; +} + +static smartGDDPointer mapShortToGdd(void* v,aitIndex count) { + dbr_short_t* sv = (dbr_short_t*)v; + + if(count>1) { + smartGDDPointer dd=new gddAtomic(gddDbrToAit[DBR_SHORT].app, + gddDbrToAit[DBR_SHORT].type,1,count); + dd->unreference(); + dbr_short_t* pCopy = (dbr_short_t*) new char [sizeof(dbr_short_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_short_t)*count); + dd->putRef(pCopy, new gddDestructor); + return dd; + } else { + smartGDDPointer dd=new gddScalar(gddDbrToAit[DBR_SHORT].app); + dd->unreference(); + *dd=*sv; + return dd; + } +} + +static int mapGddToShort(void* vd, aitIndex count, const gdd &dd, + const gddEnumStringTable &enumStringTable) { + dbr_short_t* sv = (dbr_short_t*)vd; + aitIndex sz = dd.getDataSizeElements(); + const void * v=dd.dataVoid(); + int status; + + if (count>sz) { + memset (sv+sz, '\0', sizeof(*sv)*(count-sz)); + count = sz; + } + + if (local_data_format==aitLocalDataFormat) { + if((dbr_short_t*)v!=sv) { + status = aitConvert(aitEnumInt16,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = count*sizeof(dbr_short_t); + } + } + else { + status = aitConvertToNet(aitEnumInt16,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + + return status; +} + +static smartGDDPointer mapFloatToGdd(void* v,aitIndex count) { + dbr_float_t* sv = (dbr_float_t*)v; + + if(count>1) { + smartGDDPointer dd=new gddAtomic(gddDbrToAit[DBR_FLOAT].app, + gddDbrToAit[DBR_FLOAT].type,1,count); + dd->unreference(); + dbr_float_t* pCopy = (dbr_float_t*) new char [sizeof(dbr_float_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_float_t)*count); + dd->putRef(pCopy, new gddDestructor); + return dd; + } else { + smartGDDPointer dd=new gddScalar(gddDbrToAit[DBR_FLOAT].app); + dd->unreference(); + *dd=*sv; + return dd; + } +} + +static int mapGddToFloat(void* vd, aitIndex count, + const gdd & dd, const gddEnumStringTable &enumStringTable) { + dbr_float_t* sv = (dbr_float_t*)vd; + aitIndex sz=dd.getDataSizeElements(); + const void * v = dd.dataVoid(); + int status; + + if (count>sz) { + memset (sv+sz, '\0', sizeof(*sv)*(count-sz)); + count = sz; + } + + if(local_data_format==aitLocalDataFormat) { + if((dbr_float_t*)v!=sv) { + status = aitConvert(aitEnumFloat32,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = sz*sizeof(dbr_float_t); + } + } + else { + status = aitConvertToNet(aitEnumFloat32,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + + return status; +} + +static smartGDDPointer mapEnumToGdd(void* v,aitIndex count) { + dbr_enum_t* sv = (dbr_enum_t*)v; + smartGDDPointer dd; + + if(count>1) { + dd=new gddAtomic(gddDbrToAit[DBR_ENUM].app, + gddDbrToAit[DBR_ENUM].type,1,count); + dd->unreference(); + dbr_enum_t* pCopy = (dbr_enum_t *) new char [sizeof(dbr_enum_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_enum_t)*count); + dd->putRef(pCopy, new gddDestructor); + } else { + dd=new gddScalar(gddDbrToAit[DBR_ENUM].app); + dd->unreference(); + *dd=*sv; + } + return dd; +} + +static int mapGddToEnum(void* vd, aitIndex count, const gdd & dd, + const gddEnumStringTable &enumStringTable) { + dbr_enum_t* sv = (dbr_enum_t*)vd; + aitIndex sz=dd.getDataSizeElements(); + const void* v = dd.dataVoid(); + int status; + + if (count>sz) { + memset (sv+sz, '\0', sizeof(*sv)*(count-sz)); + count = sz; + } + + if(local_data_format==aitLocalDataFormat) { + if((dbr_enum_t*)v!=sv) { + status = aitConvert(aitEnumEnum16,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = sizeof(dbr_enum_t)*count; + } + } + else { + status = aitConvertToNet(aitEnumEnum16,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + + return status; +} + +static smartGDDPointer mapCharToGdd(void* v,aitIndex count) { + dbr_char_t* sv = (dbr_char_t*)v; + smartGDDPointer dd; + + if(count>1) { + dd=new gddAtomic(gddDbrToAit[DBR_CHAR].app, + gddDbrToAit[DBR_CHAR].type,1,count); + dd->unreference(); + dbr_char_t* pCopy = (dbr_char_t *) new char [sizeof(dbr_char_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_char_t)*count); + dd->putRef(pCopy, new gddDestructor); + } else { + dd=new gddScalar(gddDbrToAit[DBR_CHAR].app); + dd->unreference(); + *dd=*sv; + } + return dd; +} + +static int mapGddToChar(void* vd, aitIndex count, + const gdd & dd, const gddEnumStringTable &enumStringTable) { + dbr_char_t* sv = (dbr_char_t*)vd; + aitIndex sz=dd.getDataSizeElements(); + const void* v = dd.dataVoid(); + int status; + + if (count>sz) { + memset (sv+sz, '\0', sizeof(*sv)*(count-sz)); + count = sz; + } + + if (local_data_format==aitLocalDataFormat) { + if((dbr_char_t*)v!=sv) { + status = aitConvert(aitEnumInt8,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = sz*sizeof(dbr_char_t); + } + } + else { + status = aitConvertToNet(aitEnumInt8,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + + return status; +} + +static smartGDDPointer mapLongToGdd(void* v,aitIndex count) { + dbr_long_t* sv = (dbr_long_t*)v; + smartGDDPointer dd; + + if(count>1) { + dd=new gddAtomic(gddDbrToAit[DBR_LONG].app, + gddDbrToAit[DBR_LONG].type,1,count); + dd->unreference(); + dbr_long_t* pCopy = (dbr_long_t*) new char [sizeof(dbr_long_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_long_t)*count); + dd->putRef(pCopy, new gddDestructor); + } else { + dd=new gddScalar(gddDbrToAit[DBR_LONG].app); + dd->unreference(); + *dd=*sv; + } + return dd; +} + +static int mapGddToLong(void* vd, aitIndex count, const gdd & dd, + const gddEnumStringTable &enumStringTable) { + dbr_long_t* sv = (dbr_long_t*)vd; + aitIndex sz=dd.getDataSizeElements(); + const void* v = dd.dataVoid(); + int status; + + if (count>sz) { + memset (sv+sz, '\0', sizeof(*sv)*(count-sz)); + count = sz; + } + + if (local_data_format==aitLocalDataFormat) { + if ((dbr_long_t*)v!=sv) { + status = aitConvert(aitEnumInt32,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = count*sizeof(dbr_long_t); + } + } + else { + status = aitConvertToNet(aitEnumInt32,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + + return status; +} + +static smartGDDPointer mapDoubleToGdd(void* v,aitIndex count) { + dbr_double_t* sv = (dbr_double_t*)v; + smartGDDPointer dd; + + if(count>1) { + dd=new gddAtomic(gddDbrToAit[DBR_DOUBLE].app, + gddDbrToAit[DBR_DOUBLE].type,1,count); + dd->unreference(); + dbr_double_t* pCopy = (dbr_double_t *) new char [sizeof(dbr_double_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_double_t)*count); + dd->putRef(pCopy, new gddDestructor); + } else { + dd=new gddScalar(gddDbrToAit[DBR_DOUBLE].app); + dd->unreference(); + *dd=*sv; + } + return dd; +} + +static int mapGddToDouble(void* vd, aitIndex count, const gdd & dd, + const gddEnumStringTable &enumStringTable) { + dbr_double_t* sv = (dbr_double_t*)vd; + aitIndex sz=dd.getDataSizeElements(); + const void* v = dd.dataVoid(); + int status; + + if (count>sz) { + memset (sv+sz, '\0', sizeof(*sv)*(count-sz)); + count = sz; + } + + if (local_data_format==aitLocalDataFormat) { + if ((dbr_double_t*)v!=sv) { + status = aitConvert(aitEnumFloat64,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = count*sizeof(dbr_double_t); + } + } + else { + status = aitConvertToNet(aitEnumFloat64,sv, + dd.primitiveType(),v,count, &enumStringTable); + } + + return status; +} + +// ******************************************************************** +// sts structure mappings +// ******************************************************************** + +static smartGDDPointer mapStsStringToGdd(void* v,aitIndex count) +{ + dbr_sts_string* db = (dbr_sts_string*)v; + aitFixedString* dbv = (aitFixedString*)db->value; + aitEnum to_type = gddDbrToAit[DBR_STS_STRING].type; + aitUint16 to_app = gddDbrToAit[DBR_STS_STRING].app; + smartGDDPointer dd; + + if(count<=1) + { + dd=new gddScalar(to_app,to_type); + dd->unreference(); + dd->put(*dbv); + } + else + { + dd=new gddAtomic(to_app,to_type,1,count); + dd->unreference(); + aitFixedString* pCopy = (aitFixedString*) new char [sizeof(aitFixedString)*count]; + memcpy (pCopy,dbv,sizeof(aitFixedString)*count); + dd->putRef(pCopy, new gddDestructor); + } + + dd->setStatSevr(db->status,db->severity); + return dd; +} + +static int mapStsGddToString(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_sts_string* db = (dbr_sts_string*)v; + aitFixedString* dbv = (aitFixedString*)db->value; + + dd.getStatSevr(db->status,db->severity); + return mapGddToString(dbv, count, dd, enumStringTable); +} + +static smartGDDPointer mapStsShortToGdd(void* v,aitIndex count) +{ + dbr_sts_short* dbv = (dbr_sts_short*)v; + smartGDDPointer dd=mapShortToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + return dd; +} + +static int mapStsGddToShort(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_sts_short* dbv = (dbr_sts_short*)v; + dd.getStatSevr(dbv->status,dbv->severity); + return mapGddToShort(&dbv->value, count, dd, enumStringTable); +} + +static smartGDDPointer mapStsFloatToGdd(void* v,aitIndex count) +{ + dbr_sts_float* dbv = (dbr_sts_float*)v; + smartGDDPointer dd=mapFloatToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + return dd; +} + +static int mapStsGddToFloat(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_sts_float* dbv = (dbr_sts_float*)v; + dd.getStatSevr(dbv->status,dbv->severity); + return mapGddToFloat(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapStsEnumToGdd(void* v,aitIndex count) +{ + dbr_sts_enum* dbv = (dbr_sts_enum*)v; + smartGDDPointer dd=mapEnumToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + return dd; +} + +static int mapStsGddToEnum(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_sts_enum* dbv = (dbr_sts_enum*)v; + dd.getStatSevr(dbv->status,dbv->severity); + return mapGddToEnum(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapStsCharToGdd(void* v,aitIndex count) +{ + dbr_sts_char* dbv = (dbr_sts_char*)v; + smartGDDPointer dd=mapCharToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + return dd; +} + +static int mapStsGddToChar(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_sts_char* dbv = (dbr_sts_char*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dbv->RISC_pad = '\0'; // shut up purify + return mapGddToChar(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapStsLongToGdd(void* v,aitIndex count) +{ + dbr_sts_long* dbv = (dbr_sts_long*)v; + smartGDDPointer dd=mapLongToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + return dd; +} + +static int mapStsGddToLong(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_sts_long* dbv = (dbr_sts_long*)v; + dd.getStatSevr(dbv->status,dbv->severity); + return mapGddToLong(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapStsDoubleToGdd(void* v,aitIndex count) +{ + dbr_sts_double* dbv = (dbr_sts_double*)v; + smartGDDPointer dd=mapDoubleToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + return dd; +} + +static int mapStsGddToDouble(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_sts_double* dbv = (dbr_sts_double*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dbv->RISC_pad = 0; // shut up purify + return mapGddToDouble(&dbv->value,count,dd, enumStringTable); +} + +// ******************************************************************** +// time structure mappings +// ******************************************************************** + +static smartGDDPointer mapTimeStringToGdd(void* v,aitIndex count) +{ + dbr_time_string* db = (dbr_time_string*)v; + aitFixedString* dbv = (aitFixedString*)db->value; + aitEnum to_type = gddDbrToAit[DBR_TIME_STRING].type; + aitUint16 to_app = gddDbrToAit[DBR_TIME_STRING].app; + smartGDDPointer dd; + + if(count<=1) + { + dd=new gddScalar(to_app,to_type); + dd->unreference(); + dd->put(*dbv); + } + else + { + dd=new gddAtomic(to_app,to_type,1,count); + dd->unreference(); + aitFixedString* pCopy = (aitFixedString*) new char [sizeof(aitFixedString)*count]; + memcpy (pCopy,dbv,sizeof(aitFixedString)*count); + dd->putRef(pCopy, new gddDestructor); + } + + dd->setStatSevr(db->status,db->severity); + dd->setTimeStamp(&db->stamp); + return dd; +} + +static int mapTimeGddToString(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_time_string* db = (dbr_time_string*)v; + aitFixedString* dbv = (aitFixedString*)db->value; + + dd.getStatSevr(db->status,db->severity); + dd.getTimeStamp(&db->stamp); + return mapGddToString(dbv, count, dd, enumStringTable); +} + +static smartGDDPointer mapTimeShortToGdd(void* v,aitIndex count) +{ + dbr_time_short* dbv = (dbr_time_short*)v; + smartGDDPointer dd=mapShortToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + dd->setTimeStamp(&dbv->stamp); + return dd; +} + +static int mapTimeGddToShort(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_time_short* dbv = (dbr_time_short*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dd.getTimeStamp(&dbv->stamp); + dbv->RISC_pad = 0; // shut up purify + return mapGddToShort(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapTimeFloatToGdd(void* v,aitIndex count) +{ + dbr_time_float* dbv = (dbr_time_float*)v; + smartGDDPointer dd=mapFloatToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + dd->setTimeStamp(&dbv->stamp); + return dd; +} + +static int mapTimeGddToFloat(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_time_float* dbv = (dbr_time_float*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dd.getTimeStamp(&dbv->stamp); + return mapGddToFloat(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapTimeEnumToGdd(void* v,aitIndex count) +{ + dbr_time_enum* dbv = (dbr_time_enum*)v; + smartGDDPointer dd=mapEnumToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + dd->setTimeStamp(&dbv->stamp); + return dd; +} + +static int mapTimeGddToEnum(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_time_enum* dbv = (dbr_time_enum*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dd.getTimeStamp(&dbv->stamp); + dbv->RISC_pad = 0; // shut up purify + return mapGddToEnum(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapTimeCharToGdd(void* v,aitIndex count) +{ + dbr_time_char* dbv = (dbr_time_char*)v; + smartGDDPointer dd=mapCharToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + dd->setTimeStamp(&dbv->stamp); + return dd; +} + +static int mapTimeGddToChar(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_time_char* dbv = (dbr_time_char*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dd.getTimeStamp(&dbv->stamp); + dbv->RISC_pad0 = 0; // shut up purify + dbv->RISC_pad1 = '\0'; // shut up purify + return mapGddToChar(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapTimeLongToGdd(void* v,aitIndex count) +{ + dbr_time_long* dbv = (dbr_time_long*)v; + smartGDDPointer dd=mapLongToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + dd->setTimeStamp(&dbv->stamp); + return dd; +} + +static int mapTimeGddToLong(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_time_long* dbv = (dbr_time_long*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dd.getTimeStamp(&dbv->stamp); + return mapGddToLong(&dbv->value,count,dd, enumStringTable); +} + +static smartGDDPointer mapTimeDoubleToGdd(void* v,aitIndex count) +{ + dbr_time_double* dbv = (dbr_time_double*)v; + smartGDDPointer dd=mapDoubleToGdd(&dbv->value,count); + dd->setStatSevr(dbv->status,dbv->severity); + dd->setTimeStamp(&dbv->stamp); + return dd; +} + +static int mapTimeGddToDouble(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + dbr_time_double* dbv = (dbr_time_double*)v; + dd.getStatSevr(dbv->status,dbv->severity); + dd.getTimeStamp(&dbv->stamp); + dbv->RISC_pad = 0; // shut up purify + return mapGddToDouble(&dbv->value,count,dd, enumStringTable); +} + +// ******************************************************************** +// graphic structure mappings +// ******************************************************************** + +// -------------map the short structures---------------- +static smartGDDPointer mapGraphicShortToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_gr_short* db = (dbr_gr_short*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_GR_SHORT].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_gr_short_value]; + + aitString* str=NULL; + (*dd)[gddAppTypeIndex_dbr_gr_short_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_gr_short_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_short_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_short_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_short_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_short_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_gr_short_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumInt16,1,&count); + else vdd.setPrimType(aitEnumInt16); + vdd.setBound(0,0,count); + + dbr_short_t* pCopy = (dbr_short_t*) new char [sizeof(dbr_short_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_short_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static smartGDDPointer mapControlShortToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_ctrl_short* db = (dbr_ctrl_short*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_CTRL_SHORT].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_ctrl_short_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_ctrl_short_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_controlLow]=db->lower_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_controlHigh]=db->upper_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_short_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumInt16,1,&count); + else vdd.setPrimType(aitEnumInt16); + vdd.setBound(0,0,count); + + dbr_short_t* pCopy = (dbr_short_t*) new char [sizeof(dbr_short_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_short_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static int mapGraphicGddToShort(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_gr_short* db = (dbr_gr_short*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_gr_short_value]; + + dd[gddAppTypeIndex_dbr_gr_short_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_gr_short_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_gr_short_graphicHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_gr_short_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_gr_short_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_gr_short_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_gr_short_alarmHighWarning]; + + vdd.getStatSevr(db->status,db->severity); + return mapGddToShort(&db->value,count,vdd, enumStringTable); +} + +static int mapControlGddToShort(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_ctrl_short* db = (dbr_ctrl_short*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_ctrl_short_value]; + + dd[gddAppTypeIndex_dbr_ctrl_short_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_short_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_short_graphicHigh]; + db->lower_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_short_controlLow]; + db->upper_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_short_controlHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_short_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_short_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_short_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_short_alarmHighWarning]; + + vdd.getStatSevr(db->status,db->severity); + return mapGddToShort(&db->value,count,vdd, enumStringTable); +} + +// -------------map the float structures---------------- +static smartGDDPointer mapGraphicFloatToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_gr_float* db = (dbr_gr_float*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_GR_FLOAT].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_gr_float_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_gr_float_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_gr_float_precision]=db->precision; + (*dd)[gddAppTypeIndex_dbr_gr_float_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_float_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_float_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_float_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_float_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_gr_float_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumFloat32,1,&count); + else vdd.setPrimType(aitEnumFloat32); + vdd.setBound(0,0,count); + + dbr_float_t* pCopy = (dbr_float_t*) new char [sizeof(dbr_float_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_float_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static smartGDDPointer mapControlFloatToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_ctrl_float* db = (dbr_ctrl_float*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_CTRL_FLOAT].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_ctrl_float_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_ctrl_float_precision]=db->precision; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_controlLow]=db->lower_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_controlHigh]=db->upper_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_float_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumFloat32,1,&count); + else vdd.setPrimType(aitEnumFloat32); + vdd.setBound(0,0,count); + + dbr_float_t* pCopy = (dbr_float_t*) new char [sizeof(dbr_float_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_float_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static int mapGraphicGddToFloat(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_gr_float* db = (dbr_gr_float*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_gr_float_value]; + + dd[gddAppTypeIndex_dbr_gr_float_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->precision=dd[gddAppTypeIndex_dbr_gr_float_precision]; + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_gr_float_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_gr_float_graphicHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_gr_float_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_gr_float_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_gr_float_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_gr_float_alarmHighWarning]; + db->RISC_pad0 = 0; // shut up purify + + vdd.getStatSevr(db->status,db->severity); + return mapGddToFloat(&db->value,count,vdd, enumStringTable); +} + +static int mapControlGddToFloat(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_ctrl_float* db = (dbr_ctrl_float*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_ctrl_float_value]; + + dd[gddAppTypeIndex_dbr_ctrl_float_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->precision=dd[gddAppTypeIndex_dbr_ctrl_float_precision]; + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_float_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_float_graphicHigh]; + db->lower_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_float_controlLow]; + db->upper_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_float_controlHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_float_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_float_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_float_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_float_alarmHighWarning]; + db->RISC_pad = 0; // shut up purify + + vdd.getStatSevr(db->status,db->severity); + return mapGddToFloat(&db->value,count,vdd, enumStringTable); +} + +// -------------map the enum structures---------------- +static smartGDDPointer mapGraphicEnumToGdd(void* v, aitIndex /*count*/) +{ + dbr_gr_enum* db = (dbr_gr_enum*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_GR_ENUM].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_gr_enum_value]; + gdd& menu = (*dd)[gddAppTypeIndex_dbr_gr_enum_enums]; + aitFixedString* str = menu; + //aitFixedString* f = (aitFixedString*)db->strs; + aitIndex sz,i; + + // int i; + // old way using aitString menu + // for(i=0;ino_str;i++) str[i]=((const char*)&(db->strs[i][0])); + + if(menu.dataPointer()==NULL || !menu.isAtomic()) + { + // need to copy the menu chunk from dbr to aitFixedString chunk + menu.setDimension(1); + sz=db->no_str; + str=new aitFixedString[db->no_str]; + menu.putRef(str,new dbMapperFixedStringDestructor); + } + else + { + if((sz=menu.getDataSizeElements())>(aitIndex)db->no_str) + sz=db->no_str; + } + + unsigned minl = epicsMin(sizeof(aitFixedString),sizeof(str[0].fixed_string)) - 1; + for (i=0;istrs[i][0]), minl); + memset(&str[i].fixed_string[minl], '\0', sizeof(aitFixedString)-minl); + } + menu.setBound(0,0,sz); + + // should always be a scaler + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + vdd.setStatSevr(db->status,db->severity); + return dd; +} + +static smartGDDPointer mapControlEnumToGdd(void* v, aitIndex /*count*/) +{ + dbr_ctrl_enum* db = (dbr_ctrl_enum*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_CTRL_ENUM].app); + gdd& menu = (*dd)[gddAppTypeIndex_dbr_ctrl_enum_enums]; + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_ctrl_enum_value]; + aitFixedString* str = menu; + //aitFixedString* f = (aitFixedString*)db->strs; + aitIndex sz,i; + + // int i; + // old way using aitString menu + // for(i=0;ino_str;i++) str[i]=((const char*)&(db->strs[i][0])); + + if(menu.dataPointer()==NULL || !menu.isAtomic()) + { + // need to copy the menu chunk from dbr to aitFixedString chunk + menu.setDimension(1); + sz=db->no_str; + str=new aitFixedString[db->no_str]; + menu.putRef(str,new dbMapperFixedStringDestructor); + } + else + { + if((sz=menu.getDataSizeElements())>(aitIndex)db->no_str) + sz=db->no_str; + } + + unsigned minl = epicsMin(sizeof(aitFixedString),(size_t)MAX_ENUM_STRING_SIZE) - 1; + for (i=0;istrs[i][0]), minl); + memset(&str[i].fixed_string[minl], '\0', sizeof(aitFixedString)-minl); + } + menu.setBound(0,0,sz); + + // should always be a scaler + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + vdd.setStatSevr(db->status,db->severity); + return dd; +} + +static int mapGraphicGddToEnum ( + void * v, aitIndex count, const gdd & dd, + const gddEnumStringTable & enumStringTable ) +{ + dbr_gr_enum * db = ( dbr_gr_enum * ) v; + const gdd & vdd = dd[gddAppTypeIndex_dbr_gr_enum_value]; + + vdd.getStatSevr ( db->status, db->severity ); + unsigned noStr = enumStringTable.numberOfStrings (); + if ( noStr < MAX_ENUM_STATES ) { + db->no_str = static_cast < dbr_short_t > ( noStr ); + } + else { + db->no_str = MAX_ENUM_STATES; + } + for ( int i=0; i < db->no_str; i++ ) { + enumStringTable.getString ( i, + &(db->strs[i][0]), MAX_ENUM_STRING_SIZE ); + } + for ( int j = db->no_str; j < MAX_ENUM_STATES; j++ ) { + db->strs[j][0] = '\0'; + } + return mapGddToEnum ( &db->value, + count, vdd, enumStringTable ); +} + +static int mapControlGddToEnum ( + void * v, aitIndex count, const gdd & dd, + const gddEnumStringTable & enumStringTable ) +{ + dbr_ctrl_enum* db = ( dbr_ctrl_enum * ) v; + const gdd & vdd = dd[gddAppTypeIndex_dbr_ctrl_enum_value]; + + vdd.getStatSevr ( db->status, db->severity ); + unsigned noStr = enumStringTable.numberOfStrings (); + if ( noStr < MAX_ENUM_STATES ) { + db->no_str = static_cast < dbr_short_t > ( noStr ); + } + else { + db->no_str = MAX_ENUM_STATES; + } + for ( int i=0; i < db->no_str; i++ ) { + enumStringTable.getString ( i, + &(db->strs[i][0]), MAX_ENUM_STRING_SIZE ); + } + for ( int j = db->no_str; j < MAX_ENUM_STATES; j++ ) { + db->strs[j][0] = '\0'; + } + return mapGddToEnum ( &db->value, + count, vdd, enumStringTable ); +} + +// -------------map the char structures---------------- +static smartGDDPointer mapGraphicCharToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_gr_char* db = (dbr_gr_char*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_GR_CHAR].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_gr_char_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_gr_char_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_gr_char_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_char_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_char_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_char_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_char_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_gr_char_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumInt8,1,&count); + else vdd.setPrimType(aitEnumInt8); + vdd.setBound(0,0,count); + + dbr_char_t* pCopy = (dbr_char_t *) new char [sizeof(dbr_char_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_char_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static smartGDDPointer mapControlCharToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_ctrl_char* db = (dbr_ctrl_char*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_CTRL_CHAR].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_ctrl_char_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_ctrl_char_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_controlLow]=db->lower_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_controlHigh]=db->upper_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_char_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumInt8,1,&count); + else vdd.setPrimType(aitEnumInt8); + vdd.setBound(0,0,count); + + dbr_char_t* pCopy = (dbr_char_t*) new char [sizeof(dbr_char_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_char_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static int mapGraphicGddToChar(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_gr_char* db = (dbr_gr_char*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_gr_char_value]; + + dd[gddAppTypeIndex_dbr_gr_char_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_gr_char_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_gr_char_graphicHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_gr_char_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_gr_char_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_gr_char_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_gr_char_alarmHighWarning]; + db->RISC_pad = 0; + + vdd.getStatSevr(db->status,db->severity); + return mapGddToChar(&db->value,count,vdd, enumStringTable); +} + +static int mapControlGddToChar(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_ctrl_char* db = (dbr_ctrl_char*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_ctrl_char_value]; + + dd[gddAppTypeIndex_dbr_ctrl_char_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_char_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_char_graphicHigh]; + db->lower_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_char_controlLow]; + db->upper_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_char_controlHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_char_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_char_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_char_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_char_alarmHighWarning]; + db->RISC_pad = '\0'; // shut up purify + + vdd.getStatSevr(db->status,db->severity); + return mapGddToChar(&db->value,count,vdd, enumStringTable); +} + +// -------------map the long structures---------------- +static smartGDDPointer mapGraphicLongToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_gr_long* db = (dbr_gr_long*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_GR_LONG].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_gr_long_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_gr_long_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_gr_long_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_long_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_long_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_long_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_long_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_gr_long_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumInt32,1,&count); + else vdd.setPrimType(aitEnumInt32); + vdd.setBound(0,0,count); + + dbr_long_t* pCopy = (dbr_long_t*) new char [sizeof(dbr_long_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_long_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static smartGDDPointer mapControlLongToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_ctrl_long* db = (dbr_ctrl_long*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_CTRL_LONG].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_ctrl_long_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_ctrl_long_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_controlLow]=db->lower_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_controlHigh]=db->upper_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_long_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumInt32,1,&count); + else vdd.setPrimType(aitEnumInt32); + vdd.setBound(0,0,count); + + dbr_long_t* pCopy = (dbr_long_t *)new char [sizeof(dbr_long_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_long_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static int mapGraphicGddToLong(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_gr_long* db = (dbr_gr_long*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_gr_long_value]; + + dd[gddAppTypeIndex_dbr_gr_long_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_gr_long_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_gr_long_graphicHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_gr_long_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_gr_long_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_gr_long_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_gr_long_alarmHighWarning]; + + vdd.getStatSevr(db->status,db->severity); + return mapGddToLong(&db->value,count,vdd, enumStringTable); +} + +static int mapControlGddToLong(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_ctrl_long* db = (dbr_ctrl_long*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_ctrl_long_value]; + + dd[gddAppTypeIndex_dbr_ctrl_long_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_long_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_long_graphicHigh]; + db->lower_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_long_controlLow]; + db->upper_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_long_controlHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_long_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_long_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_long_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_long_alarmHighWarning]; + + vdd.getStatSevr(db->status,db->severity); + return mapGddToLong(&db->value,count,vdd, enumStringTable); +} + +// -------------map the double structures---------------- +static smartGDDPointer mapGraphicDoubleToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_gr_double* db = (dbr_gr_double*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_GR_DOUBLE].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_gr_double_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_gr_double_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_gr_double_precision]=db->precision; + (*dd)[gddAppTypeIndex_dbr_gr_double_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_double_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_gr_double_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_double_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_gr_double_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_gr_double_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumFloat64,1,&count); + else vdd.setPrimType(aitEnumFloat64); + vdd.setBound(0,0,count); + + dbr_double_t* pCopy = (dbr_double_t *) new char [sizeof(dbr_double_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_double_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static smartGDDPointer mapControlDoubleToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_ctrl_double* db = (dbr_ctrl_double*)v; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_CTRL_DOUBLE].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_ctrl_double_value]; + + aitString* str = NULL; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_units].getRef(str); + str->copy(db->units); + + (*dd)[gddAppTypeIndex_dbr_ctrl_double_precision]=db->precision; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_graphicLow]=db->lower_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_graphicHigh]=db->upper_disp_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_controlLow]=db->lower_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_controlHigh]=db->upper_ctrl_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_alarmLow]=db->lower_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_alarmHigh]=db->upper_alarm_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_alarmLowWarning]=db->lower_warning_limit; + (*dd)[gddAppTypeIndex_dbr_ctrl_double_alarmHighWarning]=db->upper_warning_limit; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd=db->value; + } else { + if(vdd.dimension()!=1) vdd.reset(aitEnumFloat64,1,&count); + else vdd.setPrimType(aitEnumFloat64); + vdd.setBound(0,0,count); + + dbr_double_t* pCopy = (dbr_double_t *) new char [sizeof(dbr_double_t)*count]; + memcpy (pCopy,&db->value,sizeof(dbr_double_t)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static int mapGraphicGddToDouble(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_gr_double* db = (dbr_gr_double*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_gr_double_value]; + + dd[gddAppTypeIndex_dbr_gr_double_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->precision=dd[gddAppTypeIndex_dbr_gr_double_precision]; + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_gr_double_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_gr_double_graphicHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_gr_double_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_gr_double_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_gr_double_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_gr_double_alarmHighWarning]; + db->RISC_pad0 = 0; // shut up purify + + vdd.getStatSevr(db->status,db->severity); + return mapGddToDouble(&db->value,count,vdd, enumStringTable); +} + +static int mapControlGddToDouble(void* v, aitIndex count, const gdd & dd, const gddEnumStringTable &enumStringTable) +{ + const aitString* str; + dbr_ctrl_double* db = (dbr_ctrl_double*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_ctrl_double_value]; + + dd[gddAppTypeIndex_dbr_ctrl_double_units].getRef(str); + if(str->string()) { + strncpy(db->units,str->string(), sizeof(db->units)); + db->units[sizeof(db->units)-1u] = '\0'; + } + + db->precision=dd[gddAppTypeIndex_dbr_ctrl_double_precision]; + db->lower_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_double_graphicLow]; + db->upper_disp_limit=dd[gddAppTypeIndex_dbr_ctrl_double_graphicHigh]; + db->lower_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_double_controlLow]; + db->upper_ctrl_limit=dd[gddAppTypeIndex_dbr_ctrl_double_controlHigh]; + db->lower_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_double_alarmLow]; + db->upper_alarm_limit=dd[gddAppTypeIndex_dbr_ctrl_double_alarmHigh]; + db->lower_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_double_alarmLowWarning]; + db->upper_warning_limit=dd[gddAppTypeIndex_dbr_ctrl_double_alarmHighWarning]; + db->RISC_pad0 = '\0'; // shut up purify + + vdd.getStatSevr(db->status,db->severity); + return mapGddToDouble(&db->value,count,vdd, enumStringTable); +} + +static smartGDDPointer mapStsAckStringToGdd(void* v, aitIndex count) +{ + // must be a container + dbr_stsack_string* db = (dbr_stsack_string*)v; + aitFixedString* dbv = (aitFixedString*)db->value; + smartGDDPointer dd = type_table->getDD(gddDbrToAit[DBR_STSACK_STRING].app); + gdd& vdd = (*dd)[gddAppTypeIndex_dbr_stsack_string_value]; + + (*dd)[gddAppTypeIndex_dbr_stsack_string_ackt]=db->ackt; + (*dd)[gddAppTypeIndex_dbr_stsack_string_acks]=db->acks; + + vdd.setStatSevr(db->status,db->severity); + + if(count==1) { + if(vdd.dimension()) vdd.clear(); + vdd.put(*dbv); + } else { + if(vdd.dimension()!=1) + vdd.reset(aitEnumFixedString,1,&count); + else vdd.setPrimType(aitEnumFixedString); + vdd.setBound(0,0,count); + + aitFixedString* pCopy = (aitFixedString*) new char [sizeof(aitFixedString)*count]; + memcpy (pCopy,&db->value,sizeof(aitFixedString)*count); + vdd.putRef(pCopy, new gddDestructor); + } + return dd; +} + +static int mapStsAckGddToString(void* v, aitIndex count, const gdd &dd, + const gddEnumStringTable &enumStringTable) +{ + dbr_stsack_string* db = (dbr_stsack_string*)v; + const gdd& vdd = dd[gddAppTypeIndex_dbr_stsack_string_value]; + + db->ackt=dd[gddAppTypeIndex_dbr_stsack_string_ackt]; + db->acks=dd[gddAppTypeIndex_dbr_stsack_string_acks]; + + // Unlike all the others, which are dbr_short_t, status and + // severity for a dbr_stsack_string are dbr_ushort_t. Have to + // convert. + dbr_short_t st,sv; + vdd.getStatSevr(st,sv); + db->status = (dbr_ushort_t)st; + db->severity = (dbr_ushort_t)sv; + return mapGddToString (&db->value, count, vdd, enumStringTable); +} + +// -------------map the ack elements -------------------- +static smartGDDPointer mapAcktToGdd(void* v,aitIndex count) { + dbr_put_ackt_t* sv = (dbr_put_ackt_t*)v; + smartGDDPointer dd; + + // Count should be 1, but leave it general + if(count>1) { + dd=new gddAtomic(gddDbrToAit[DBR_PUT_ACKT].app, + gddDbrToAit[DBR_PUT_ACKT].type,1,count); + dd->unreference(); + dbr_put_ackt_t* pCopy = (dbr_put_ackt_t*) new char [sizeof(dbr_put_ackt_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_put_ackt_t)*count); + dd->putRef(pCopy, new gddDestructor); + } else { + dd=new gddScalar(gddDbrToAit[DBR_PUT_ACKT].app); + dd->unreference(); + *dd=*sv; + } + return dd; +} + +static int mapGddToAckt(void* vd, aitIndex count, const gdd & dd, + const gddEnumStringTable &enumStringTable) { + dbr_put_ackt_t* sv = (dbr_put_ackt_t*)vd; + aitIndex sz = dd.getDataSizeElements(); + const void* v=dd.dataVoid(); + int status; + + // Count should be 1, but leave it general + if (count==sz) { + if (local_data_format==aitLocalDataFormat) { + if((dbr_put_ackt_t*)v!=sv) { + status = + aitConvert(aitEnumUint16,sv, + dd.primitiveType(),v,sz,&enumStringTable); + } + else { + status = sz*sizeof(dbr_put_ackt_t); + } + } + else { + status = + aitConvertToNet(aitEnumUint16,sv, + dd.primitiveType(),v,sz, &enumStringTable); + } + } + else { + status = -1; + } + + return status; +} + +static smartGDDPointer mapAcksToGdd(void* v,aitIndex count) { + dbr_put_acks_t* sv = (dbr_put_acks_t*)v; + smartGDDPointer dd; + + // Count should be 1, but leave it general + if(count>1) { + dd=new gddAtomic(gddDbrToAit[DBR_PUT_ACKS].app, + gddDbrToAit[DBR_PUT_ACKS].type,1,count); + dd->unreference(); + dbr_put_acks_t* pCopy = (dbr_put_acks_t*) new char [sizeof(dbr_put_acks_t)*count]; + memcpy (pCopy,sv,sizeof(dbr_put_acks_t)*count); + dd->putRef(pCopy, new gddDestructor); + } else { + dd=new gddScalar(gddDbrToAit[DBR_PUT_ACKS].app); + dd->unreference(); + *dd=*sv; + } + return dd; +} + +static int mapGddToAcks(void* vd, aitIndex count, const gdd &dd, + const gddEnumStringTable &enumStringTable) { + dbr_put_acks_t* sv = (dbr_put_acks_t*)vd; + aitIndex sz = dd.getDataSizeElements(); + const void* v=dd.dataVoid(); + int status; + + // Count should be 1, but leave it general + if (count==sz) { + if (local_data_format==aitLocalDataFormat) { + if((dbr_put_acks_t*)v!=sv) { + status = + aitConvert(aitEnumUint16,sv, + dd.primitiveType(),v,sz, &enumStringTable); + } + else { + status = sz*sizeof(dbr_put_acks_t); + } + } + else { + status = + aitConvertToNet(aitEnumUint16,sv, + dd.primitiveType(),v,sz, &enumStringTable); + } + } + else { + status = -1; + } + + return status; +} + +// -------------map the class name element -------------- + +static smartGDDPointer mapClassNameToGdd(void* v,aitIndex count) { + aitFixedString* db = (aitFixedString*)v; + aitEnum to_type = gddDbrToAit[DBR_CLASS_NAME].type; + aitUint16 to_app = gddDbrToAit[DBR_CLASS_NAME].app; + smartGDDPointer dd; + + // Count should be 1, but leave it general + if(count<=1) + { + dd=new gddScalar(to_app,to_type); + dd->unreference(); + dd->put(*db); + } + else + { + dd=new gddAtomic(to_app,to_type,1,count); + dd->unreference(); + aitFixedString* pCopy = (aitFixedString*) new char [sizeof(aitFixedString)*count]; + memcpy (pCopy,db,sizeof(aitFixedString)*count); + dd->putRef(pCopy, new gddDestructor); + } + return dd; +} + +static int mapGddToClassName(void* vd, aitIndex count, const gdd & dd, + const gddEnumStringTable &enumStringTable) { + aitFixedString* db = (aitFixedString*)vd; + aitIndex sz = dd.getDataSizeElements(); + const void* v = dd.dataVoid(); + int status; + + // Count should be 1, but leave it general + if (count<=sz) { + if(local_data_format==aitLocalDataFormat) { + if((aitFixedString*)v!=db) { + status = + aitConvert(aitEnumFixedString,db, + dd.primitiveType(),v,count, &enumStringTable); + } + else { + status = sz*sizeof(aitFixedString); + } + } + else { + status = + aitConvertToNet(aitEnumFixedString,db, + dd.primitiveType(),v,count, &enumStringTable); + } + } + else { + status = -1; + } + + return status; +} + + +// ----------------must run this to use mapping functions-------------- +epicsShareFunc void gddMakeMapDBR(gddApplicationTypeTable& tt) { gddMakeMapDBR(&tt); } +epicsShareFunc void gddMakeMapDBR(gddApplicationTypeTable* tt) +{ + type_table=tt; + size_t i; + + // Storing the DBRxxx type code in the app table will not work + // for most of the types. This is because many share the same + // app type "value", this includes the normal, sts, and time structures + + for(i=0;igetApplicationType(gddDbrToAit[i].app_name); + tt->storeValue(gddDbrToAit[i].app,i); + } +} + +// +// dbMapperFixedStringDestructor::run() +// +// special gddDestructor guarantees same form of new and delete +// +void dbMapperFixedStringDestructor::run (void *pUntyped) +{ + aitFixedString *ps = (aitFixedString *) pUntyped; + delete [] ps; +} + +// An array of one function per DBR structure is provided here so conversions +// can take place quickly by knowing the DBR enumerated type. +// +// C++ will not make a const decl external linkage unless +// we explicitly specify extern +// +extern epicsShareDef const gddDbrMapFuncTable gddMapDbr[DBM_N_DBR_TYPES] = { + { mapStringToGdd, mapGddToString }, // DBR_STRING + { mapShortToGdd, mapGddToShort }, // DBR_SHORT + { mapFloatToGdd, mapGddToFloat }, // DBR_FLOAT + { mapEnumToGdd, mapGddToEnum }, // DBR_ENUM + { mapCharToGdd, mapGddToChar }, // DBR_CHAR + { mapLongToGdd, mapGddToLong }, // DBR_LONG + { mapDoubleToGdd, mapGddToDouble }, // DBR_DOUBLE + { mapStsStringToGdd, mapStsGddToString }, // DBR_STS_STRING + { mapStsShortToGdd, mapStsGddToShort }, // DBR_STS_SHORT + { mapStsFloatToGdd, mapStsGddToFloat }, // DBR_STS_FLOAT + { mapStsEnumToGdd, mapStsGddToEnum }, // DBR_STS_ENUM + { mapStsCharToGdd, mapStsGddToChar }, // DBR_STS_CHAR + { mapStsLongToGdd, mapStsGddToLong }, // DBR_STS_LONG + { mapStsDoubleToGdd, mapStsGddToDouble }, // DBR_STS_DOUBLE + { mapTimeStringToGdd, mapTimeGddToString }, // DBR_TIME_STRING + { mapTimeShortToGdd, mapTimeGddToShort }, // DBR_TIME_SHORT + { mapTimeFloatToGdd, mapTimeGddToFloat }, // DBR_TIME_FLOAT + { mapTimeEnumToGdd, mapTimeGddToEnum }, // DBR_TIME_ENUM + { mapTimeCharToGdd, mapTimeGddToChar }, // DBR_TIME_CHAR + { mapTimeLongToGdd, mapTimeGddToLong }, // DBR_TIME_LONG + { mapTimeDoubleToGdd, mapTimeGddToDouble }, // DBR_TIME_DOUBLE + { mapStsStringToGdd, mapStsGddToString }, // DBR_GR_STRING + { mapGraphicShortToGdd, mapGraphicGddToShort }, // DBR_GR_SHORT + { mapGraphicFloatToGdd, mapGraphicGddToFloat }, // DBR_GR_FLOAT + { mapGraphicEnumToGdd, mapGraphicGddToEnum }, // DBR_GR_ENUM + { mapGraphicCharToGdd, mapGraphicGddToChar }, // DBR_GR_CHAR + { mapGraphicLongToGdd, mapGraphicGddToLong }, // DBR_GR_LONG + { mapGraphicDoubleToGdd,mapGraphicGddToDouble }, // DBR_GR_DOUBLE + { mapStsStringToGdd, mapStsGddToString }, // DBR_CTRL_STRING + { mapControlShortToGdd, mapControlGddToShort }, // DBR_CTRL_SHORT + { mapControlFloatToGdd, mapControlGddToFloat }, // DBR_CTRL_FLOAT + { mapControlEnumToGdd, mapControlGddToEnum }, // DBR_CTRL_ENUM + { mapControlCharToGdd, mapControlGddToChar }, // DBR_CTRL_CHAR + { mapControlLongToGdd, mapControlGddToLong }, // DBR_CTRL_LONG + { mapControlDoubleToGdd,mapControlGddToDouble }, // DBR_CTRL_DOUBLE + { mapAcktToGdd, mapGddToAckt }, // DBR_PUT_ACKT + { mapAcksToGdd, mapGddToAcks }, // DBR_PUT_ACKS + { mapStsAckStringToGdd, mapStsAckGddToString }, // DBR_STSACK_STRING + { mapClassNameToGdd, mapGddToClassName } // DBR_CLASS_NAME +}; + +#if DBM_N_DBR_TYPES != (LAST_BUFFER_TYPE+1) +#error db mapper is out of sync with db_access.h +#endif + +extern epicsShareDef const unsigned gddMapDbrNElem = + sizeof(gddMapDbr)/sizeof(gddDbrToAit[0]); + diff --git a/src/gdd/dbMapper.h b/src/gdd/dbMapper.h new file mode 100644 index 000000000..0b9f53dff --- /dev/null +++ b/src/gdd/dbMapper.h @@ -0,0 +1,66 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef DB_MAPPER_H +#define DB_MAPPER_H + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#include "shareLib.h" +#include "aitTypes.h" +#include "gdd.h" +#include "smartGDDPointer.h" + +#include "db_access.h" +#include "cadef.h" + +class gddApplicationTypeTable; + +// Function proto to convert from a db_struct to a gdd. The gdd will +// reference the data in the db_struct if the db_struct points to an +// array. The second argument is the number of elements if db_struct +// represents an array, or zero if the db_struct is a scalar. +typedef smartGDDPointer (*to_gdd)(void* db_struct, aitIndex element_count); + +// Function proto to convert from a gdd to a dbr structure, returns the +// number of elements that the value field of db_struct points to if the +// gdd points to an array or -1 if the number of elements in the value +// field is not identical to element_count available in db_struct. +typedef int (*to_dbr)(void* db_struct, aitIndex element_count, + const gdd &, const gddEnumStringTable &enumStringTable); + +struct gddDbrMapFuncTable { + to_gdd conv_gdd; + to_dbr conv_dbr; +}; +typedef struct gddDbrMapFuncTable gddDbrMapFuncTable; + +struct gddDbrToAitTable { + aitEnum type; + aitUint16 app; + const char* app_name; +}; +typedef struct gddDbrToAitTable gddDbrToAitTable; + +#define DBM_N_DBR_TYPES 39 +epicsShareExtern gddDbrToAitTable gddDbrToAit[DBM_N_DBR_TYPES]; +epicsShareExtern const int gddAitToDbr[aitConvertLast+1]; +epicsShareExtern const gddDbrMapFuncTable gddMapDbr[DBM_N_DBR_TYPES]; + +epicsShareFunc void gddMakeMapDBR(gddApplicationTypeTable& tt); +epicsShareFunc void gddMakeMapDBR(gddApplicationTypeTable* tt); + +#endif + diff --git a/src/gdd/gdd.cc b/src/gdd/gdd.cc new file mode 100644 index 000000000..8c9e90783 --- /dev/null +++ b/src/gdd/gdd.cc @@ -0,0 +1,1829 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#include +#include +#include + +#include +#include + +#define epicsExportSharedSymbols +#include "gdd.h" + +gdd_NEWDEL_NEW(gdd) +gdd_NEWDEL_DEL(gdd) +gdd_NEWDEL_STAT(gdd) + +epicsMutex * gdd::pGlobalMutex; + +static epicsThreadOnceId gddOnce = EPICS_THREAD_ONCE_INIT; + +class gddFlattenDestructor : public gddDestructor +{ +public: + gddFlattenDestructor(void) { } + gddFlattenDestructor(void* user_arg):gddDestructor(user_arg) { } + void run(void*); +}; + +class gddContainerCleaner : public gddDestructor +{ +public: + gddContainerCleaner(void) { } + gddContainerCleaner(void* user_arg):gddDestructor(user_arg) { } + void run(void*); +}; + +void gddFlattenDestructor::run(void*) +{ + return; +} + +void gddContainerCleaner::run(void* v) +{ + gddContainer* cdd = (gddContainer*)v; + int tot = cdd->total(); + int i; + for(i=0;iremove(0); +} + +// +// special gddDestructor guarantees same form of new and delete +// +class gddAitUint8Destructor: public gddDestructor { + virtual void run (void *); +}; + +// +// special gddDestructor guarantees same form of new and delete +// +class gddAitStringDestructor: public gddDestructor { + virtual void run (void *); +}; + +// --------------------------The gdd functions------------------------- + +extern "C" void gddStaticInit ( void * p ) +{ + epicsMutex * * pMutex = static_cast < epicsMutex * * > ( p ); + *pMutex = new epicsMutex (); +} + +gdd::gdd(int app, aitEnum prim, int dimen) +{ + init(app,prim,dimen); +} + +gdd::gdd(int app, aitEnum prim, int dimen, aitUint32* val) +{ + init(app,prim,dimen); + for(int i=0;iprim_type = prim; + dim=(aitUint8)dimen; + destruct=NULL; + ref_cnt=1; + flags=0; + bounds=NULL; + setStatSevr(0u,0u); + + if(dim) + { + switch(dim) + { + case 1: bounds=(gddBounds*)new gddBounds1D; bounds->set(0,0); break; + case 2: bounds=(gddBounds*)new gddBounds2D; break; + case 3: bounds=(gddBounds*)new gddBounds3D; break; + default: bounds=(gddBounds*)new gddBounds[dim]; break; + } + memset ( & this->data, '\0', sizeof ( this->data ) ); + } + else if(primitiveType()==aitEnumString) + { + aitString* str=(aitString*)dataAddress(); + str->init(); + } + else if (primitiveType()==aitEnumFixedString) + { + this->data.FString = new aitFixedString; + memset ( this->data.FString, '\0', sizeof(aitFixedString) ); + } + else { + memset ( & this->data, '\0', sizeof ( this->data ) ); + } +} + +gdd::gdd(gdd* dd) +{ + // + // added this because the "copy()" below bombs + // if the GDD isnt initialized + // joh - 4-23-99 + // + this->init (dd->appl_type, dd->primitiveType(), dd->dimension()); + + copyInfo(dd); +} + +gdd::~gdd(void) +{ + gdd* dd; + gdd* temp; + + // fprintf(stderr,"A gdd is really being deleted %8.8x!!\n",this); + + if(isContainer()) + { + if(destruct) + destruct->destroy(dataPointer()); + else + { + for(dd=(gdd*)dataPointer();dd;) + { + temp=dd; + dd=dd->next(); + temp->unreference(); + } + freeBounds(); + } + } + else if(isAtomic()) + { + if(destruct) destruct->destroy(dataPointer()); + if(bounds) freeBounds(); + } + else + { + // + // this destroys any scalar string data that may be present + // + this->setPrimType (aitEnumInvalid); + } + this->setApplType (0); + memset ( & this->data, '\0', sizeof ( this->data ) ); +} + +// this routine is private so we dont need to initialize +// string data when changing to a scalar, but we do need +// to be careful about how it is called +void gdd::freeBounds(void) +{ + if(bounds) + { + switch(dim) + { + case 0: + fprintf ( stderr, "gdd: freeing bounds, bounds exist, but gdd is scalar?\n" ); + break; + case 1: { gddBounds1D* d1=(gddBounds1D*)bounds; delete d1; } break; + case 2: { gddBounds2D* d2=(gddBounds2D*)bounds; delete d2; } break; + case 3: { gddBounds3D* d3=(gddBounds3D*)bounds; delete d3; } break; + default: delete [] bounds; break; + } + bounds=NULL; + } + dim=0; +} + +void gdd::setDimension(int d, const gddBounds* bnds) +{ + if ( dim != 0 ) { + if ( isFlat() || isManaged() ) { + throw std::logic_error ( + "sorry: cant change the bounds on an atomic, managed or flat gdd" ); + } + } + if(dim!=d) + { + if ( dim == 0 ) { + // run destructors for scalar string data + if ( primitiveType() == aitEnumFixedString ) + { + // aitString type could have destructors + if ( destruct ) { + destruct->destroy(dataPointer()); + destruct = 0; + } + else + if (data.FString) delete data.FString; + } + else if ( primitiveType() == aitEnumString ) + { + // aitString type could have destructors + if ( destruct ) { + destruct->destroy(dataAddress()); + destruct = 0; + } + else + { + aitString* s = (aitString*)dataAddress(); + s->clear(); + } + } + // changing from scalar to vector so set the + // vector pointer to nill + memset ( & this->data, '\0', sizeof ( this->data ) ); + } + else { + this->freeBounds(); + } + dim=(aitUint8)d; + switch(dim) + { + case 0: bounds=0; break; + case 1: bounds=(gddBounds*)new gddBounds1D; bounds->set(0,0); break; + case 2: bounds=(gddBounds*)new gddBounds2D; break; + case 3: bounds=(gddBounds*)new gddBounds3D; break; + default: bounds=(gddBounds*)new gddBounds[dim]; break; + } + if ( dim == 0 ) { + if ( destruct ) { + destruct->destroy(dataAddress()); + destruct = 0; + } + // run constructers for scalar string data types + if ( primitiveType() == aitEnumString ) { + aitString* str=(aitString*)dataAddress(); + str->init(); + } + else if ( primitiveType() ==aitEnumFixedString ) { + this->data.FString = new aitFixedString; + memset (this->data.FString, '\0', sizeof(aitFixedString)); + } + else { + memset ( & this->data, '\0', sizeof ( this->data ) ); + } + } + } + if ( bnds ) + { + for(int i=0;ireference(); + + if(isContainer()||isFlat()) + markManaged(); + + return 0; +} + +gddStatus gdd::genCopy(aitEnum t, const void* d, aitDataFormat f) +{ + gddStatus rc=0; + + if(isScalar()) + set(t,d,f); + else if(isAtomic()) + { + if(!dataPointer()) + { + if ( primitiveType()==aitEnumString ) { + size_t nElem = describedDataSizeElements (); + aitString * pStrVec = new aitString [ nElem ]; + if ( ! pStrVec ) { + gddAutoPrint("gdd::genCopy()",gddErrorNewFailed); + rc = gddErrorNewFailed; + } + else { + destruct = new gddAitStringDestructor; + if ( destruct ) { + destruct->reference(); + setData ( pStrVec ); + } + else { + delete [] pStrVec; + gddAutoPrint("gdd::genCopy()",gddErrorNewFailed); + rc = gddErrorNewFailed; + } + } + } + else { + size_t sz=describedDataSizeBytes(); + aitUint8 * buf = new aitUint8[sz]; + if ( buf == NULL ) { + gddAutoPrint("gdd::genCopy()",gddErrorNewFailed); + rc=gddErrorNewFailed; + } + else + { + destruct=new gddAitUint8Destructor; + if (destruct==NULL) { + gddAutoPrint("gdd::genCopy()",gddErrorNewFailed); + rc=gddErrorNewFailed; + delete [] buf; + } + else { + setData(buf); + destruct->reference(); + } + } + } + } + if(rc==0) + { + if(f==aitLocalDataFormat) + aitConvert(primitiveType(),dataPointer(),t,d, + getDataSizeElements()); + else + aitConvertFromNet(primitiveType(),dataPointer(),t,d, + getDataSizeElements()); + + markLocalDataFormat(); + } + } + else + { + gddAutoPrint("gdd::genCopy()",gddErrorTypeMismatch); + rc=gddErrorTypeMismatch; + } + + return rc; +} + + +gddStatus gdd::changeType(int app,aitEnum prim) +{ + gddStatus rc=0; + + // this should only be allowed for setting the type if it is + // undefined or if the data is a scalar + + if(isScalar() || primitiveType()==aitEnumInvalid) + { + setApplType(app); + setPrimType(prim); + } + else + { + gddAutoPrint("gdd::changeType()",gddErrorTypeMismatch); + rc=gddErrorTypeMismatch; + } + + return rc; +} + +gddStatus gdd::setBound(unsigned index_dim, aitIndex first, aitIndex count) +{ + gddStatus rc=0; + if(index_dimappl_type); + setPrimType(aitEnumContainer); + setStatSevr(dd->getStat(),dd->getSevr()); + + if(dd->isContainer()) + { + cdd=(gddContainer*)dd; + gddCursor cur=cdd->getCursor(); + for(ndd=cur.first();ndd;ndd=cur.next()) + { + pdd=new gdd(ndd->applicationType(), + ndd->primitiveType(),ndd->dimension()); + pdd->setNext((gdd*)dataPointer()); + setData(pdd); + bounds->setSize(bounds->size()+1); + pdd->copyStuff(ndd,ctype); + } + } + else if(dd->isScalar()) { + if (dd->primitiveType()==aitEnumString) { + aitString* pStrDest =(aitString*)&data; + aitString* pStrSrc =(aitString*)&dd->data; + *pStrDest = *pStrSrc; + } + else if (dd->primitiveType()==aitEnumFixedString) { + aitFixedString* pStrDest =(aitFixedString*)data.Pointer; + aitFixedString* pStrSrc =(aitFixedString*)dd->data.Pointer; + memcpy (pStrDest, pStrSrc, sizeof(aitFixedString)); + } + else { + data=dd->data; + } + } + else // atomic + { + const gddBounds* bnds = dd->getBounds(); + for(unsigned i=0;idimension();i++) bounds[i]=bnds[i]; + + switch(ctype) + { + case 1: // copy() + if ( primitiveType()==aitEnumString ) { + size_t nElem = dd->describedDataSizeElements (); + aitString * pStrVec = new aitString [ nElem ]; + if ( ! pStrVec ) { + gddAutoPrint("gdd::copyStuff()",gddErrorNewFailed); + rc=gddErrorNewFailed; + } + else { + destruct = new gddAitStringDestructor; + if ( destruct ) { + const aitString * pSrc = + static_cast ( dd->dataPointer() ); + for ( unsigned j=0; j < nElem; j++ ) { + pStrVec[j] = pSrc[j]; + } + destruct->reference(); + setData ( pStrVec ); + } + else { + delete [] pStrVec; + gddAutoPrint("gdd::copyStuff()",gddErrorNewFailed); + rc=gddErrorNewFailed; + } + } + } + else { + size_t a_size = dd->getDataSizeBytes(); + aitUint8* array = new aitUint8[a_size]; + if ( array ) { + destruct=new gddAitUint8Destructor; + if (destruct!=NULL) { + destruct->reference(); + memcpy(array,dd->dataPointer(),a_size); + setData(array); + } + else { + delete [] array; + gddAutoPrint("gdd::copyStuff()",gddErrorNewFailed); + rc=gddErrorNewFailed; + } + } + else + { + gddAutoPrint("gdd::copyStuff()",gddErrorNewFailed); + rc=gddErrorNewFailed; + } + } + break; + case 2: // Dup() + data=dd->getData(); // copy the data reference + destruct=dd->destruct; + if(destruct) destruct->reference(); + break; + case 0: // copyInfo() + default: break; + } + } + return rc; +} + +size_t gdd::getDataSizeBytes(void) const +{ + size_t sz=0; + const gdd* pdd; + aitString* str; + + if(isContainer()) + { + const gddContainer* cdd=(const gddContainer*)this; + constGddCursor cur=cdd->getCursor(); + for(pdd=cur.first();pdd;pdd=cur.next()) + sz+=pdd->getTotalSizeBytes(); + } + else + { + if(aitValid(primitiveType())) + { + if(primitiveType()==aitEnumString) + { + if(dimension()) str=(aitString*)dataPointer(); + else str=(aitString*)dataAddress(); + sz+=(size_t)(aitString::totalLength(str, + getDataSizeElements())); + } + else + sz+=(size_t)(getDataSizeElements())*aitSize[primitiveType()]; + } + } + return sz; +} + +size_t gdd::describedDataSizeBytes(void) const +{ + size_t sz=0; + + // does not work well for aitString - only reports the aitString info + + if(!isContainer()) + sz+=(size_t)(describedDataSizeElements())*aitSize[primitiveType()]; + + return sz; +} + +size_t gdd::getTotalSizeBytes(void) const +{ + size_t sz; + unsigned long tsize; + const gdd* pdd; + + // add up size of bounds + size of this DD + sz=sizeof(gdd)+(sizeof(gddBounds)*dimension()); + + // special case the aitString/aitFixedString here - sucks bad + if(isScalar()) + { + if(primitiveType()==aitEnumString) + { + aitString* str=(aitString*)dataAddress(); + sz+=str->length()+1; // include the NULL + } + else if(primitiveType()==aitEnumFixedString) + sz+=sizeof(aitFixedString); + } + else if(isAtomic()) + { + if(aitValid(primitiveType())) + { + if(primitiveType()==aitEnumString) + { + // special case the aitString here + tsize=(size_t)(aitString::totalLength((aitString*)dataPointer(), + getDataSizeElements())); + } + else + tsize=(size_t)(getDataSizeElements())*aitSize[primitiveType()]; + + sz+=(size_t)align8(tsize); // include alignment + } + } + else if(isContainer()) + { + const gddContainer* cdd=(const gddContainer*)this; + constGddCursor cur=cdd->getCursor(); + for(pdd=cur.first();pdd;pdd=cur.next()) + sz+=pdd->getTotalSizeBytes(); + } + return sz; +} + +aitUint32 gdd::getDataSizeElements(void) const +{ + unsigned long total=1u; + unsigned i; + + if(dimension()>0u && dataPointer()) + { + for(i=0u;i0) flat_dd->convertAddressToOffsets(); + return sz; +} + +// IMPORTANT NOTE: +// Currently the user cannot register an empty container as a prototype. +// The destructor will not be installed to clean up the container when +// it is freed. + +// This is an important function +// Data should be flattened as follows: +// 1) all the GDDs, seen as an array an GDDs +// 2) all the bounds info for all the GDDs +// 3) all the data for all GDDs +// +// In addition, the user should be able to flatten GDDs without the data +// and flatten portions or all the data without flattening GDDs + +size_t gdd::flattenWithAddress(void* buf, size_t size, aitIndex* total_dd) +{ + gdd* pdd = (gdd*)buf; + size_t pos,sz,spos; + aitUint32 i; + gddBounds* bnds; + + // copy this gdd (first one) to get things started + // this is done in two passes - one to copy DDs, one for bounds/data + // need to be able to check if the DD has been flattened already + + if((sz=getTotalSizeBytes())>size) return 0; + pdd[0]=*this; + pdd[0].destruct=NULL; + pdd[0].flags=0; + pos=1; + + // not enough to just copy the gdd info if the primitive type is + // aitString or aitFixedString (even if scalar gdd) + // must special case the strings - that really sucks + + if(isScalar()) + { + // here is special case for the string types + if(primitiveType()==aitEnumFixedString) + { + if(data.FString) + memcpy((char*)&pdd[pos],data.FString,sizeof(aitFixedString)); + + pdd[0].data.FString=(aitFixedString*)&pdd[pos]; + } + else if(primitiveType()==aitEnumString) + { + aitString* str=(aitString*)pdd[0].dataAddress(); + if(str->string()) + { + memcpy((char*)&pdd[pos],str->string(),str->length()+1); + str->installBuf((char*)&pdd[pos],str->length(),str->length()+1); + } + else + str->init(); + } + } + else if(isContainer()) + { + // need to check for bounds in the container and flatten them + if(dataPointer()) + { + // process all the container's DDs + spos=pos; + pos+=flattenDDs((gddContainer*)this,&pdd[pos], + size-(pos*sizeof(gdd))); + + // copy all the data from the entire container into the buffer + flattenData(&pdd[0],pos,&pdd[pos],size-(pos*sizeof(gdd))); + + pdd[0].markFlat(); + pdd[0].setData(&pdd[spos]); + } + else + sz=0; // failure should occur - cannot flatten an empty container + } + else if(isAtomic()) + { + // Just copy the data from this DD into the buffer, copy bounds + if(bounds) + { + pdd[0].markFlat(); + bnds=(gddBounds*)(&pdd[pos]); + for(i=0;istring()) + { + memcpy(ptr,str->string(),str->length()+1); + str->installBuf((char *)ptr, str->length(), str->length()+1); + ptr+=str->length()+1; + } + else + str->init(); + } + else if(dd[i].primitiveType()==aitEnumFixedString) + { + if(dd[i].data.FString) + memcpy(ptr,dd[i].data.FString,sizeof(aitFixedString)); + dd[i].data.FString=(aitFixedString*)ptr; + ptr+=sizeof(aitFixedString); + } + } + } + return 0; +} + +int gdd::flattenDDs(gddContainer* dd, void* buf, size_t size) +{ + gdd* ptr=(gdd*)buf; + int i,tot,pos,spos; + gdd *pdd; + gddCursor cur; + + cur=dd->getCursor(); + + // make first pass to copy all the container's DDs into the buffer + for(tot=0,pdd=cur.first();pdd;pdd=pdd->next(),tot++) + { + ptr[tot]=*pdd; + ptr[tot].destruct=NULL; + ptr[tot].setNext(&ptr[tot+1]); + ptr[tot].noReferencing(); + } + ptr[tot-1].setNext(NULL); + + // make second pass to copy all child containers into buffer + for(pos=tot,i=0;ireference(); + } + } + } + return pos; +} + +gddStatus gdd::convertOffsetsToAddress(void) +{ + aitUint8* pdd = (aitUint8*)this; + unsigned long bnds = (unsigned long)(bounds); + unsigned long dp = (unsigned long)(dataPointer()); + gdd* tdd; + gddContainer* cdd; + gddCursor cur; + aitString* str; + aitIndex i; + const char* cstr; + + if(isContainer()) + { + // change bounds and data first + bounds=(gddBounds*)(pdd+bnds); + setData(pdd+dp); + cdd=(gddContainer*)this; + cur=cdd->getCursor(); + + for(tdd=cur.first();tdd;tdd=cur.next()) + { + if(tdd->next()) tdd->setNext((gdd*)(pdd+(unsigned long)tdd->next())); + tdd->convertOffsetsToAddress(); + } + } + else + { + if(isAtomic()) + { + bounds=(gddBounds*)(pdd+bnds); + setData(pdd+dp); + if(primitiveType()==aitEnumString) + { + // force all the strings in the array to offsets + str=(aitString*)dataPointer(); + for(i=0;istring()) + { + cstr=str->string(); + str->installBuf((char *)(pdd+(unsigned long)cstr), + str->length(), str->length()+1u); + } + else + str->init(); + } + } + } + return 0; +} + +gddStatus gdd::convertAddressToOffsets(void) +{ + aitUint8* pdd = (aitUint8*)this; + aitUint8* bnds = (aitUint8*)(bounds); + aitUint8* dp = (aitUint8*)(dataPointer()); + gddContainer* tdd; + gddCursor cur; + gdd *cdd,*ddd; + aitString* str; + aitIndex i; + const char* cstr; + + // does not ensure that all the members of a container are flat! + if(!isFlat()) + { + gddAutoPrint("gdd::convertAddressToOffsets()",gddErrorNotAllowed); + return gddErrorNotAllowed; + } + + if(isContainer()) + { + tdd=(gddContainer*)this; + cur=tdd->getCursor(); + + for(cdd=cur.first();cdd;) + { + ddd=cdd; + cdd=cur.next(); + ddd->convertAddressToOffsets(); + if(cdd) ddd->setNext((gdd*)((aitUint8*)(ddd->next())-pdd)); + } + + // bounds and data of container to offsets + setData((gdd*)(dp-pdd)); + bounds=(gddBounds*)(bnds-pdd); + } + else + { + if(isAtomic()) + { + if(primitiveType()==aitEnumString) + { + // force all the strings in the array to offsets + str=(aitString*)dataPointer(); + for(i=0;istring(); + if(cstr) str->installBuf((char *)(cstr-(const char*)pdd), + str->length(), str->length()+1u); + else str->init(); + } + } + } + return 0; +} + +void gdd::destroyData(void) +{ + if (isScalar()) + { + // this destroys the string types + this->setPrimType (aitEnumInvalid); + memset ( & this->data, '\0', sizeof ( this->data ) ); + } + else { + if(destruct) + { + if(isContainer()) + destruct->destroy(this); + else + destruct->destroy(dataPointer()); + + destruct=NULL; + } + freeBounds(); + this->prim_type = aitEnumInvalid; + memset ( & this->data, '\0', sizeof ( this->data ) ); + } +} + +gddStatus gdd::clearData(void) +{ + gddStatus rc=0; + + if(isContainer()||isManaged()||isFlat()) + { + gddAutoPrint("gdd::clearData()",gddErrorNotAllowed); + rc=gddErrorNotAllowed; + } + else if ( this->isScalar () ) { + // clear scaler types + if ( this->primitiveType() == aitEnumString ) { + aitString * str=(aitString*)dataAddress(); + str->clear(); + } + else if ( this->primitiveType() == aitEnumFixedString ) { + memset ( this->data.FString, '\0', sizeof ( this->data.FString ) ); + } + else { + memset ( & this->data, '\0', sizeof ( this->data ) ); + } + } + else { + if(destruct) + { + destruct->destroy(dataPointer()); + destruct=NULL; + } + setDimension ( 0, 0 ); + } + return rc; +} + +gddStatus gdd::clear(void) +{ + if(isFlat()||isManaged()) + { + gddAutoPrint("gdd::clear()",gddErrorNotAllowed); + return gddErrorNotAllowed; + } + + if(isAtomic()) + { + destroyData(); + } + else if(isContainer()) + { + gddContainer* cdd = (gddContainer*)this; + gddCursor cur = cdd->getCursor(); + gdd *dd,*tdd; + + for(dd=cur.first();dd;) + { + tdd=dd; + dd=cur.next(); + if(tdd->unreference()<0) delete tdd; + } + freeBounds(); + } + + // + // this code clears out aitString and + // aitFixedString scalars (the doc says + // that every field is set to invalid) + // + changeType(0,aitEnumInvalid); + memset ( & this->data, '\0', sizeof ( this->data ) ); + + return 0; +} + +// Curently gives no indication of failure, which is bad. +// Obviously managed or flattened DDs cannot be redone. +// However, a DD that is within a managed or flattened container can +// use this to describe data - how's that for obscure +// This is required if the "value" is to be allowed within a container +// The "value" could be scalar or an array of unknown size. The same is +// true of the enum strings and "units" attribute. + +gddStatus gdd::reset(aitEnum prim, int dimen, aitIndex* cnt) +{ + int i; + gddStatus rc; + + if((rc=clear())==0) + { + setPrimType(prim); + setDimension(dimen); + for(i=0;ifixed_string, + sizeof(d)); + d.fixed_string[sizeof(d)-1u] = '\0'; + } + else + get(aitEnumFixedString,&d); +} + +void gdd::getConvert(aitString& d) const +{ + get(aitEnumString,&d); +} + +void gdd::getConvert(aitFixedString& d) const +{ + get(aitEnumFixedString,d.fixed_string); +} + +gddStatus gdd::put(const aitString& d) +{ + gddStatus rc=0; + if(isScalar()) + { + // + // destroy existing fixed string if it exists + // and construct new aitString object + // + setPrimType(aitEnumString); + aitString* s=(aitString*)dataAddress(); + *s=d; + } + else + { + gddAutoPrint("gdd::put(aitString&)",gddErrorNotAllowed); + rc=gddErrorNotAllowed; + } + + return rc; +} + +gddStatus gdd::put(const aitFixedString& d) +{ + gddStatus rc=0; + + if(isScalar()) + { + this->setPrimType(aitEnumFixedString); + + if (data.FString!=NULL) + memcpy (data.FString->fixed_string, d.fixed_string, sizeof(d.fixed_string)); + } + else + { + gddAutoPrint("gdd::put(aitString&)",gddErrorNotAllowed); + rc=gddErrorNotAllowed; + } + return rc; +} + +void gdd::putConvert(const aitString& d) +{ + set(aitEnumString,&d); +} + +void gdd::putConvert(const aitFixedString& d) +{ + set(aitEnumFixedString,(void*)d.fixed_string); +} + +// copy each of the strings into this DDs storage area +gddStatus gdd::put(const aitString* const d) +{ + return genCopy(aitEnumString,d); +} + +// copy each of the strings into this DDs storage area +gddStatus gdd::put(const aitFixedString* const d) +{ + gddStatus rc=0; + + if(isAtomic()) + if(dataPointer()) + aitConvert(primitiveType(),dataPointer(),aitEnumFixedString,d, + getDataSizeElements()); + else + genCopy(aitEnumFixedString,d); + else + { + gddAutoPrint("gdd::put(const aitFixedString*const)",gddErrorNotAllowed); + rc=gddErrorTypeMismatch; + } + + return rc; +} + +gddStatus gdd::putRef(const gdd*) +{ + gddAutoPrint("gdd::putRef(const gdd*) - NOT IMPLEMENTED", + gddErrorNotSupported); + return gddErrorNotSupported; +} + +gddStatus gdd::put ( const gdd * dd ) +{ + if ( this->isScalar() && dd->isScalar() ) + { + // this is the simple case - just make this scalar look like the other + this->set(dd->primitiveType(),dd->dataVoid()); + } + else if ( isContainer() || dd->isContainer() ) + { + gddAutoPrint("gdd::put(const gdd*)",gddErrorNotSupported); + return gddErrorNotSupported; + } + else if ( this->dimension() > 1 || dd->dimension() > 1 ) + { + // sorry, no support currently for multidimensional arrays + return gddErrorOutOfBounds; + } + else if ( this->isScalar() ) // dd must be atomic if this is true + { + this->set ( dd->primitiveType(), dd->dataPointer() ); + } + // at this point this GDD must be atomic + // and the src gdd is either atomic or scalar + else { + // this must be single dimensional atomic at this point + + // dd can be scaler or single dimensional atomic at this point + // so fetch the bounds carefully + aitUint32 srcFirst; + aitUint32 srcElemCount; + if ( dd->isScalar() ) { + srcFirst = 0; + srcElemCount = 1; + } + else { + srcFirst = dd->getBounds()->first(); + srcElemCount = dd->getBounds()->size(); + } + + // clip to lower limit of source + aitUint32 srcCopyFirst; + if ( this->getBounds()->first () > srcFirst ) { + srcCopyFirst = this->getBounds()->first(); + } + else { + srcCopyFirst = srcFirst; + } + + // clip to upper limit of source + aitUint32 srcCopySize; + const aitUint32 unusedSrcBelow = srcCopyFirst - srcFirst; + if ( srcElemCount <= unusedSrcBelow ) { + return gddErrorOutOfBounds; + } + + aitUint32 srcAvailSize = srcElemCount - unusedSrcBelow; + if ( srcAvailSize > this->getBounds()->size() ) { + srcCopySize = this->getBounds()->size(); + } + else { + srcCopySize = srcAvailSize; + } + + if ( dataVoid() == NULL ) + { + if (primitiveType()==aitEnumInvalid) { + setPrimType (dd->primitiveType()); + } + if ( primitiveType()==aitEnumString ) { + aitString * pStrVec = new aitString [ srcCopySize ]; + if( ! pStrVec ) { + return gddErrorNewFailed; + } + destruct = new gddAitStringDestructor; + if ( destruct ) { + destruct->reference(); + setData ( pStrVec ); + } + else { + delete [] pStrVec; + gddAutoPrint("gdd::copyData(const gdd*)",gddErrorNewFailed); + return gddErrorNewFailed; + } + } + else { + size_t sz = srcCopySize * aitSize[primitiveType()]; + + // allocate a data buffer for the user + aitUint8 * arr = new aitUint8[sz]; + if( ! arr ) { + return gddErrorNewFailed; + } + destruct=new gddAitUint8Destructor; + if (destruct!=NULL) { + destruct->reference(); + setData(arr); + } + else { + delete [] arr; + gddAutoPrint("gdd::copyData(const gdd*)",gddErrorNewFailed); + return gddErrorNewFailed; + } + } + + // the rule is that if storage is not preallocated then its ok + // for the dest bounds to shrink to match the original dest + // bounds intersection with the source data bounds + for ( unsigned i = 0; i < this->dimension(); i++ ) { + if ( i == 0 ) { + this->setBound ( i, srcCopyFirst, srcCopySize ); + } + else { + this->setBound ( i, 0, 1 ); + } + } + } + + aitUint8* pDst = (aitUint8*) this->dataPointer(); + assert ( srcCopyFirst >= this->getBounds()->first() ); + const aitUint32 unusedDstLow = srcCopyFirst - this->getBounds()->first(); + if ( unusedDstLow > 0 ) { + // + // zero portions that dont match + // (should eventually throw an exception ?) + // + aitUint32 byteCount = aitSize[primitiveType()] * unusedDstLow; + memset (pDst, '\0', byteCount); + pDst += byteCount; + } + + aitUint8* pSrc = (aitUint8*) dd->dataVoid(); + pSrc += aitSize[dd->primitiveType()] * unusedSrcBelow; + int gddStatus = aitConvert (this->primitiveType(), pDst, + dd->primitiveType(), pSrc, srcCopySize); + if ( gddStatus < 0 ) { + return gddErrorTypeMismatch; + } + + assert ( this->getBounds()->size() >= srcCopySize + unusedDstLow ); + const aitUint32 unusedDstHigh = this->getBounds()->size() - ( srcCopySize + unusedDstLow ); + if ( unusedDstHigh > 0 ) { + pDst += aitSize[primitiveType()] * srcCopySize; + // + // zero portions that dont match + // (should eventually throw an exception ?) + // + aitUint32 byteCount = aitSize[primitiveType()] * unusedDstHigh; + memset (pDst, '\0', byteCount); + } + } + + setStatSevr(dd->getStat(),dd->getSevr()); + aitTimeStamp ts; + dd->getTimeStamp(&ts); + setTimeStamp(&ts); + + return 0; // success +} + +size_t gdd::outHeader(void* buf,aitUint32 bufsize) const +{ + // simple encoding for now.. will change later + // this is the SLOW, simple version + + aitUint8* b = (aitUint8*)buf; + aitUint8* app = (aitUint8*)&appl_type; + aitUint8* stat = (aitUint8*)&status; + aitUint8* ts_sec = (aitUint8*)&time_stamp.tv_sec; + aitUint8* ts_nsec = (aitUint8*)&time_stamp.tv_nsec; + size_t i,j,sz; + aitIndex ff,ss; + aitUint8 *f,*s; + + // verify that header will fit into buffer first + sz=4+sizeof(status)+sizeof(time_stamp)+sizeof(appl_type)+ + sizeof(prim_type)+sizeof(dim)+(dim*sizeof(gddBounds)); + + if(sz>bufsize) return 0; // blow out here! + + *(b++)='H'; *(b++)='E'; *(b++)='A'; *(b++)='D'; + + // how's this for putrid + *(b++)=dim; + *(b++)=prim_type; + + if(aitLocalNetworkDataFormatSame) + { + *(b++)=app[0]; *(b++)=app[1]; + for(i=0;i0u); + i=sizeof(time_stamp.tv_sec)-1u; do { *(b++)=ts_sec[i]; } while(i-->0u); + i=sizeof(time_stamp.tv_nsec)-1u; do { *(b++)=ts_nsec[i]; } while(i-->0u); + } + + // put out the bounds info + for(j=0;j0u); + i=sizeof(aitIndex)-1u; do { *(b++)=f[i]; } while(i-->0u); + } + } + return sz; +} +size_t gdd::outData(void* buf,aitUint32 bufsize, aitEnum e, aitDataFormat f) const +{ + // put data into user's buffer in the format that the user wants (e/f). + // if e is invalid, then use whatever format this gdd describes. + + aitUint32 sz = getDataSizeElements(); + aitUint32 len = getDataSizeBytes(); + aitEnum type=(e==aitEnumInvalid)?primitiveType():e; + + if(len>bufsize) return 0; // blow out early + + if(sz>0) + { + if(f==aitLocalDataFormat) + aitConvert(type,buf,primitiveType(),dataVoid(),sz); + else + aitConvertToNet(type,buf,primitiveType(),dataVoid(),sz); + } + + return len; +} +size_t gdd::out(void* buf,aitUint32 bufsize,aitDataFormat f) const +{ + size_t index = outHeader(buf,bufsize); + size_t rc; + + if(index>0) + rc=outData(((char*)buf)+index,bufsize-index,aitEnumInvalid,f)+index; + else + rc=0; + + return rc; +} + +size_t gdd::inHeader(void* buf) +{ + // simple encoding for now.. will change later + // this is the SLOW, simple version + + aitUint16 inapp; + aitUint8 inprim; + aitUint8 indim; + + aitUint8* b = (aitUint8*)buf; + aitUint8* b1 = b; + aitUint8* app = (aitUint8*)&inapp; + aitUint8* stat = (aitUint8*)&status; + aitUint8* ts_sec = (aitUint8*)&time_stamp.tv_sec; + aitUint8* ts_nsec = (aitUint8*)&time_stamp.tv_nsec; + size_t i,j; + aitIndex ff,ss; + aitUint8 *f,*s; + + if(strncmp((char*)b,"HEAD",4)!=0) return 0; + b+=4; + + indim=*(b++); + inprim=*(b++); + + if(aitLocalNetworkDataFormatSame) + { + app[0]=*(b++); app[1]=*(b++); + init(inapp,(aitEnum)inprim,indim); + for(i=0u;i0u); + i=sizeof(time_stamp.tv_sec)-1u; do { ts_sec[i]=*(b++); } while(i-->0u); + i=sizeof(time_stamp.tv_nsec)-1u; do { ts_nsec[i]=*(b++); } while(i-->0u); + } + + // read in the bounds info + f=(aitUint8*)&ff; + s=(aitUint8*)&ss; + + for(j=0u;j0u); + i=sizeof(aitIndex)-1u; do { f[i]=*(b++); } while(i-->0u); + } + bounds[j].setFirst(ff); + bounds[j].setSize(ss); + } + return (size_t)(b-b1); +} +size_t gdd::inData(void* buf,aitUint32 tot, aitEnum e, aitDataFormat f) +{ + size_t rc; + + // Get data from a buffer and put it into this gdd. Lots of rules here. + // 1) tot is the total number of elements to copy from buf to this gdd. + // * if tot is zero, then use the element count described in the gdd. + // * if tot is not zero, then set the gdd up as a 1 dimensional array + // with tot elements in it. + // 2) e is the primitive data type of the incoming data. + // * if e is aitEnumInvalid, then use the gdd primitive type. + // * if e is valid and gdd primitive type is invalid, set the gdd + // primitive type to e. + // * if e is valid and so is this gdd primitive type, then convert the + // incoming data from type e to gdd primitive type. + + // Bad error condition, don't do anything. + if(e==aitEnumInvalid && primitiveType()==aitEnumInvalid) + return 0; + + aitIndex sz=tot; + aitEnum src_type=(e==aitEnumInvalid)?primitiveType():e; + aitEnum dest_type=(primitiveType()==aitEnumInvalid)?e:primitiveType(); + + // I'm not sure if this is the best way to do this. + if(sz>0) + reset(dest_type,dimension(),&sz); + + if(genCopy(src_type,buf,f)==0) + rc=getDataSizeBytes(); + else + rc=0; + + return rc; +} +size_t gdd::in(void* buf, aitDataFormat f) +{ + size_t index = inHeader(buf); + size_t rc; + + if(index>0) + rc=inData(((char*)buf)+index,0,aitEnumInvalid,f)+index; + else + rc=0; + + return rc; + +} + +// +// rewrote this to properly construct/destruct +// scalar string types when the prim type changes +// joh 05-22-98 +// +// +void gdd::setPrimType (aitEnum t) +{ + // + // NOOP if there is no change + // + if ( this->prim_type == t ) { + return; + } + + // + // I (joh) assume that something needs to be done when + // the primative type of a container changes. For now I + // assuming that the gdd should be cleared. + // + if(isContainer()) { + this->clear(); + } + + // + // run constructors/destructors for string data + // if it is scalar + // + if (isScalar()) + { + // + // run destructors for existing string data + // + if(primitiveType()==aitEnumFixedString) + { + // aitString type could have destructors + if ( destruct ) { + destruct->destroy(dataPointer()); + destruct = 0; + } + else + if (data.FString) delete data.FString; + } + else if(primitiveType()==aitEnumString) + { + // aitString type could have destructors + if ( destruct ) { + destruct->destroy(dataAddress()); + destruct = 0; + } + else + { + aitString* s = (aitString*)dataAddress(); + s->clear(); + } + } + + // + // run constructors for new string data types + // + if (t==aitEnumString) { + aitString* str=(aitString*)dataAddress(); + str->init(); + } + else if (t==aitEnumFixedString) { + this->data.FString = new aitFixedString; + memset ( this->data.FString, '\0', sizeof(aitFixedString) ); + } + else { + memset ( & this->data, '\0', sizeof(this->data) ); + } + } + + // + // I (joh) assume that Jim intended that + // calling of the destructors for arrays of string + // data when the primitive type changes is handled + // by the application. Not sure - nothing was done + // by Jim to take care of this as far as I can tell. + // + else if(isAtomic()) + { + if ( dataPointer() && destruct ) { + destruct->destroy(dataPointer()); + destruct=NULL; + } + memset (&this->data, '\0', sizeof(this->data)); + } + + this->prim_type = t; +} + +// +// gdd::indexDD() +// +// modified by JOH 4-23-99 so that the correct method +// is used if the container gdd is not organized +// as an array of GDDs in memory (i.e. its not flat) +// +const gdd* gdd::indexDD (aitIndex index) const +{ + aitIndex i; + unsigned nElem; + + if (index==0u) { + return this; + } + + // + // otherwise this had better be a container + // we are indexing + // + assert (this->prim_type==aitEnumContainer); + + // + // catch out of bounds index + // + nElem = getDataSizeElements(); + assert (index<=nElem); + + // + // if the container GDD is "flat" + // + if (this->isFlat()) { + return this + index; + } + + // + // otherwise linear search for it + // + gdd* dd = (gdd*) dataPointer(); + i = nElem; + while (i>index) { + dd=(gdd*)dd->next(); + i--; + } + return dd; +} + +// +// gddAitUint8Destructor::run() +// +// special gddDestructor guarantees same form of new and delete +// +void gddAitUint8Destructor::run (void *pUntyped) +{ + aitUint8 *pui8 = (aitUint8 *) pUntyped; + delete [] pui8; +} + +// +// gddAitStringDestructor::run() +// +// special gddDestructor guarantees same form of new and delete +// +void gddAitStringDestructor::run (void *pUntyped) +{ + aitString *pStr = (aitString *) pUntyped; + delete [] pStr; +} + diff --git a/src/gdd/gdd.gif b/src/gdd/gdd.gif new file mode 100644 index 0000000000000000000000000000000000000000..ab12e2e028e4446cf585be5f33b42bf09d8a185b GIT binary patch literal 4417 zcmV-H5x(w6Nk%v~VWa{20KxzO_!K|@009519smCT|Ns900000000000EC2ui0Hgu> z000C22)f+}*y*TU5yZ>M)j$~<`XsWJk>%MR-&vb3yc&_h!@BhG{a7Zi~kI1BQ z$!t2G(5Q4uty-_xtai)odcWYXcuX#v&*-#z&2GEj@VIs;jK6uCK7Mva>${JhmSJ zxN5sIy*j^&y2Hf9#>dFX%FE2i8qLtr($mr@)YsU_huPfS*U#SJ;?*YO=H!6q>f;&f z?&~G*^2h1(_R#J3`q(4;_kR8Y7Rx8FpfV8#-|iqC#TFVn9!nUoVabzGLXLwuv*OJZI(wE{xA5V! zqgjI8D>`-1)sa!V7F&6OuG2kdlZE}JwQiBMBl!OP6LjdyvWa^%&StoBy3BG zgxk(_TR+2>`fFlgyY-5GgXxi{`ee}u_Za%hT|P+7=q5N^B9+6c==^964ofCY6u9%7JvpiDUFqi z=^v(g;<#ysixN1Gqr;?8YF65vDx{U4=Eth4j9R8>tg`+$?6Jrq{(Bcrqh0}{k>(f` zA*B+q_baejJ}TX?!%ds)x5qB)>ZV$h>JF_shKXgdu!8IEyWl3HskY^D+pCNR!W*x+ zRMu(+pzomDDXP`p%iyH?Cakc$2SdiV|y^iBcE1rzb9j?a<+Uji}1ze%?v8SE%)s5v+UyBK)#Cl7V^aPWlZmj1}_)t zyhO*G^T$r}?DNP;H=U7wM0=|9(Ftd*HFQgN0rYNGi|nSvLJ!^aF<1L6w$8qm?RLsk zlifAaXP!-S+gq3m_QGS=-8J9_z72QWSPxuyf$esicG!LX`@JW*RVY0<;dHMGE8?7X zjxWoMQ(ko8qwk7mr(Wl6c)NtRF1p^JTVZ+7d#jE5&WEd>xai1~uKVe;uPilvtiN1! z?xxG$DzwK3zWdg}_q;aGpj%tL@cQPNDcaGmj`#DI!rr`w3_AWLXPBxVu?j9lmZ(Aht8 z4wIkaGvz61C`ms)5|hX5<{<02&)&5lpZ*L*VmXPo&QU?In!GINDan{dZ6cC;>00O} zuXxWBKy;-4tlv0sCr*LtQ=$fZ=?Ob}$#{ZPp*(zP$gEivXhsyKDD!7ZGm0T<{t|P+ zG^i~4Hbn9R_idlEU&{I?8re}3pR@B;MloTvr zQ6D?hz*g3@oV0B%4*SU1CUmxz{@tl?+ge&hsuikcy(lE%8idTMu&44^>@FM2f&u0h zo!;$Svnnaf%SJ)2TE(tHMCwNFt{}XFrK4!g%iLO8wvfx+L3!_Z-5%(+sGoIVXy-fG zdhwQszD4ZD0BYEcqEfR?fUbbMi(4+fO0QTY$Zvs*;0U{xm?V3Ggx#wG_}&+=&n4@B zc_r8rb2Ps=*e^*lsLBA7*cug%?}ZQC;jI34!G+cD0TI|=58Jq;<;d}gA&21%`xvnt zrtdXB3{TjKSjBjyuzjOU*c1nNvn}Q_ewiR+dqQ}11yOHuYdhfPI+wIpb@G!{E9OFy znaWWl*hI{7U*)zr&Y}GNFIweHStvs-$T7w9eJgbn zjpzKK`5}&WBN|;`;yR<9N$yh4Wf_yXp-e+SM^2 zZgFMJkWf3**7#JispXq$$o5*nHe2y6gsny7Ie3@Gt~HXAjO#a}de;S>bGM_-;2gj@ z*1x;7{ zd){i@QR1?n1rF|Tr_GcfUnVYsPLn9(nxkST}BGF#DR^h=xJF z37bQHkKCD39r6CjNsv1h?wG(CpAf5I-tiI-x#8{x@M;yAbAV4+;67jRpb4$ac)MYojmvt;{1DBrd{tY~cWf=Fb<2<7RnmE)^$85$2z3QdC zI^|_<`7?{$_m>9#-d{g_`4wOEiVvZe_>SXFU;g>4XT2uP-g%S89`t&*aP4jHciH>E zy3SX6;r?+~#^6)`?vFoyKqpQ0#ao@|S?3a4CwO-Ee3>_Q(l_jmZFdsQ}g;?QbX$SZZXhMxC-WcY=fY+dgMd)NYmGh9%zn+_>5z8hw$T7&e(caIEyTX zj9*8PBx#V^_=XS&f~v@o5J!z&*pDdKjwX4MmV}eIv66pykS@81#RrDND25OI36cKj zi6B>GUs#QbL_9vJZ9qwh=p!&{MU>=7lSc`85vgdHQj)hwg|ax6+*UH~w}hCuewR3Z zcgSv@h;PX_jZXkbBvX4*76c z_X2ncmk{BX-k61kd6+|XYKpmoYS~wjnUzMTZkP#{VQGJu`IdRPmJG?5`{$Ucd6yZ8 zN`|6_WGR-vcP)p)PkkwB_h2WisV=BFCHhvIrg@m8Axgq1nNQ=JCzx%*nVZNIl^PhA z$BB>0SrBE?nYT56&&h}_34YxPofw&%%2_ka`Im(`gyBhZ#F?4vshR#BnT-3;70c(G z?@62ADSzsDpV#M}%ZU|jIezy^nPcag-PxbqSs?++h6#G0F({n_YK^}5k`X#)S{I!B z`Jjbap{=Hu`xzq(>W3UkbUSCD4r-m`IVB-#fFo*)`DvohIg~KkAN8qSEozYU)1Wbm zoGGfJzsaDn`JNpJp*MPtsu`plvZ9|kphLQ$MM{AbIioZhqEafHO)8*I+L7^zq)Gat zxR|9ZnWGrGo=n!GKB}FX^r1|up-u{>M!K19YM}x+rWr~N{AHtqybu`R?4PW z8mGZEQF~gXeCelW>QF=~sGzx}Y6_wt>7EsOrIyL4yyl^nI{uAn_NZ%Wrv;g*Na?7c z3ZL7lqht!Gl{%_Lsgs3jsK1G*f9k1=nyF@Drm@@j9ufYObSNJ=R38r+TqNF|Y(1odz4H$xxfeSh0eE z4;YK519F)K8>s7=vfJ9289OHEu&MjXBaYObBugC8{-BEisjzQC4gT7Mr@6Gyz^hFA zD?}TU&hxY*)RQAiwOA{wX_~bH0j(2?K3qEvUMo^z%e0eNwhS@0IE%Jon=&9!o@|RL zXB)QxVT`dMh;%y*!*;cKdzf?ExA$PT6bra|>q~e@xcZPk(2BTgq_!)|xZBf+SL;cX zD?BQAP}>StmAkosWR#lAxuDCe3<|oWOS&O7gQc6gR}*Bgq`I!VFXs2TuRFV#3$L_$ zyL{uhoQ1odB)UVwyTIExm6Nc+TfD4`M#Y=D=*fo3+r06#a?TsPh=wH5yScubD%G34 zE)$m9sJ*T`y`AK}hqS!EE56B_HNzz@8)2`s@Ad{Y1=yB7?Ql%>H7 z48Iz-!JI2?9xTFQ6u~B(yELG{DXc*M+QI}(jVvs}AiSM03`gl|!8c67t5(7TOuja} z!zsjIJ`BUzC%3a(!#oSQ_5hqrd=5jAv`;(@LN>+!;KWwk4Tr0Ud*%v2ObcKf#$r6i zWL(B(e8y;;#%jFAY~03f{Kjw`$8tQ!bX>=Fe8+g4$9lZSeB8%={KtSC$bvk`gj~pm ze8`BL$cntkjNHhM{K$|T$&x(Dlw2RlmVC*WoXMKJ$(-EDp8Uz69Ll0R%A{P%rhLk7 HoB#kjllKmf literal 0 HcmV?d00001 diff --git a/src/gdd/gdd.h b/src/gdd/gdd.h new file mode 100644 index 000000000..603809a4d --- /dev/null +++ b/src/gdd/gdd.h @@ -0,0 +1,519 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_H +#define GDD_H + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +// Still need to handle to network byte order stuff. +// Should always be marked net byte order on CPUs with native network byte +// order. No extra code should be run in this case. This should be +// easy with #defines since the AIT library defines a macro if network +// byte order is not native byte order + +#include +#include +#include +#include + +#include + +#if defined(_WIN32) +#elif defined(vxWorks) +# include +#elif defined(UNIX) + // hopefully a posix compliant OS +# include +# include +#endif + +// gddNewDel.h - a simple bunch of macros to make a class use free lists +// with new/remove operators + +#include "gddNewDel.h" +#include "gddUtils.h" +#include "gddErrorCodes.h" +#include "aitTypes.h" +#include "aitConvert.h" + +class gddContainer; +class gddArray; +class gddScalar; + +struct epicsTimeStamp; +struct timespec; + +// Not Complete in this prototype: +// - Read only DD. +// - Small array management using free lists + +// - need bit in DD to indicate that no referencing is allowed for the DD +// - need bit to indicate that DD should not be modified, for a container +// this means that the container cannot be added to or removed from. + +// Notes: +// gdds do not need to be linked lists. I could have used a container +// to describe arrays of DDs, and manage the commonly used ones (DD array +// sizes on free lists when the container was destroyed. The constructor +// to the container could take the number of DDs the container will +// manage. This would be similar to the gddBounds method. +// +// Has weak support for changing the dimension of a DD. In other words +// it is not easy to get a "value" DD (defaulting to scaler) and change +// the dimension to a two dimensional array. Need to add routines to +// restructure the bounds. +// +// Need to add methods for inserting const pointers into the DD. +// +// Overriding the operator[] can make the linked list contained in the +// container appear as an array. + +// --------------------------------------------------------------------- +// class structure for DDs: +// +// gddScalar +// | +// gddAtomic gddContainer +// | | +// gdd +// +// All the subclasses of gdd are around to simplify creation and use of +// DDs. + +// --------------------------------------------------------------------- +// This is the main Data Descriptor (DD). + +class epicsShareClass gdd +{ +public: + gdd(void); + gdd(gdd*); + gdd(int app); + gdd(int app,aitEnum prim); + gdd(int app,aitEnum prim,int dimen); + gdd(int app,aitEnum prim,int dimen,aitUint32* size_array); + + enum + { + GDD_MANAGED_MASK=0x01, + GDD_FLAT_MASK=0x02, + GDD_NET_MASK=0x04, + GDD_NOREF_MASK=0x08, + GDD_CONSTANT_MASK=0x10 + }; + + unsigned applicationType(void) const; + aitEnum primitiveType(void) const; + unsigned dimension(void) const; + aitType& getData(void); + const aitType& getData(void) const; + aitType* dataUnion(void); + const aitType* dataUnion(void) const; + const gddDestructor* destructor(void) const; + gdd* next(void); + const gdd* next(void) const; + void setNext(gdd*); + + const gddBounds* getBounds(void) const; + const gddBounds* getBounds(int bn) const; + + void dump(void) const; + void test(void); + + void setPrimType(aitEnum t); + void setApplType(int t); + void setDimension(int d,const gddBounds* = NULL); + void destroyData(void); + gddStatus reset(aitEnum primtype,int dimension, aitIndex* dim_counts); + gddStatus clear(void); // clear all fields of the DD, including arrays + gddStatus changeType(int appltype, aitEnum primtype); + gddStatus setBound(unsigned dim_to_set, aitIndex first, aitIndex count); + gddStatus getBound(unsigned dim_to_get, aitIndex& first, aitIndex& count) const; + gddStatus registerDestructor(gddDestructor*); + gddStatus replaceDestructor(gddDestructor*); + const void* dataVoid(void) const; + void* dataVoid(void); + const void* dataAddress(void) const; + void* dataAddress(void); + const void* dataPointer(void) const; + void* dataPointer(void); + const void* dataPointer(aitIndex element_offset) const; + void* dataPointer(aitIndex element_offset); + + void getTimeStamp(struct timespec* const ts) const; + void getTimeStamp(aitTimeStamp* const ts) const; + void getTimeStamp(struct epicsTimeStamp* const ts) const; + + void setTimeStamp(const struct timespec* const ts); + void setTimeStamp(const aitTimeStamp* const ts); + void setTimeStamp(const struct epicsTimeStamp* const ts); + + void setStatus(aitUint32); + void setStatus(aitUint16 high, aitUint16 low); + void getStatus(aitUint32&) const; + void getStatus(aitUint16& high, aitUint16& low) const; + + void setStat(aitUint16); + void setSevr(aitUint16); + aitUint16 getStat(void) const; + aitUint16 getSevr(void) const; + void setStatSevr(aitInt16 stat, aitInt16 sevr); + void getStatSevr(aitInt16& stat, aitInt16& sevr) const; + + size_t getTotalSizeBytes(void) const; + size_t getDataSizeBytes(void) const; + aitUint32 getDataSizeElements(void) const; + + // Note for all copy operations: + // Cannot change a container into atomic or scaler type, cannot change + // scaler or atomic type to container + + // copyInfo() will copy DD info only, this means appl, primitive type + // and bounds. Scalar data will be copied, but no arrays. + // copy() will copy DD info, bounds, allocate array data buffer and + // copy data into it. + // Dup() will copy DD info. bounds, data references copied only. + + gddStatus copyInfo(const gdd*); + gddStatus copy(const gdd*); + gddStatus Dup(const gdd*); + + // copy the entire DD structure into a contiguous buffer, return the + // last byte of the buffer that was used. + size_t flattenWithAddress(void* buffer,size_t size,aitIndex* total_dd=0); + size_t flattenWithOffsets(void* buffer,size_t size,aitIndex* total_dd=0); + gddStatus convertOffsetsToAddress(void); + gddStatus convertAddressToOffsets(void); + + int isScalar(void) const; + int isContainer(void) const; + int isAtomic(void) const; + + int isManaged(void) const; + int isFlat(void) const; + int isLocalDataFormat(void) const; + int isNetworkDataFormat(void) const; + int isConstant(void) const; + int isNoRef(void) const; + + void markConstant(void); + void markManaged(void); + void markUnmanaged(void); + void markLocalDataFormat(void); + void markNotLocalDataFormat(void); + + // The only way for a user to get rid of a DD is to Unreference it. + // NoReferencing() means that the DD cannot be referenced. + gddStatus noReferencing(void); + gddStatus reference(void) const; + gddStatus unreference(void) const; + + gdd& operator=(const gdd& v); + + // get a pointer to the data in the DD + void getRef(const aitFloat64*& d) const; + void getRef(const aitFloat32*& d) const; + void getRef(const aitUint32*& d) const; + void getRef(const aitInt32*& d) const; + void getRef(const aitUint16*& d) const; + void getRef(const aitInt16*& d) const; + void getRef(const aitUint8*& d) const; + void getRef(const aitInt8*& d) const; + void getRef(const aitString*& d) const; + void getRef(const aitFixedString*& d) const; + void getRef(const void*& d) const; + void getRef(aitFloat64*& d); + void getRef(aitFloat32*& d); + void getRef(aitUint32*& d); + void getRef(aitInt32*& d); + void getRef(aitUint16*& d); + void getRef(aitInt16*& d); + void getRef(aitUint8*& d); + void getRef(aitInt8*& d); + void getRef(aitString*& d); + void getRef(aitFixedString*& d); + void getRef(void*& d); + + // make the DD points to user data with a destroy method, + // put the referenced in and adjust the primitive type + void putRef(void*,aitEnum,gddDestructor* =0); + void putRef(aitFloat64*,gddDestructor* =0); + void putRef(aitFloat32*,gddDestructor* =0); + void putRef(aitUint8*,gddDestructor* =0); + void putRef(aitInt8*,gddDestructor* =0); + void putRef(aitUint16*,gddDestructor* =0); + void putRef(aitInt16*,gddDestructor* =0); + void putRef(aitUint32*,gddDestructor* =0); + void putRef(aitInt32*,gddDestructor* =0); + void putRef(aitString*,gddDestructor* =0); + void putRef(aitFixedString*,gddDestructor* =0); + // work with constants + void putRef(const aitFloat64*,gddDestructor* =0); + void putRef(const aitFloat32*,gddDestructor* =0); + void putRef(const aitUint8*,gddDestructor* =0); + void putRef(const aitInt8*,gddDestructor* =0); + void putRef(const aitUint16*,gddDestructor* =0); + void putRef(const aitInt16*,gddDestructor* =0); + void putRef(const aitUint32*,gddDestructor* =0); + void putRef(const aitInt32*,gddDestructor* =0); + void putRef(const aitString*,gddDestructor* =0); + void putRef(const aitFixedString*,gddDestructor* =0); + gddStatus putRef(const gdd*); + + // get the data in the form the user wants (do conversion) + void getConvert(aitFloat64& d) const; + void getConvert(aitFloat32& d) const; + void getConvert(aitUint32& d) const; + void getConvert(aitInt32& d) const; + void getConvert(aitUint16& d) const; + void getConvert(aitInt16& d) const; + void getConvert(aitUint8& d) const; + void getConvert(aitInt8& d) const; + void getConvert(aitString& d) const; + void getConvert(aitFixedString& d) const; + + // convert the user data to the type in the DD and set value + void putConvert(aitFloat64 d); + void putConvert(aitFloat32 d); + void putConvert(aitUint32 d); + void putConvert(aitInt32 d); + void putConvert(aitUint16 d); + void putConvert(aitInt16 d); + void putConvert(aitUint8 d); + void putConvert(aitInt8 d); + void putConvert(const aitString& d); + void putConvert(const aitFixedString& d); + + // copy the user data into the already set up DD array + gddStatus put(const aitFloat64* const d); + gddStatus put(const aitFloat32* const d); + gddStatus put(const aitUint32* const d); + gddStatus put(const aitInt32* const d); + gddStatus put(const aitUint16* const d); + gddStatus put(const aitInt16* const d); + gddStatus put(const aitUint8* const d); + gddStatus put(const aitInt8* const d); + gddStatus put(const aitString* const d); + gddStatus put(const aitFixedString* const d); + gddStatus put(const gdd* dd); + + // put the user data into the DD and reset the primitive type + gddStatus put(aitFloat64 d); + gddStatus put(aitFloat32 d); + gddStatus put(aitUint32 d); + gddStatus put(aitInt32 d); + gddStatus put(aitUint16 d); + gddStatus put(aitInt16 d); + gddStatus put(aitUint8 d); + gddStatus put(aitInt8 d); + gddStatus put(const aitString& d); + gddStatus put(const aitFixedString& d); + gddStatus put(aitType* d); + + // copy the array data out of the DD + void get(void* d) const; + void get(void* d,aitEnum) const; + void get(aitFloat64* d) const; + void get(aitFloat32* d) const; + void get(aitUint32* d) const; + void get(aitInt32* d) const; + void get(aitUint16* d) const; + void get(aitInt16* d) const; + void get(aitUint8* d) const; + void get(aitInt8* d) const; + void get(aitString* d) const; + void get(aitFixedString* d) const; + + // copy the data out of the DD + void get(aitFloat64& d) const; + void get(aitFloat32& d) const; + void get(aitUint32& d) const; + void get(aitInt32& d) const; + void get(aitUint16& d) const; + void get(aitInt16& d) const; + void get(aitUint8& d) const; + void get(aitInt8& d) const; + void get(aitString& d) const; + void get(aitFixedString& d) const; + void get(aitType& d) const; + + // Same as putRef() methods + gdd& operator=(aitFloat64* v); + gdd& operator=(aitFloat32* v); + gdd& operator=(aitUint32* v); + gdd& operator=(aitInt32* v); + gdd& operator=(aitUint16* v); + gdd& operator=(aitInt16* v); + gdd& operator=(aitUint8* v); + gdd& operator=(aitInt8* v); + gdd& operator=(aitString* v); + gdd& operator=(aitFixedString* v); + + // Same as put() methods + gdd& operator=(aitFloat64 d); + gdd& operator=(aitFloat32 d); + gdd& operator=(aitUint32 d); + gdd& operator=(aitInt32 d); + gdd& operator=(aitUint16 d); + gdd& operator=(aitInt16 d); + gdd& operator=(aitUint8 d); + gdd& operator=(aitInt8 d); + gdd& operator=(const aitString& d); + // gdd& operator=(aitFixedString d); // not present + + // Same as getRef() methods + operator const aitFloat64*(void) const; + operator const aitFloat32*(void) const; + operator const aitUint32*(void) const; + operator const aitInt32*(void) const; + operator const aitUint16*(void) const; + operator const aitInt16*(void) const; + operator const aitUint8*(void) const; + operator const aitInt8*(void) const; + operator const aitString*(void) const; + operator const aitFixedString*(void) const; + + operator aitFloat64*(void); + operator aitFloat32*(void); + operator aitUint32*(void); + operator aitInt32*(void); + operator aitUint16*(void); + operator aitInt16*(void); + operator aitUint8*(void); + operator aitInt8*(void); + operator aitString*(void); + operator aitFixedString*(void); + + // Same as get() methods + operator aitFloat64(void) const; + operator aitFloat32(void) const; + operator aitUint32(void) const; + operator aitInt32(void) const; + operator aitUint16(void) const; + operator aitInt16(void) const; + operator aitUint8(void) const; + operator aitInt8(void) const; + operator aitString(void) const; + // operator aitFixedString(void); // not present + + // + // added by JOH 4-23-99 so that the correct method + // is used if the container gdd is not organized + // as an array of GDDs in memory + // + // note that this requires a reference (pointers + // do not invoke this function) + // + gdd & operator [] (aitIndex index); + const gdd & operator [] (aitIndex index) const; + gdd & operator [] (int index); + const gdd & operator [] (int index) const; + + // + // The following can be slow and inefficient + // if the GDD isnt "flat" + // + // Only appropriate for container GDDs + // + const gdd* getDD(aitIndex index) const; + const gdd* getDD(aitIndex index, const gddScalar*&) const; + const gdd* getDD(aitIndex index, const gddArray*&) const; + const gdd* getDD(aitIndex index, const gddContainer*&) const; + gdd* getDD(aitIndex index); + gdd* getDD(aitIndex index,gddScalar*&); + gdd* getDD(aitIndex index,gddArray*&); + gdd* getDD(aitIndex index,gddContainer*&); + + gddStatus genCopy(aitEnum t, const void* d, + aitDataFormat f=aitLocalDataFormat); + void adjust(gddDestructor* d,void* v,aitEnum type, + aitDataFormat f=aitLocalDataFormat); + void get(aitEnum t,void* v,aitDataFormat f=aitLocalDataFormat) const; + void set(aitEnum t,const void* v,aitDataFormat f=aitLocalDataFormat); + + // following methods are used to import and export data into and out + // of this gdd. For the out methods, the returned count in the number + // of bytes put into the user's buffer. For the in methods, the + // returned count in the number of bytes put into the gdd from the + // user's buffer. The in methods always change the data format to local + // from whatever input format is specified with the argument. The out + // methods convert the gdd to whatever format is specified with the + // aitDataFormat argument. If aitEnum argument left as invalid, then + // data in the user's buffer will be assumed to be the same as the + // type defined in the gdd. + + size_t out(void* buf,aitUint32 bufsize,aitDataFormat =aitNetworkDataFormat) const; + size_t outHeader(void* buf,aitUint32 bufsize) const; + size_t outData(void* buf,aitUint32 bufsize, + aitEnum = aitEnumInvalid, aitDataFormat = aitNetworkDataFormat) const; + + size_t in(void* buf, aitDataFormat =aitNetworkDataFormat); + size_t inHeader(void* buf); + size_t inData(void* buf,aitUint32 number_of_elements = 0, + aitEnum = aitEnumInvalid, aitDataFormat = aitNetworkDataFormat); + + gdd_NEWDEL_FUNC(bounds) // managed on free lists + +protected: + ~gdd(void); // users must call Unreference() + + void init(int app, aitEnum prim, int dimen); + void freeBounds(void); + void dumpInfo(void) const; + gddStatus copyStuff(const gdd*,int type); + gddStatus clearData(void); + + size_t describedDataSizeBytes(void) const; + aitUint32 describedDataSizeElements(void) const; + + void markFlat(void); + gddStatus flattenData(gdd* dd, int tot_dds, void* buf, size_t size); + int flattenDDs(gddContainer* dd, void* buf, size_t size); + aitUint32 align8(unsigned long count) const; + + void setData(void* d); + + aitType data; // array pointer or scaler data + gddBounds* bounds; // array of bounds information length dim + gdd* nextgdd; + mutable gddDestructor* destruct; // NULL=none supplied, use remove + aitTimeStamp time_stamp; + aitStatus status; + aitUint16 appl_type; // application type code + aitUint8 prim_type; // primitive type code + aitUint8 dim; // 0=scaler, >0=array + + gdd_NEWDEL_DATA // required for using generic new and remove + +private: + mutable aitUint32 ref_cnt; + aitUint8 flags; + + static epicsMutex * pGlobalMutex; + const gdd* indexDD (aitIndex index) const; +}; + +// include these to be backward compatible with first gdd library version +#include "gddArray.h" +#include "gddScalar.h" +#include "gddContainer.h" + +#include "gddI.h" +#include "gddArrayI.h" +#include "gddScalarI.h" +#include "gddContainerI.h" + +#endif diff --git a/src/gdd/gdd.html b/src/gdd/gdd.html new file mode 100644 index 000000000..2235ea1a1 --- /dev/null +++ b/src/gdd/gdd.html @@ -0,0 +1,497 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +GDD User's Guide +

    +

    General Data Descriptor Library User's Guide

    + +


    General Overview

    +The purpose of the General Data Descriptor Library (GDD library) is to +fully describe, hold, and manage scalar and array data. Using a GDD, +you can describe a piece of data's type, dimensionality, and structure. +In addition you can identify the data with an integer application type value +which can be translated into a text string. A user can store data into and +retreived data from a GDD in any desired data type. A GDD can contain +a list of GDDs. This allows users to create and describe hierarchical +data structures dynamically. GDDs organized in this fashion are known +as containers. +

    +As mentioned above, a GDD can describe an n-dimension array. The user +can describe the bounds of the n-dimensional array. To facilitate the +use of large, and perhaps shared data arrays, a GDD allows a user to +store a reference to an array of data. In addition, a destructor function +can be registed in the GDD to inform the owner of the data array when +the GDD referencing the data goes away. The purpose of the destructor +function is to delete the array of data, since the GDD does not know +what to do with referenced data. +

    +To manage GDDs in a multi-tasking system or a system that +uses many layers of software, GDDs implement reference counting. With +reference counting, only one instance of a GDD can be shared by many subsystems. +The GDD creator function can pass it to many other functions without +worrying about deleting it, only when the last function using the GDD requests +that it be destroyed does it really go away. +

    +As menitioned above, a GDD allows the user to describe the data in terms +of the application. This is done by the user by assigning an arbitrary +integer identifier to a GDD. The user places a meaning on the identifiers +such as 1=high-alarm-limit, 2=low-alarm-limit. This identifier is termed +application type. A second component of the GDD library known as the +Application Type Table is used to manage the application type identifiers. +Application type values are registered in the table along with a text +string and optionally a prototype GDD. The prototype GDD can be a +container GDD. The table allows users to retreive GDDs of a specific +application type. +

    +A GDD describes and manages a piece of data using the following information: +

    +

      +
    • Application Type (user assigned integer value) +
    • Primitive Type (storage type identifier - int/double/short/etc.) +
    • Dimension (of array data) +
    • Bounds (array data structure information) +
    • Destructor (to delete referenced array data) +
    • Time Stamp (last time updated) +
    • Status (user assign data status field) +
    • Reference Count +
    • Data Field +
    + +

    +The GDD library is a C++ class library and therefore requires using the +C++ compiler. + +


    GDD Description

    +The gdd class is the main class in the GDD library. It controls almost +all actions performed on the data. The gdd class is further broken down +into three subclasses for performing operations on specific types of GDDs: +gddContainer, gddAtomic, and gddScalar. The gddContainer class aids in the +creation of container type GDDs. It adds functions such as "insert GDD", +"remove GDD", and "give me GDD x from the container" to the basic gdd class. +The gddAtomic class helps create and manage array data GDDs. The gddScalar +class makes it easy to create scalar GDDs. +

    +All GDDs must be created dynamically, a GDD cannot be created on the +stack as a local variable. The gdd class forbids the user from deleting +the gdd. In order for reference counting to work correctly the user must +"unreference" the gdd instance instead of deleting it. The gdd class does +take over the memory management routines for itself and all it's subclasses. +This means that you are not using the malloc()/free() memory management when +GDDs are created and destroyed. It is important to remember that since +reference counting is used, a GDD passed into a function must be referenced +before the function returns if the GDD is to be kept for longer then the +running of that function. In other words, if you are creating a library +function "add" that records process variable names in a linked list, and the +process variable names are passed to you as GDDs, then you must reference +the GDD since the linked list exists after the return of the "add" function. +If you are creating a GDD, you must unreference it when you are finished +with it, even it you have passed it into other library functions. +Generalizing on this, it is the responsibility of the GDD creator or +GDD referencer to unreference the GDD instance when they are finished +with it. +

    +For a GDD to be useful after it is created, it must be given information +to describe the data to will hold. This data was summarized in the overview +section. To further understand the meaning of all these items and use +them correctly, each needs to be discussed. + +


    Primitive Types

    +As mentioned in the overview, a piece of descriptive information that a +GDD maintains is the primitive type code. This field +describes the format of the data (storage type). +The primitive type field of a GDD is an enumeration +of all the general storage types supported by the compilers. A user can +determine dynamically what the storage format of the data in a GDD is using +the primitive type code information. The GDD library redefines the general +storage class names for integers and floating point numbers and enumerates +them in the "aitTypes.h" header file. The redefined names describe the +format and the bit length so that they can be used across architectures. +The initial part of the header file name "aitTypes.h" is ait which +stands for "Architecture Independant Types". The standard data types +supported by the GDD library are +
    +	aitInt8           8 bit character
    +	aitUint8          8 bit unsigned character
    +	aitInt16          16 bit short
    +	aitUint16         16 bit unsigned short
    +	aitEnum16         16 enumerated value
    +	aitInt32          32 bit integer
    +	aitUint32         32 bit unsigned integer
    +	aitFloat32        32 bit floating point number
    +	aitFloat64        64 bit floating point number
    +	aitPointer        Standard pointer
    +	aitIndex          32 bit index value
    +	aitStatus         32 bit unsigned integer for status value
    +	aitFixedString    40 byte string of characters
    +	aitString         Variable length string data type
    +	aitTimeStamp      Two 32 bit integers describing time (seconds/nanoseconds)
    +
    +These data types should be used whenever possible to prevent problems when +compiling programs for different architectures. Most of the data types +described above are enumerated for use as a primitive type code. The +enumerated names are just the above type names with the word "Enum" +inserted after "ait". It should be noted that aitTimeStamp is not a +standard primitive type. +
    +typedef enum {
    +	 aitEnumInvalid=0,
    +	 aitEnumInt8,
    +	 aitEnumUint8,
    +	 aitEnumInt16,
    +	 aitEnumUint16,
    +	 aitEnumEnum16,
    +	 aitEnumInt32,
    +	 aitEnumUint32,
    +	 aitEnumFloat32,
    +	 aitEnumFloat64,
    +	 aitEnumFixedString,
    +	 aitEnumString,
    +	 aitEnumContainer
    +} aitEnum;
    +
    +The enumerated type code allows a user to dynamically convert from one +type to another. The AIT portion of the GDD library contains a large +primitive type conversion matrix. The conversion matrix is indexed by +the source and destination enumeration type codes. The 12x12 matrix +contains functions pointers for each type of conversion that can take +place. Generally this matrix is never accessed directly by the user, +a convert function: +
    +	void aitConvert(aitEnum dest_type, void* dest_data,
    +						 aitEnum src_type, const void* src_data,
    +						 aitIndex element_count)
    +
    +runs the correct function to perform the calculation. This function is used +extensively in the gdd class when data is put into or retrieved from a GDD. +

    +The primitive type code really only describes the storage format and length +of an element within a GDD. This code does not imply or assign any meaning +to the data. Assigning meaning to the data is the job of the application +type code. + +


    Application Types

    +The application type is a 32 bit integer value that is user assigned. Each +value is arbitrary and is designed to give meaning to the chunk of data. +Normally each value has a character string associated with it which can +be looked up through a standard hash table. The GDD library predefines +many values and structures for standard control system applications; +this portion of the library will be dicussed in detail later in this document. +

    +A typical way that application type codes are used is in defining structures. +A type code of 54 may be assigned the meaning "Temperature Reading". The +"Temperature Reading" structure may be a container with four GDDs in it: +a value, a high alarm limit, a low alarm limit, and units. Each +of these GDDs also have an application type code. A generic program can +actually be written to completely discover the contents of the +"Temperature Reading" structure and configure itself to display meaningful +data. + +


    Time Stamp and Status

    +Each GDD can store a time stamp and status value. The time stamp is a +standard 32-bit seconds, 32-bit nanoseconds since a standard epoch. The +status is a 32-bit value that is user assigned. The purpose of the time +stamp is to reflect the time when the data held within the GDD was read +and stored. The status is intented to reflect the validity of the data +within the GDD. In the context of EPICS, the status field is really a +16-bit status and 16-bit severity code. +

    +Storing time stamp and status information in each GDD was really a processing +versus storage compromise. Most data in a control system that is +constantly changing and needs to be transfered in GDDs requires a status +and time stamp. Transfering this type of data in one GDD in fairly easy. +If GDDs did not contain time stamps and status, then this actively changing +data would need to be transfered as a structure of three GDDs: one for +the value, one for the status, and one for the time stamp. In addition +to the three GDDs, a fourth that describes the GDD container will need to +be transfered. + +


    Dimension and Bounds Information

    +A GDD that is a scalar is self contained. All information to describe +the data within the GDD is contained within the GDD. If the GDD describes +a structure (a container), or the GDD describes an array, then dimension +and array bounds information are needed. A GDD can describe an array +of any dimension. Each dimension is required to have a description +of it's size in terms of bounds. +

    +Bounds, and consequencely a single dimension, in GDD are described by two +fields, a start and an element count. With this setup, and n-dimensional +space can be described. For example, a typical three dimension array +would be described as A(5,4,6), which is a 5x4x6 cube. In a GDD this +cube would be described with dimension=3, bounds={(0,5)(0,4)(0,6)}. If +we want to describe a subset of this array, we would normally do so by +giving two endpoints of the sub-cube, such as (1,2,3)->(2,3,5). In GDD +terms, this would be bounds={(1,2),(2,3),(3,5)}. +

    +The dimension information is stored directly within the GDD. The bounds +information is not. If bounds are required, then they are allocated as +a single dimensional array and referenced by the GDD. Methods exist in +the GDD class to automatically manipulate, allocate, and deallocate +bounds structures. Typically GDD are assigned a dimension when created +and do not morph into a different space. + +


    Data Field

    +A GDD contains a data field large enough to hold any scalar value or +a pointer. This field is the union of all the primitive data types. +Currently it takes up 8 bytes, which is the size of a double. If a GDD +refers to an array of data, then the pointer to the actual data is stored +in the data field. If the dimension is greater then zero, then the data +field references the actual data. + +


    Destructor

    +Since GDDs can reference arrays of data, the user can optionally register +a destructor to be called when the GDD is destroyed. The GDD can store +a reference to a user destructor. The GDD library contains a destructor +base class called gddDestructor. A "destroy" method exists in this class +that is called when the GDD is being destroyed. Users must derive a class +from gddDestructor and define a "run" method. The "run" method gets +invoked with the address of the data array. The user, in the "run" +method, casts the address to the appropriate data type and deletes +the array. The default behavior of the gddDestuctor if the user does not +override the "run" method will be to cast the data array to a +character array and delete it. +

    +The gddDestructor allows reference counting similar to the GDD class. +Typically a data array will be associated with one instance of the +gddDestructor class. If more then one GDD needs to reference the array, +then each GDD is registered with the same gddDestructor. Each time +the gddDestructor is registered, the reference count should be bumped +up. The gddDestructor "run" method will only be invoked when the +reference count drops to zero. + +


    Strings and Fixed Strings

    +Strings are special cases in the GDD library. There is a class called +aitString designed to work with and manage strings. Strings is the GDD +library generally contain two components: a reference to a character +array and a length. Storing a string in a GDD always results in a +reference to a character array, even if the GDD is a scalar. Character +strings are first put into an aitString and then inserted into the GDD. +The GDD data field can hold an instance of the aitString class for use +when the GDD is a scalar with one string in it. A scalar string GDD +still references the actual string within the aitString class. +

    +A fixed string class exists in the GDD library in order to support +certain features of EPICS. Fixed strings are used internally and +should generally not be used when creating and maniplulating strings with +the GDD library. Fixed strings are too big to fit into a GDD and also +always referenced, even if the GDD is a scalar. + +


    GDD Summary

    +Several important issues related to where the actual data is stored +must be remembered when using GDDs: + +
      +
    1. If a gdd is a scalar, then the gdd holds the data it describes, the + dimension is zero, and the bounds are empty. +
    2. If a gdd is an array, then the gdd refers to the data it describes, + refers to bounds that describe it's structure, has a dimension + greater then zero, and optionally + refers to a user's data destructor. +
    3. If a gdd is a container, then the dimension is fixed at one and the + bounds describe how many elements (GDDs) are in the container. + The destructor in the container case knows how to free up all the + GDDs in the container. +
    + +


    Creating and Using GDDs

    +
    +	#include "gdd.h"
    +	.
    +	.
    +	// my destructor ------------------
    +	class myDest : public gddDestructor
    +	{
    +	public:
    +		myDest(void) : gddDestructor() { }
    +		void run(void*);
    +	}
    +
    +	void myDest::run(void* v)
    +	{
    +		aitInt16* i16 = (aitInt16*)v;
    +		delete [] i16;
    +	}
    +	// --------------------------------
    +	.
    +	.
    +	.
    +	int app_type_code = 100;
    +
    +	// create a scalar GDD of type Int32 and put the value 5 into it
    +	aitInt32 ival = 5;
    +	aitFloat64 sval;
    +	gdd* dds = new gddScalar(app_type_code,aitEnumInt32);
    +	dds->put(ival);
    +	dds->getConvert(sval);
    +	dds->dump();
    +
    +	aitString str = "test string";
    +	gdd* dd_str = new gddScalar(++app_type_code,aitEnumString);
    +	dd_str->put(str);
    +	printf("string length = %n",str->length());
    +	dd_str->dump();
    +
    +	// create an array GDD and of dimension 1 and bound of 20 elements
    +	// reference the array into the container
    +	aitUint32 tot_elements = 20;
    +	int dim = 1;
    +	aitFloat64 a[20];
    +	gdd* dda = new gddAtomic(++app_type_code,aitEnumFloat64,dim,&tot_elements);
    +	dda->putRef(a);
    +	dda->dump();
    +
    +	aitInt16* i16 = new aitInt16[tot_elements];
    +	gdd* ddb = new gddAtomic(++app_type_code,aitEnumInt16,dim,&tot_elements);
    +	ddb->putRef(i16,new myDest);
    +	ddb->dump();
    +
    +	// create a container GDD that holds to GDDs and put the previously
    +	// created GDDs into it.
    +	int tot_in_container = 2;
    +	gddContainer* ddc = new gddContainer(++app_type_code,tot_in_container);
    +	ddc->insert(dds);
    +	ddc->insert(dda);
    +	ddc->dump();
    +
    +	// clean up the container GDD, this will clean up all member GDDs,
    +	// myDest run() should also be invoked
    +	delete ddc;
    +	.
    +	.
    +	.
    +
    + +


    Application Type Table Description

    +A facility within the GDD library is designed to store application +type code to string mappings. The facility can also be used to store +and retrieve prototype GDDs by application type code. The application +type table is really a simple database containing records with the following +information: +
      +
    • Application type code (32-bit integer) +
    • Application type name (variable length character string) +
    • Prototype GDD (gdd scalar, atomic, or container) +
    • Indexing maps +
    +The type table has methods for registering or inserting records into the +database. Methods also exist to query the type name given a type code, and +to query the type code given the type name. Type codes are values +assigned to a type name when the type name is registered; the user is given the +type code value when the registeration takes place. A type code sometimes has +a prototype GDD associated with it, especially if it is a container GDD. +Methods also exist to retrieve a new GDD given an application type code. The +new GDD structure will be a copy of the prototype GDD for that type code. +The application type table +is commonly used create GDDs. A given application type code usually +implies a certain structure to the data. The prototype mechanism allows +this imposed structure to be adhered to when the GDD is created. This is +particularly important when it comes to container type GDDs. The type +table basically copies the prototype when the user requests a GDD with a +particular type code. The application type table also packs GDDs in +an efficient manage, which allows very high performance creation and +deletion of container GDDs and atomic GDDs. +

    +GDDs in general have several components that are references, they do not +hold all the information they need. Creating and using array GDDs or +container GDDs +can be very time consuming. For an array GDD, the bounds and a destructor +must be allocated in addition to the GDD itself and referenced into the +GDD. For a container GDD, the GDD elements are really stored as a linked +list. To access the third element of a container, the linked list must be +traversed. With aitString GDDs the situation is worse yet. The GDD class +allow for a given GDD to be packed or flattened into a single linear buffer. +This mechanism includes packing GDD containers. In the array case, +the actual GDD is the first thing in the buffer, followed by any bounds +information, followed by the actual data array. The fields of the GDD +work exactly as before, except that they reference bounds and data that is +in the same buffer. In the case of containers, all the GDDs are stored as +an array of GDDs at the front of the buffer, followed by the bounds +information for each of the GDDs, followed by each of the GDD's data arrays if +present. There are many advantages of this configuration. Since all the +GDDs in a container are stored as an array instead of a linked list, the +user can directly index a particular GDD within the container. The +application type table performs this packing on a GDD that is registered +as a prototype for a particular type code. Since the GDD for a given +type code is now a fixed size, the type code table can manage the GDDs on +a free list. Each type code database entry with a prototype GDD contains +a free list +of GDDs for that type code. Creating a GDD using the type code table +involves retrieving a preallocated, packed GDD from a particular free list +and giving it to the user. This operation is very fast and efficient since +complex GDD structures are already constructed. +

    +As stated above, container GDDs managed by the application type table can +be directly indexed as an array by the user. Unfortunetly the application +type code and indexes have no correlation - you cannot use the application +type code to index the GDD container. The type table has mapping functions +that convert between an application type code and an index into a container +GDD. Of course each type code maintains it's own index map and the +container GDD type code determines which mapping is to be used. A mechanism +exists in the GDD library for generating "#define" statements that +label container index values with a unique string. Preregistered containers +use this mechanism to generate indexing labels. The index label is a +concatenation of the type code names. If a container GDD type code name is +"TemperatureReading" and the first element is "Value", then the index +label generated will be "TemperatureReading_Value". At any time in a +running application, the user can request that index labels for all +registered prototypes be generated and dumped into a file. + +The library preregisters a number of application type names: + +

      +
    • "units" +
    • "maxElements" +
    • "precision" +
    • "graphicHigh" +
    • "graphicLow" +
    • "controlHigh" +
    • "controlLow" +
    • "alarmHigh" +
    • "alarmLow" +
    • "alarmHighWarning" +
    • "alarmLowWarning" +
    • "value" +
    • "enums" +
    • "menuitem" +
    • "status" +
    • "severity" +
    • "seconds" +
    • "nanoseconds" +
    • "name" +
    • "all" +
    • "attributes" +
    +In addition, if EPICS is used then the following are registered: +
      +
    • "dbr_gr_short" +
    • "dbr_gr_float" +
    • "dbr_gr_enum" +
    • "dbr_gr_char" +
    • "dbr_gr_long" +
    • "dbr_gr_double" +
    • "dbr_ctrl_short" +
    • "dbr_ctrl_float" +
    • "dbr_ctrl_enum" +
    • "dbr_ctrl_char" +
    • "dbr_ctrl_long" +
    • "dbr_ctrl_double" +
    + +The file gddApps.h contains all the index label defines for the EPICS +related GDD containers. + +


    Using the Application Type Table

    + +

    GDD Reference Manual +


    Home page for Jim Kowalkowski.
    + +
    Argonne National Laboratory +Copyright +Information
    +
    jbk@aps.anl.gov (Jim Kowalkowski)
    updated 9/13/96
    + diff --git a/src/gdd/gdd.rc b/src/gdd/gdd.rc new file mode 100755 index 000000000..e5ca7379d --- /dev/null +++ b/src/gdd/gdd.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","General Data Descriptor Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "General Data Descriptor Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "gdd\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "gdd.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/gdd/gddAppDefs.cc b/src/gdd/gddAppDefs.cc new file mode 100644 index 000000000..1e1535bdb --- /dev/null +++ b/src/gdd/gddAppDefs.cc @@ -0,0 +1,268 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#define epicsExportSharedSymbols +#include "gddAppTable.h" + +// useless utility function +gddApplicationTypeTable* gddGenerateApplicationTypeTable(long x/*=(1<<13)*/) +{ + gddApplicationTypeTable* tt = new gddApplicationTypeTable((unsigned int)x); + return tt; +} + +void gddApplicationTypeTable::GenerateTypes(void) +{ + gddScalar* add_units = new gddScalar(0,aitEnumString); + + // One attempt at building a menu using aitString, which is not so + // good. + // gddAtomic* add_enum = new gddAtomic(0,aitEnumString,1,16); + // aitString add_enum_buf[16]; + // add_enum->putRef(add_enum_buf); + + // Just describe the menu - allow the block of choiced to be + // referenced in. + // gddAtomic* add_enum = new gddAtomic(0,aitEnumFixedString,1,16); + + // ---------------------------------------------------------------- + // register simple types + + // should I allow a dd retrieved through the tag table to be adjusted + // even though it is flattened? What if an array needs to change size + + // or type in a flattened dd? + + // value - filled by user no dd to register + // units - variable length of array of chars + + // not really required attributes - they are contained in DDs + registerApplicationType(GDD_NAME_STATUS); + registerApplicationType(GDD_NAME_SEVERITY); + registerApplicationType(GDD_NAME_TIME_STAMP); + registerApplicationType(GDD_NAME_PV_NAME); + registerApplicationType(GDD_NAME_CLASS); + + // required attributes + int type_prec=registerApplicationType(GDD_NAME_PRECISION); + int type_ghigh=registerApplicationType(GDD_NAME_GRAPH_HIGH); + int type_glow=registerApplicationType(GDD_NAME_GRAPH_LOW); + int type_chigh=registerApplicationType(GDD_NAME_CONTROL_HIGH); + int type_clow=registerApplicationType(GDD_NAME_CONTROL_LOW); + int type_ahigh=registerApplicationType(GDD_NAME_ALARM_HIGH); + int type_alow=registerApplicationType(GDD_NAME_ALARM_LOW); + int type_awhigh=registerApplicationType(GDD_NAME_ALARM_WARN_HIGH); + int type_awlow=registerApplicationType(GDD_NAME_ALARM_WARN_LOW); + int type_maxele=registerApplicationType(GDD_NAME_MAX_ELEMENTS); + int type_value=registerApplicationType(GDD_NAME_VALUE); + int type_menu=registerApplicationType(GDD_NAME_ENUM); + int type_units=registerApplicationTypeWithProto(GDD_NAME_UNITS,add_units); + int type_ackt=registerApplicationType(GDD_NAME_ACKT); + int type_acks=registerApplicationType(GDD_NAME_ACKS); + + // old menu method + // int type_menu=registerApplicationType(GDD_NAME_ENUM); + + // ---------------------------------------------------------------- + // register container types - not as easy + + // container of all PV attributes + gddContainer* cdd_attr=new gddContainer(1); + cdd_attr->insert(getDD(type_prec)); + cdd_attr->insert(getDD(type_ghigh)); + cdd_attr->insert(getDD(type_glow)); + cdd_attr->insert(getDD(type_chigh)); + cdd_attr->insert(getDD(type_clow)); + cdd_attr->insert(getDD(type_ahigh)); + cdd_attr->insert(getDD(type_alow)); + cdd_attr->insert(getDD(type_awhigh)); + cdd_attr->insert(getDD(type_awlow)); + cdd_attr->insert(getDD(type_units)); + cdd_attr->insert(getDD(type_maxele)); + registerApplicationTypeWithProto(GDD_NAME_ATTRIBUTES,cdd_attr); + + // container of everything about a PV + gddContainer* cdd_all=new gddContainer(1); + cdd_all->insert(getDD(type_prec)); + cdd_all->insert(getDD(type_ghigh)); + cdd_all->insert(getDD(type_glow)); + cdd_all->insert(getDD(type_chigh)); + cdd_all->insert(getDD(type_clow)); + cdd_all->insert(getDD(type_ahigh)); + cdd_all->insert(getDD(type_alow)); + cdd_all->insert(getDD(type_awhigh)); + cdd_all->insert(getDD(type_awlow)); + cdd_all->insert(getDD(type_units)); + cdd_all->insert(getDD(type_value)); + registerApplicationTypeWithProto(GDD_NAME_ALL,cdd_all); + + // ------------- generate required dbr containers ----------------- + + // DBR_GR_SHORT + gddContainer* cdd_gr_short=new gddContainer(0); + cdd_gr_short->insert(new gddScalar(type_value,aitEnumInt16)); + cdd_gr_short->insert(new gddScalar(type_ghigh,aitEnumInt16)); + cdd_gr_short->insert(new gddScalar(type_glow,aitEnumInt16)); + cdd_gr_short->insert(new gddScalar(type_ahigh,aitEnumInt16)); + cdd_gr_short->insert(new gddScalar(type_alow,aitEnumInt16)); + cdd_gr_short->insert(new gddScalar(type_awhigh,aitEnumInt16)); + cdd_gr_short->insert(new gddScalar(type_awlow,aitEnumInt16)); + cdd_gr_short->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_gr_short",cdd_gr_short); + + // DBR_GR_FLOAT + gddContainer* cdd_gr_float=new gddContainer(0); + cdd_gr_float->insert(new gddScalar(type_value,aitEnumFloat32)); + cdd_gr_float->insert(new gddScalar(type_prec,aitEnumInt16)); + cdd_gr_float->insert(new gddScalar(type_ghigh,aitEnumFloat32)); + cdd_gr_float->insert(new gddScalar(type_glow,aitEnumFloat32)); + cdd_gr_float->insert(new gddScalar(type_ahigh,aitEnumFloat32)); + cdd_gr_float->insert(new gddScalar(type_alow,aitEnumFloat32)); + cdd_gr_float->insert(new gddScalar(type_awhigh,aitEnumFloat32)); + cdd_gr_float->insert(new gddScalar(type_awlow,aitEnumFloat32)); + cdd_gr_float->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_gr_float",cdd_gr_float); + + // DBR_GR_ENUM + gddContainer* cdd_gr_enum=new gddContainer(0); + // old: cdd_gr_enum->insert(new gddAtomic(type_menu,aitEnumFixedString,1)); + cdd_gr_enum->insert(getDD(type_menu)); + cdd_gr_enum->insert(new gddScalar(type_value,aitEnumEnum16)); + registerApplicationTypeWithProto("dbr_gr_enum",cdd_gr_enum); + + // DBR_GR_CHAR + gddContainer* cdd_gr_char=new gddContainer(0); + cdd_gr_char->insert(new gddScalar(type_value,aitEnumInt8)); + cdd_gr_char->insert(new gddScalar(type_ghigh,aitEnumInt8)); + cdd_gr_char->insert(new gddScalar(type_glow,aitEnumInt8)); + cdd_gr_char->insert(new gddScalar(type_ahigh,aitEnumInt8)); + cdd_gr_char->insert(new gddScalar(type_alow,aitEnumInt8)); + cdd_gr_char->insert(new gddScalar(type_awhigh,aitEnumInt8)); + cdd_gr_char->insert(new gddScalar(type_awlow,aitEnumInt8)); + cdd_gr_char->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_gr_char",cdd_gr_char); + + // DBR_GR_LONG + gddContainer* cdd_gr_long=new gddContainer(0); + cdd_gr_long->insert(new gddScalar(type_value,aitEnumInt32)); + cdd_gr_long->insert(new gddScalar(type_ghigh,aitEnumInt32)); + cdd_gr_long->insert(new gddScalar(type_glow,aitEnumInt32)); + cdd_gr_long->insert(new gddScalar(type_ahigh,aitEnumInt32)); + cdd_gr_long->insert(new gddScalar(type_alow,aitEnumInt32)); + cdd_gr_long->insert(new gddScalar(type_awhigh,aitEnumInt32)); + cdd_gr_long->insert(new gddScalar(type_awlow,aitEnumInt32)); + cdd_gr_long->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_gr_long",cdd_gr_long); + + // DBR_GR_DOUBLE + gddContainer* cdd_gr_double=new gddContainer(0); + cdd_gr_double->insert(new gddScalar(type_value,aitEnumFloat64)); + cdd_gr_double->insert(new gddScalar(type_prec,aitEnumInt16)); + cdd_gr_double->insert(new gddScalar(type_ghigh,aitEnumFloat64)); + cdd_gr_double->insert(new gddScalar(type_glow,aitEnumFloat64)); + cdd_gr_double->insert(new gddScalar(type_ahigh,aitEnumFloat64)); + cdd_gr_double->insert(new gddScalar(type_alow,aitEnumFloat64)); + cdd_gr_double->insert(new gddScalar(type_awhigh,aitEnumFloat64)); + cdd_gr_double->insert(new gddScalar(type_awlow,aitEnumFloat64)); + cdd_gr_double->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_gr_double",cdd_gr_double); + + // DBR_CTRL_SHORT + gddContainer* cdd_ctrl_short=new gddContainer(0); + cdd_ctrl_short->insert(new gddScalar(type_value,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_ghigh,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_glow,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_chigh,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_clow,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_ahigh,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_alow,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_awhigh,aitEnumInt16)); + cdd_ctrl_short->insert(new gddScalar(type_awlow,aitEnumInt16)); + cdd_ctrl_short->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_ctrl_short",cdd_ctrl_short); + + // DBR_CTRL_FLOAT + gddContainer* cdd_ctrl_float=new gddContainer(0); + cdd_ctrl_float->insert(new gddScalar(type_value,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_prec,aitEnumInt16)); + cdd_ctrl_float->insert(new gddScalar(type_ghigh,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_glow,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_chigh,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_clow,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_ahigh,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_alow,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_awhigh,aitEnumFloat32)); + cdd_ctrl_float->insert(new gddScalar(type_awlow,aitEnumFloat32)); + cdd_ctrl_float->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_ctrl_float",cdd_ctrl_float); + + // DBR_CTRL_ENUM + gddContainer* cdd_ctrl_enum=new gddContainer(0); + //old:cdd_ctrl_enum->insert(new gddAtomic(type_menu,aitEnumFixedString,1)); + cdd_ctrl_enum->insert(getDD(type_menu)); + cdd_ctrl_enum->insert(new gddScalar(type_value,aitEnumEnum16)); + registerApplicationTypeWithProto("dbr_ctrl_enum",cdd_ctrl_enum); + + // DBR_CTRL_CHAR + gddContainer* cdd_ctrl_char=new gddContainer(0); + cdd_ctrl_char->insert(new gddScalar(type_value,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_ghigh,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_glow,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_chigh,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_clow,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_ahigh,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_alow,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_awhigh,aitEnumInt8)); + cdd_ctrl_char->insert(new gddScalar(type_awlow,aitEnumInt8)); + cdd_ctrl_char->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_ctrl_char",cdd_ctrl_char); + + // DBR_CTRL_LONG + gddContainer* cdd_ctrl_long=new gddContainer(0); + cdd_ctrl_long->insert(new gddScalar(type_value,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_ghigh,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_glow,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_chigh,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_clow,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_ahigh,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_alow,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_awhigh,aitEnumInt32)); + cdd_ctrl_long->insert(new gddScalar(type_awlow,aitEnumInt32)); + cdd_ctrl_long->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_ctrl_long",cdd_ctrl_long); + + // DBR_CTRL_DOUBLE + gddContainer* cdd_ctrl_double=new gddContainer(0); + cdd_ctrl_double->insert(new gddScalar(type_value,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_prec,aitEnumInt16)); + cdd_ctrl_double->insert(new gddScalar(type_ghigh,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_glow,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_chigh,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_clow,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_ahigh,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_alow,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_awhigh,aitEnumFloat64)); + cdd_ctrl_double->insert(new gddScalar(type_awlow,aitEnumFloat64)); + cdd_ctrl_double->insert(getDD(type_units)); + registerApplicationTypeWithProto("dbr_ctrl_double",cdd_ctrl_double); + + // DBR_STSACK_STRING + gddContainer* cdd_stsack_string=new gddContainer(0); + cdd_stsack_string->insert(new gddScalar(type_value,aitEnumString)); + cdd_stsack_string->insert(new gddScalar(type_acks,aitEnumUint16)); + cdd_stsack_string->insert(new gddScalar(type_ackt,aitEnumUint16)); + registerApplicationTypeWithProto("dbr_stsack_string",cdd_stsack_string); +} + diff --git a/src/gdd/gddAppFuncTable.h b/src/gdd/gddAppFuncTable.h new file mode 100644 index 000000000..dafdeca90 --- /dev/null +++ b/src/gdd/gddAppFuncTable.h @@ -0,0 +1,271 @@ +/*************************************************************************\ +* 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef gddAppFuncTableH +#define gddAppFuncTableH + +// +// ANSI C +// +#include + +// +// GDD +// +#include "gdd.h" +#include "gddAppTable.h" +#include "errMdef.h" +#include "errlog.h" + +typedef aitUint32 gddAppFuncTableStatus; + +#define S_gddAppFuncTable_Success 0u +#define S_gddAppFuncTable_badType (M_gddFuncTbl|1u) /*application type unregistered*/ +#define S_gddAppFuncTable_gddLimit (M_gddFuncTbl|2u) /*at gdd lib limit*/ +#define S_gddAppFuncTable_noMemory (M_gddFuncTbl|3u) /*dynamic memory pool exhausted*/ + +#ifndef NELEMENTS +#define NELEMENTS(array) (sizeof(array)/sizeof((array)[0])) +#endif + +// +// class gddAppFuncTable +// +template +class gddAppFuncTable { + +public: + gddAppFuncTable() : pMFuncRead(0), appTableNElem(0u) + { + } + + ~gddAppFuncTable() + { + if (this->pMFuncRead) { + delete [] this->pMFuncRead; + } + } + + // + // installReadFunc() + // + // The 2nd parameter should be declared as follows: + // + // gddAppFuncTableStatus PV::memberFunction(gdd &value); + // + gddAppFuncTableStatus installReadFunc (const unsigned type, + gddAppFuncTableStatus (PV::*pMFuncIn)(gdd &) ); + + // + // installReadFunc() + // + // The 2nd parameter should be declared as follows: + // + // gddAppFuncTableStatus PV::memberFunction(gdd &value); + // + gddAppFuncTableStatus installReadFunc ( const char * pName, + gddAppFuncTableStatus (PV::*pMFuncIn)(gdd &) ); + + // + // + // + gddAppFuncTableStatus read ( PV &pv, gdd &value ); + gddAppFuncTableStatus callReadFunc ( PV &pv, gdd &value ); + +private: + // + // The total number of application tags to manage should be + // hidden from the application (eventually allow for auto + // expansion of the table) + // + gddAppFuncTableStatus (PV::**pMFuncRead)(gdd &); + unsigned appTableNElem; + + void newTbl ( unsigned neMaxType ); +}; + +// +// gddAppFuncTable::installReadFunc() +// +// The 2nd parameter should be declared as follows: +// +// gddAppFuncTableStatus PV::memberFunction(gdd &value); +// +// A typedef is not used here because of portability +// problems resulting from compiler weaknesses +// +template +gddAppFuncTableStatus gddAppFuncTable::installReadFunc ( + const unsigned type, gddAppFuncTableStatus (PV::*pMFuncIn)(gdd &) ) +{ + // + // Attempt to expand the table if the app type will not fit + // + if (type>=this->appTableNElem) { + this->newTbl(type); + if (type>=this->appTableNElem) { + return S_gddAppFuncTable_noMemory; + } + } + this->pMFuncRead[type]=pMFuncIn; + return S_gddAppFuncTable_Success; +} + +// +// installReadFunc() +// +// The 2nd parameter should be declared as follows: +// +// gddAppFuncTableStatus PV::memberFunction(gdd &value); +// +template +gddAppFuncTableStatus gddAppFuncTable::installReadFunc ( + const char * pName, gddAppFuncTableStatus (PV::*pMFuncIn)(gdd &) ) +{ + aitUint32 type; + gddStatus rc; + + rc = gddApplicationTypeTable:: + app_table.registerApplicationType (pName, type); + if (rc!=0 && rc!=gddErrorAlreadyDefined) { + printf( +"at gdd lib limit => read of PV attribute \"%s\" will fail\n", pName); + return S_gddAppFuncTable_gddLimit; + } +# ifdef DEBUG + printf("installing PV attribute %s = %d\n", pName, type); +# endif + + return this->installReadFunc(type, pMFuncIn); +} + +// +// gddAppFuncTable::newTbl() +// +// The total number of application tags to manage should be +// hidden from the application +// +// The typedef is not used here because of portability +// problems resulting from compiler weaknesses +// +template +void gddAppFuncTable::newTbl(unsigned newApplTypeMax) +{ + gddAppFuncTableStatus (PV::**pMNewFuncTbl)(gdd &); + unsigned maxApp; + unsigned i; + + if (this->appTableNElem>newApplTypeMax) { + return; + } + maxApp = newApplTypeMax+(1u<<6u); + +# if defined(_MSC_VER) && _MSC_VER <= 1200 + // + // MS Visual C++ 6.0 (_MSC_VER==1200) or lower + // compilers allocate the wrong amount of memory + // (i.e. too little) for member function pointers, + // only explicit calculation via sizeof() works. + // + pMNewFuncTbl = (gddAppFuncTableStatus (PV::**)(gdd &)) + new char[sizeof( gddAppFuncTableStatus (PV::*)(gdd &) ) * maxApp]; +# else + typedef gddAppFuncTableStatus (PV::*pMF_t)(gdd &); + pMNewFuncTbl = new pMF_t [maxApp]; +# endif + if (pMNewFuncTbl) { + for (i=0u; iappTableNElem) { + pMNewFuncTbl[i] = this->pMFuncRead[i]; + } + else { + // + // some versions of NULL include (void *) cast + // (so I am using vanilla zero here) + // + pMNewFuncTbl[i] = 0; + } + } + if (this->pMFuncRead) { + delete [] this->pMFuncRead; + } + this->pMFuncRead = pMNewFuncTbl; + this->appTableNElem = maxApp; + } +} + +// +// gddAppFuncTable::read() +// +template +gddAppFuncTableStatus gddAppFuncTable::read(PV &pv, gdd &value) +{ + gddAppFuncTableStatus status; + + // + // if this gdd is a container then step through it + // and fetch all of the values inside + // + if (value.isContainer()) { + gddContainer *pCont = (gddContainer *) &value; + gddCursor curs = pCont->getCursor(); + gdd *pItem; + + status = S_gddAppFuncTable_Success; + for (pItem=curs.first(); pItem; pItem=curs.next()) + { + status = this->read(pv, *pItem); + if (status) { + break; + } + } + return status; + } + return callReadFunc(pv, value); +} + +// +// gddAppFuncTable::callReadFunc() +// +template +gddAppFuncTableStatus gddAppFuncTable::callReadFunc (PV &pv, gdd &value) +{ + unsigned type; + gddAppFuncTableStatus (PV::*pFunc)(gdd &); + + // + // otherwise call the function associated + // with this application type + // + type = value.applicationType(); + if (type>=this->appTableNElem) { + errPrintf (S_gddAppFuncTable_badType, __FILE__, + __LINE__, "- large appl type code = %u\n", + type); + return S_gddAppFuncTable_badType; + } + pFunc = this->pMFuncRead[type]; + if ( pFunc == 0 ) { + errPrintf (S_gddAppFuncTable_badType, __FILE__, + __LINE__, "- ukn appl type code = %u\n", + type); + return S_gddAppFuncTable_badType; + } + return (pv.*pFunc)(value); +} + +#endif // gddAppFuncTableH + diff --git a/src/gdd/gddAppTable.cc b/src/gdd/gddAppTable.cc new file mode 100644 index 000000000..e410ffbba --- /dev/null +++ b/src/gdd/gddAppTable.cc @@ -0,0 +1,638 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#define epicsExportSharedSymbols +#include "gddAppTable.h" + +// -----------------general destructor for managed gdds-------------------- + +void gddApplicationTypeDestructor::run(void* v) +{ + gdd* ec = (gdd*)v; + // fprintf(stderr," destructing %8.8x\n",v); + gddApplicationTypeTable* db = (gddApplicationTypeTable*)arg; + db->freeDD(ec); +} + +gddApplicationTypeElement::gddApplicationTypeElement(void) { } +gddApplicationTypeElement::~gddApplicationTypeElement(void) { } + +// --------------------------app table stuff----------------------------- + +gddApplicationTypeTable gddApplicationTypeTable::app_table; + +gddApplicationTypeTable& gddApplicationTypeTable::AppTable(void) +{ + return gddApplicationTypeTable::app_table; +} + +gddApplicationTypeTable::gddApplicationTypeTable(aitUint32 tot) +{ + aitUint32 i,total; + + // round tot up to nearest power of 2 + for(i=1u<<31;i && !(tot&i);i>>=1); + if(i==0) + total=1; + else if(i==tot) + total=tot; + else + total=i<<1; + + max_groups=total/APPLTABLE_GROUP_SIZE; + if((max_groups*APPLTABLE_GROUP_SIZE) != total) ++max_groups; + max_allowed=total; + total_registered=1; + attr_table=new gddApplicationTypeElement*[max_groups]; + + for(i=0;inext(); + delete [] blk; + } + } + + if(attr_table[i][j].map) + delete [] attr_table[i][j].map; + break; + case gddApplicationTypeUndefined: break; + default: break; + } + } + delete [] attr_table[i]; + } + } + delete [] attr_table; +} + +int gddApplicationTypeTable::describeDD(gddContainer* dd, FILE* fd, + int level, char* tn) +{ + gddCursor cur = dd->getCursor(); + gdd* pdd; + char tmp[8]; + char* cp; + char* str; + + strcpy(tmp,"unknown"); + + for(pdd=cur.first();pdd;pdd=pdd->next()) + { + if((cp=getName(pdd->applicationType()))==NULL) cp=tmp; + fprintf(fd,"#define gddAppTypeIndex_%s_%s %d\n",tn,cp,level++); + } + + for(pdd=cur.first();pdd;pdd=pdd->next()) + { + if((cp=getName(pdd->applicationType()))==NULL) cp=tmp; + if(pdd->isContainer()) + { + str = new char[strlen(cp)+strlen(tn)+3]; + strcpy(str,tn); + strcat(str,"_"); + strcat(str,cp); + level=describeDD((gddContainer*)pdd,fd,level,str); + delete [] str; + } + } + return level; +} + +void gddApplicationTypeTable::describe(FILE* fd) +{ + unsigned i,j; + gdd* dd; + char* tn; + + fprintf(fd,"\n"); + for(i=0;iisContainer()) + describeDD((gddContainer*)dd,fd,1,tn); + // fprintf(fd,"\n"); + } + break; + case gddApplicationTypeUndefined: break; + default: break; + } + } + } + } + fprintf(fd,"\n"); +} + +gddStatus gddApplicationTypeTable::registerApplicationType( + const char* const name,aitUint32& new_app) +{ + aitUint32 i,group,app,rapp; + gddStatus rc; + + if( (new_app=getApplicationType(name)) ) + { + // gddAutoPrint(gddErrorAlreadyDefined); + return gddErrorAlreadyDefined; + } + if(total_registered>max_allowed) + { + gddAutoPrint("gddAppTable::registerApplicationType()",gddErrorAtLimit); + return gddErrorAtLimit; + } + + { + epicsGuard < epicsMutex > guard ( sem ); + rapp=total_registered++; + } + + if((rc=splitApplicationType(rapp,group,app))<0) return rc; + + if(attr_table[group]) + { + // group already allocated - check is app already refined + if(attr_table[group][app].type!=gddApplicationTypeUndefined) + { + // gddAutoPrint(gddErrorAlreadyDefined); + return gddErrorAlreadyDefined; + } + } + else + { + // group must be allocated + attr_table[group]=new gddApplicationTypeElement[APPLTABLE_GROUP_SIZE]; + + // initialize each element of the group as undefined + for(i=0;i %d\n",name,(int)new_app); + return 0; +} + +// registering a prototype of an empty container causes problems. +// The current implementation does not monitor this so the container +// is not cleared when free. A user may, however, include an empty +// container within a prototype container, and everything will work +// correctly. + +gddStatus gddApplicationTypeTable::registerApplicationTypeWithProto( + const char* const name, gdd* protoDD, aitUint32& new_app) +{ + aitUint32 group,app,rapp; + aitUint8* blk; + size_t sz; + aitIndex tot; + aitUint16 x; + aitUint16 i; + gddStatus rc; + + if( (rc=registerApplicationType(name,new_app)) ) return rc; + + rapp=new_app; + protoDD->setApplType(rapp); + splitApplicationType(rapp,group,app); + + // user gives me the protoDD, so I should not need to reference it. + // Warning, it the user does unreference() on it unknowningly, it will + // go away and cause big problems + + // make sure that the currently registered destructor gets called + // before setting the new one, this should occur in protoDD. + + // be sure to copy data from DD and create buffer that can be copied + // easily when user asks for a DD with proto + + // important!! put destructor into each managed DD - what if atomic? + // protoDD->registerDestructor(new gddApplicationTypeDestructor(protoDD)); + + sz=protoDD->getTotalSizeBytes(); + blk=new aitUint8[sz]; + protoDD->flattenWithAddress(blk,sz,&tot); + attr_table[group][app].proto_size=sz; + attr_table[group][app].total_dds=tot; + protoDD->unreference(); + + attr_table[group][app].type=gddApplicationTypeProto; + attr_table[group][app].proto=(gdd*)blk; + attr_table[group][app].free_list=NULL; + + // create the stupid mapping table - bad implementation for now + attr_table[group][app].map=new aitUint16[total_registered]; + attr_table[group][app].map_size=total_registered; + for(i=0;inext(); + attr_table[group][app].sem.unlock (); + } + else + { + attr_table[group][app].sem.unlock (); + // copy the prototype + blk=new aitUint8[attr_table[group][app].proto_size]; + // fprintf(stderr,"Creating a new proto DD! %d %8.8x\n",app,blk); + attr_table[group][app].proto->flattenWithAddress(blk, + attr_table[group][app].proto_size); + dd=(gdd*)blk; + } + dd->registerDestructor(new gddApplicationTypeDestructor(this)); + dd->markManaged(); // must be sure to mark the thing as managed! + break; + case gddApplicationTypeNormal: + dd=new gdd(app); + // fprintf(stderr,"Creating a new normal DD! %d\n",app); + break; + case gddApplicationTypeUndefined: dd=NULL; break; + default: break; + } + + return dd; +} + +gddStatus gddApplicationTypeTable::freeDD(gdd* dd) +{ + aitUint32 group,app,i; + gddStatus rc; + + if((rc=splitApplicationType(dd->applicationType(),group,app))<0) return rc; + + if(attr_table[group][app].type==gddApplicationTypeProto) + { + // this can be time consuming, clear out all user data from the DD + // this is done because we are allowed to register atomic protos + // that user can attach data to - which causes problems because + // the actual structure of the DD is unknown + + for(i=1;isetNext(attr_table[group][app].free_list); + attr_table[group][app].free_list=dd; + } + else if (attr_table[group][app].type==gddApplicationTypeNormal) + { + // fprintf(stderr,"freeDD a normal DD\n"); + dd->unreference(); + } + else { + fprintf ( stderr,"gddApplicationTypeTable::freeDD - unexpected DD type was %d\n", + attr_table[group][app].type ); + } + + return 0; +} + +gddStatus gddApplicationTypeTable::storeValue(aitUint32 ap, aitUint32 uv) +{ + aitUint32 group,app; + gddStatus rc=0; + + if((rc=splitApplicationType(ap,group,app))<0) return rc; + if(attr_table[group]==NULL || + attr_table[group][app].type==gddApplicationTypeUndefined) + rc=gddErrorNotDefined; + else + attr_table[group][app].user_value=uv; + + gddAutoPrint("gddAppTable::storeValue()",rc); + return rc; +} + +aitUint32 gddApplicationTypeTable::getValue(aitUint32 ap) +{ + aitUint32 group,app; + if(splitApplicationType(ap,group,app)<0) return 0; + if(attr_table[group]==NULL || + attr_table[group][app].type==gddApplicationTypeUndefined) + return 0; + + return attr_table[group][app].user_value; +} + +// ----------------------smart copy functions------------------------ +// the source in the container we must walk through is this called +gddStatus gddApplicationTypeTable::copyDD_src(gdd& dest, const gdd& src) +{ + gddStatus rc=0,s; + gddCursor cur; + gdd* dd; + aitIndex index; + + // this could be done better (faster) if we did not always recurse for + // each GDD. I could have checked for type container before recursing. + + if(src.isContainer()) + { + gddContainer& cdd = (gddContainer&) src; + + // go through src gdd and map app types to index into dest + cur=cdd.getCursor(); + for(dd=cur.first();dd;dd=dd->next()) copyDD_src(dest,*dd); + } + else + { + // find src gdd in dest container and just do put() + s=mapAppToIndex(dest.applicationType(),src.applicationType(),index); + + if(s==0) + rc=dest[index].put(&src); + } + return rc; +} + +// the destination in the container we must walk through is this called +gddStatus gddApplicationTypeTable::copyDD_dest(gdd& dest, const gdd& src) +{ + gddStatus rc=0,s; + gddCursor cur; + gdd* dd; + aitIndex index; + + if(dest.isContainer()) + { + gddContainer& cdd = (gddContainer&) dest; + // go through dest gdd and map app types to index into src + cur=cdd.getCursor(); + for(dd=cur.first();dd;dd=dd->next()) copyDD_dest(*dd,src); + } + else + { + // find dest gdd in src container and just do put() + s=mapAppToIndex(src.applicationType(),dest.applicationType(),index); + + if(s==0) + rc=dest.put(&src[index]); + } + return rc; +} + +gddStatus gddApplicationTypeTable::smartCopy(gdd* dest, const gdd* src) +{ + gddStatus rc = gddErrorNotAllowed; + + // only works with managed containers because app table mapping + // feature is used. + + if(dest->isContainer() && dest->isManaged()) + rc=copyDD_src(*dest,*src); + else if(src->isContainer() && src->isManaged()) + rc=copyDD_dest(*dest,*src); + else if(!src->isContainer() && !dest->isContainer()) { + if ( src->applicationType() == dest->applicationType() ) { + rc=dest->put(src); // both are not containers, let gdd handle it + } + else { + rc=gddErrorNotDefined; + } + } + + gddAutoPrint("gddAppTable::smartCopy()",rc); + + return rc; +} + +// ----------------------smart reference functions------------------------ +// the source in the container we must walk through is this called +gddStatus gddApplicationTypeTable::refDD_src(gdd& dest, const gdd& src) +{ + gddStatus rc=0,s; + gddCursor cur; + gdd* dd; + aitIndex index; + + // this could be done better (faster) if we did not always recurse for + // each GDD. I could have checked for type container before recursing. + + if(src.isContainer()) + { + gddContainer& cdd=(gddContainer&)src; + // go through src gdd and map app types to index into dest + cur=cdd.getCursor(); + for(dd=cur.first();dd;dd=dd->next()) refDD_src(dest,*dd); + } + else + { + // find src gdd in dest container and just do put() + s=mapAppToIndex(dest.applicationType(),src.applicationType(),index); + + if(s==0) + rc=dest[index].putRef(&src); + } + return rc; +} + +// the destination in the container we must walk through is this called +gddStatus gddApplicationTypeTable::refDD_dest(gdd& dest, const gdd& src) +{ + gddStatus rc=0,s; + gddCursor cur; + gdd* dd; + aitIndex index; + + if(dest.isContainer()) + { + gddContainer& cdd=(gddContainer&)dest; + // go through dest gdd and map app types to index into src + cur=cdd.getCursor(); + for(dd=cur.first();dd;dd=dd->next()) refDD_dest(*dd,src); + } + else + { + // find dest gdd in src container and just do put() + s=mapAppToIndex(src.applicationType(),dest.applicationType(),index); + + if(s==0) + rc=dest.putRef(&src[index]); + } + return rc; +} + +gddStatus gddApplicationTypeTable::smartRef(gdd* dest, const gdd* src) +{ + gddStatus rc=0; + + // only works with managed containers because app table mapping + // feature is used. + + if(dest->isContainer() && dest->isManaged()) + rc=refDD_src(*dest,*src); + else if(src->isContainer() && src->isManaged()) + rc=refDD_dest(*dest,*src); + else if(!src->isContainer() && !dest->isContainer()) + rc=dest->putRef(src); // both are not containers, let gdd handle it + else + rc=gddErrorNotAllowed; + + gddAutoPrint("gddAppTable::smartRef()",rc); + return rc; +} diff --git a/src/gdd/gddAppTable.h b/src/gdd/gddAppTable.h new file mode 100644 index 000000000..d10eef3f8 --- /dev/null +++ b/src/gdd/gddAppTable.h @@ -0,0 +1,206 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_APPLTYPE_TABLE_H +#define GDD_APPLTYPE_TABLE_H + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#include "gdd.h" +#include "gddUtils.h" +#include + +// must be power of 2 for group size +#define APPLTABLE_GROUP_SIZE 64 +#define APPLTABLE_GROUP_SIZE_POW 6 + +// default set of application type names +#define GDD_UNITS_SIZE 8 +#define GDD_NAME_UNITS "units" +#define GDD_NAME_MAX_ELEMENTS "maxElements" +#define GDD_NAME_PRECISION "precision" +#define GDD_NAME_GRAPH_HIGH "graphicHigh" +#define GDD_NAME_GRAPH_LOW "graphicLow" +#define GDD_NAME_CONTROL_HIGH "controlHigh" +#define GDD_NAME_CONTROL_LOW "controlLow" +#define GDD_NAME_ALARM_HIGH "alarmHigh" +#define GDD_NAME_ALARM_LOW "alarmLow" +#define GDD_NAME_ALARM_WARN_HIGH "alarmHighWarning" +#define GDD_NAME_ALARM_WARN_LOW "alarmLowWarning" +#define GDD_NAME_VALUE "value" +#define GDD_NAME_ENUM "enums" +#define GDD_NAME_MENUITEM "menuitem" +#define GDD_NAME_STATUS "status" +#define GDD_NAME_SEVERITY "severity" +#define GDD_NAME_TIME_STAMP "timeStamp" +#define GDD_NAME_ALL "all" +#define GDD_NAME_ATTRIBUTES "attributes" +#define GDD_NAME_PV_NAME "name" +#define GDD_NAME_ACKT "ackt" +#define GDD_NAME_ACKS "acks" +#define GDD_NAME_CLASS "class" + +typedef enum +{ + gddApplicationTypeUndefined, + gddApplicationTypeProto, + gddApplicationTypeNormal +} gddApplicationTypeType; + +class gddApplicationTypeTable; + +class gddApplicationTypeDestructor : public gddDestructor +{ +public: + gddApplicationTypeDestructor(gddApplicationTypeTable* v) : gddDestructor(v) { } + void run(void*); +}; + +class gddApplicationTypeElement +{ +public: + gddApplicationTypeElement(void); + ~gddApplicationTypeElement(void); + + char* app_name; + size_t proto_size; + aitIndex total_dds; + gdd* proto; + gdd* free_list; + epicsMutex sem; + gddApplicationTypeType type; + aitUint32 user_value; + aitUint16* map; + aitUint16 map_size; +}; + +// The app table allows registering a prototype DD for app. This class +// allows the user to ask for a DD given a app. The class will manage +// a free list of DD buffers if the app has a prototype DD +// associated with it. The gddApplicationTypeDestructor allows the DD to be +// returned to the free list for the DD app in the app table. + +class epicsShareClass gddApplicationTypeTable +{ +public: + gddApplicationTypeTable(aitUint32 total_number_of_apps=(1<<9)); + ~gddApplicationTypeTable(void); + + // standard method + gddStatus registerApplicationType(const char* const name, aitUint32& app); + gddStatus registerApplicationTypeWithProto(const char* const name, + gdd* protoDD, aitUint32& app); + + // alternative method to register types + aitUint32 registerApplicationType(const char* const name); + aitUint32 registerApplicationTypeWithProto(const char* const name, + gdd* protoDD); + + // hashing still not used for string names to app types + aitUint32 getApplicationType(const char* const name) const; + char* getName(aitUint32 app) const; + gddStatus mapAppToIndex(aitUint32 container_app, + aitUint32 app_to_map, aitUint32& index); + + // copy as best as possible from src to dest, one of the gdd must be + // managed for this to succeed + gddStatus smartCopy(gdd* dest, const gdd* src); + gddStatus smartRef(gdd* dest, const gdd* src); + + // old style interface + int tagC2I(const char* const ctag, int& tag); + int tagC2I(const char* const ctag, int* const tag); + int tagI2C(int tag, char* const ctag); + int insertApplicationType(int tag, const char* ctag); + + gdd* getDD(aitUint32 app); + + // This should be a protected function, users should + // always unreference a DD. Probably can occurs if there is another + // manager in the system. This function cannot distinguish between + // a DD managed by it or someone else. The AppDestructor will call + // this function indirectly by unreferencing the DD. + gddStatus freeDD(gdd*); + + aitUint32 maxAttributes(void) const { return max_allowed; } + aitUint32 totalregistered(void) const { return total_registered; } + void describe(FILE*); + gddStatus storeValue(aitUint32 app, aitUint32 user_value); + aitUint32 getValue(aitUint32 app); + + static gddApplicationTypeTable& AppTable(void); + static gddApplicationTypeTable app_table; + +protected: + void GenerateTypes(void); + + gddStatus copyDD_src(gdd& dest, const gdd& src); + gddStatus copyDD_dest(gdd& dest, const gdd& src); + gddStatus refDD_src(gdd& dest, const gdd& src); + gddStatus refDD_dest(gdd& dest, const gdd& src); + +private: + gddStatus splitApplicationType(aitUint32 r,aitUint32& g,aitUint32& a) const; + aitUint32 group(aitUint32 rapp) const; + aitUint32 index(aitUint32 rapp) const; + int describeDD(gddContainer* dd, FILE* fd, int level, char* tn); + + aitUint32 total_registered; + aitUint32 max_allowed; + aitUint32 max_groups; + + gddApplicationTypeElement** attr_table; + epicsMutex sem; +}; + +inline aitUint32 gddApplicationTypeTable::group(aitUint32 rapp) const + { return (rapp&(~(APPLTABLE_GROUP_SIZE-1)))>>APPLTABLE_GROUP_SIZE_POW; } +inline aitUint32 gddApplicationTypeTable::index(aitUint32 rapp) const + { return rapp&(APPLTABLE_GROUP_SIZE-1); } + +inline gddStatus gddApplicationTypeTable::splitApplicationType(aitUint32 rapp, + aitUint32& g, aitUint32& app) const +{ + gddStatus rc=0; + g=group(rapp); + app=index(rapp); + if(rapp>=total_registered) + { + rc=gddErrorOutOfBounds; + gddAutoPrint("gddAppTable::splitApplicationType()",rc); + } + return rc; +} + +inline aitUint32 gddApplicationTypeTable::registerApplicationType( + const char* const name) +{ + aitUint32 app; + registerApplicationType(name,app); + return app; +} + +inline aitUint32 gddApplicationTypeTable::registerApplicationTypeWithProto( + const char* const name, gdd* protoDD) +{ + aitUint32 app; + registerApplicationTypeWithProto(name,protoDD,app); + return app; +} + +gddApplicationTypeTable* gddGenerateApplicationTypeTable(aitUint32 x=(1<<10)); + +#endif + diff --git a/src/gdd/gddArray.cc b/src/gdd/gddArray.cc new file mode 100644 index 000000000..258e5fd31 --- /dev/null +++ b/src/gdd/gddArray.cc @@ -0,0 +1,103 @@ +/*************************************************************************\ +* 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 +#include +#include + +// Author: Jim Kowalkowski +// Date: 3/97 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +#define epicsExportSharedSymbols +#include "gdd.h" + +// ----------------------The gddAtomic functions------------------------- + +gddAtomic::gddAtomic(int app, aitEnum prim, int dimen, ...): + gdd(app,prim,dimen) +{ + va_list ap; + int i; + aitIndex val; + + va_start(ap,dimen); + for(i=0;i0) + for(i=0;i0) + for(i=0;i0) + for(i=0;i0) + for(i=0;i +#include +#include + +// Author: Jim Kowalkowski +// Date: 3/97 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// +#define epicsExportSharedSymbols +#include "gdd.h" + +// --------------------The gddContainer functions--------------------- + +gddCursor gddContainer::getCursor(void) +{ + return gddCursor (this); +} + +constGddCursor gddContainer::getCursor(void) const +{ + return constGddCursor (this); +} + +gddContainer::gddContainer(void):gdd(0,aitEnumContainer,1) { } +gddContainer::gddContainer(int app):gdd(app,aitEnumContainer,1) { } + +gddContainer::gddContainer(int app,int tot) : gdd(app,aitEnumContainer,1) + { cInit(tot); } + +void gddContainer::cInit(int tot) +{ + int i; + gdd* dd_list; + gdd* temp; + + setBound(0,0,tot); + dd_list=NULL; + + for(i=0;inoReferencing(); + temp->setNext(dd_list); + dd_list=temp; + } + setData(dd_list); +} + + +gddContainer::gddContainer(gddContainer* ec) +{ + // + // added this because the "copy()" below bombs + // if the GDD isnt initialized + // joh - 4-23-99 + // + this->init (ec->appl_type, aitEnumContainer, 1); + +#if 1 + // + // this replaces some the strange code below + // that existed before + // joh - 4-23-99 + // + copyInfo(ec); +#else + unsigned i; + gdd* dd_list; + gdd* temp; + + copy(ec); + dd_list=NULL; + + // this needs to recursively add to the container, only copy the + // info and bounds information and scaler data, not arrays + + for(i=0;inoReferencing(); + temp->setNext(dd_list); + dd_list=temp; + } + setData(dd_list); +#endif +} + +gddStatus gddContainer::insert(gdd* dd) +{ + dd->setNext(cData()); + setData(dd); + bounds->setSize(bounds->size()+1); + return 0; +} + +gddStatus gddContainer::remove(aitIndex index) +{ + gddCursor cur = getCursor(); + gdd *dd,*prev_dd; + aitIndex i; + + prev_dd=NULL; + + for(i=0; (dd=cur[i]) && i!=index; i++) prev_dd=dd; + + if(i==index && dd) + { + if(prev_dd) + prev_dd->setNext(dd->next()); + else + setData(dd->next()); + + dd->unreference(); + bounds->setSize(bounds->size()-1); + return 0; + } + else + { + gddAutoPrint("gddContainer::remove()",gddErrorOutOfBounds); + return gddErrorOutOfBounds; + } +} + +// ------------------------cursor functions------------------------------- + +const gdd* constGddCursor::operator[](int index) +{ + int i,start; + const gdd* dd; + + if(index>=curr_index) + { + start=curr_index; + dd=curr; + } + else + { + start=0; + dd=list->cData(); + } + + for(i=start;inext(); + curr_index=index; + curr=dd; + return dd; +} + diff --git a/src/gdd/gddContainer.h b/src/gdd/gddContainer.h new file mode 100644 index 000000000..4b7a3be96 --- /dev/null +++ b/src/gdd/gddContainer.h @@ -0,0 +1,114 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_CONTAINER_H +#define GDD_CONTAINER_H + +/* + * Author: Jim Kowalkowski + * Date: 3/97 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ + +#include "shareLib.h" + +class constGddCursor; +class gddCursor; + +/* this class needs to be able to register a destructor for the container */ + +class epicsShareClass gddContainer : public gdd +{ +public: + gddContainer(void); + gddContainer(int app); + gddContainer(gddContainer* ac); + gddContainer(int app,int number_of_things_in_it); + + gddStatus insert(gdd*); + gddStatus remove(aitIndex index); + int total(void) const; + + void dump(void) const; + void test(void); + + // preferred method for looking into a container + gddCursor getCursor(void); + constGddCursor getCursor(void) const; + + const gdd* cData(void) const; + gdd* cData(void); + +protected: + gddContainer(int,int,int,int*); + ~gddContainer(void); + + void cInit(int num_things_within); + gddStatus changeType(int,aitEnum); + gddStatus setBound(int,aitIndex,aitIndex); + gddStatus getBound(int,aitIndex&,aitIndex&) const; + gddStatus setBound(aitIndex,aitIndex); + +private: + friend class constGddCursor; +}; + +class epicsShareClass constGddCursor { +public: + constGddCursor(void); + constGddCursor(const gddContainer* ec); + + const gdd* first(void); + const gdd* first(const gddScalar*&); + const gdd* first(const gddAtomic*&); + const gdd* first(const gddContainer*&); + + const gdd* next(void); + const gdd* next(const gddScalar*&); + const gdd* next(const gddAtomic*&); + const gdd* next(const gddContainer*&); + + const gdd* current(void) const; + const gdd* current(const gddScalar*&) const; + const gdd* current(const gddAtomic*&) const; + const gdd* current(const gddContainer*&) const; + + const gdd* operator[](int index); + +private: + const gddContainer* list; + const gdd* curr; + int curr_index; +}; + +class epicsShareClass gddCursor : private constGddCursor { +public: + gddCursor(void); + gddCursor(gddContainer* ec); + + gdd* first(void); + gdd* first(gddScalar*&); + gdd* first(gddAtomic*&); + gdd* first(gddContainer*&); + + gdd* next(void); + gdd* next(gddScalar*&); + gdd* next(gddAtomic*&); + gdd* next(gddContainer*&); + + gdd* current(void) const; + gdd* current(gddScalar*&) const; + gdd* current(gddAtomic*&) const; + gdd* current(gddContainer*&) const; + + gdd* operator[](int index); +}; + +#endif diff --git a/src/gdd/gddContainerI.h b/src/gdd/gddContainerI.h new file mode 100644 index 000000000..ba1253ee8 --- /dev/null +++ b/src/gdd/gddContainerI.h @@ -0,0 +1,112 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_CONTAINERI_H +#define GDD_CONTAINERI_H + +/* + * Author: Jim Kowalkowski + * Date: 3/97 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +inline gddContainer::gddContainer(int,int,int,int*) { } +inline gddContainer::~gddContainer(void) { } + +inline gddStatus gddContainer::changeType(int,aitEnum) { + gddAutoPrint("gddContainer::changeType()",gddErrorNotAllowed); + return gddErrorNotAllowed; } +inline gddStatus gddContainer::setBound(int,aitIndex,aitIndex) { + gddAutoPrint("setBound()",gddErrorNotAllowed); + return gddErrorNotAllowed; } +inline gddStatus gddContainer::getBound(int,aitIndex&,aitIndex&) const { + gddAutoPrint("getBound()",gddErrorNotAllowed); + return gddErrorNotAllowed; } + +inline gdd* gddContainer::cData(void) + { return (gdd*)dataPointer(); } + +inline const gdd* gddContainer::cData(void) const + { return (const gdd*)dataPointer(); } + +inline int gddContainer::total(void) const + { return bounds->size(); } + +inline gddStatus gddContainer::setBound(aitIndex f, aitIndex c) + { bounds->set(f,c); return 0; } + +inline constGddCursor::constGddCursor(void):list(NULL) + { curr=NULL; } +inline constGddCursor::constGddCursor(const gddContainer* ec):list(ec) + { curr=ec->cData(); curr_index=0; } + +inline const gdd* constGddCursor::first(void) + { curr=list->cData(); curr_index=0; return curr; } +inline const gdd* constGddCursor::first(const gddScalar*& dd) + { return (const gdd*)(dd=(gddScalar*)first()); } +inline const gdd* constGddCursor::first(const gddAtomic*& dd) + { return (const gdd*)(dd=(gddAtomic*)first()); } +inline const gdd* constGddCursor::first(const gddContainer*& dd) + { return (const gdd*)(dd=(gddContainer*)first()); } + +inline const gdd* constGddCursor::next(void) + { if(curr) { curr_index++;curr=curr->next(); } return curr; } +inline const gdd* constGddCursor::next(const gddScalar*& dd) + { return (const gdd*)(dd=(gddScalar*)next()); } +inline const gdd* constGddCursor::next(const gddAtomic*& dd) + { return (const gdd*)(dd=(gddAtomic*)next()); } +inline const gdd* constGddCursor::next(const gddContainer*& dd) + { return (const gdd*)(dd=(gddContainer*)next()); } + +inline const gdd* constGddCursor::current(void) const + { return curr; } +inline const gdd* constGddCursor::current(const gddScalar*& dd) const + { return (const gdd*)(dd=(gddScalar*)current()); } +inline const gdd* constGddCursor::current(const gddAtomic*& dd) const + { return (const gdd*)(dd=(gddAtomic*)current()); } +inline const gdd* constGddCursor::current(const gddContainer*& dd) const + { return (const gdd*)(dd=(gddContainer*)current()); } + +inline gddCursor::gddCursor(void){} +inline gddCursor::gddCursor(gddContainer* ec) : + constGddCursor(ec) {} + +inline gdd* gddCursor::first(void) + { return (gdd *) constGddCursor::first(); } +inline gdd* gddCursor::first(gddScalar*& dd) + { return (gdd *) constGddCursor::first((const gddScalar*&)dd); } +inline gdd* gddCursor::first(gddAtomic*& dd) + { return (gdd *) constGddCursor::first((const gddAtomic*&)dd); } +inline gdd* gddCursor::first(gddContainer*& dd) + { return (gdd *) constGddCursor::first((const gddContainer*&)dd); } + +inline gdd* gddCursor::next(void) + { return (gdd *) constGddCursor::next(); } +inline gdd* gddCursor::next(gddScalar*& dd) + { return (gdd*)constGddCursor::next((const gddScalar*&)dd); } +inline gdd* gddCursor::next(gddAtomic*& dd) + { return (gdd*)constGddCursor::next((const gddAtomic*&)dd); } +inline gdd* gddCursor::next(gddContainer*& dd) + { return (gdd*)constGddCursor::next((const gddContainer*&)dd); } + +inline gdd* gddCursor::current(void) const + { return (gdd*)constGddCursor::current(); } +inline gdd* gddCursor::current(gddScalar*& dd) const + { return (gdd*)constGddCursor::current((const gddScalar*&)dd); } +inline gdd* gddCursor::current(gddAtomic*& dd) const + { return (gdd*)constGddCursor::current((const gddAtomic*&)dd); } +inline gdd* gddCursor::current(gddContainer*& dd) const + { return (gdd*)constGddCursor::current((const gddContainer*&)dd); } + +inline gdd* gddCursor::operator[](int index) +{ return (gdd *) constGddCursor::operator [](index); } + +#endif diff --git a/src/gdd/gddEnumStringTable.cc b/src/gdd/gddEnumStringTable.cc new file mode 100644 index 000000000..fd74b35d8 --- /dev/null +++ b/src/gdd/gddEnumStringTable.cc @@ -0,0 +1,152 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// gddEnumStringTable.cc +// Author: Jeff Hill +// + +#include + +#include + +#define epicsExportSharedSymbols +#include "gddEnumStringTable.h" + +gddEnumStringTable::~gddEnumStringTable () +{ + for ( unsigned i = 0u; i < this->nStringSlots; i++ ) { + delete [] this->pStringTable[i].pString; + } + delete [] this->pStringTable; +} + +bool gddEnumStringTable::expand ( unsigned nStringsRequired ) +{ + stringEntry * pNextTable = new ( std::nothrow ) stringEntry [nStringsRequired]; + if ( pNextTable ) { + for ( unsigned i = 0u; i < this->nStringSlots; i++ ) { + pNextTable[i] = this->pStringTable[i]; + } + for ( unsigned j = this->nStringSlots; j < nStringsRequired; j++ ) { + pNextTable[j].pString = 0; + pNextTable[j].length = 0; + } + delete [] this->pStringTable; + this->pStringTable = pNextTable; + this->nStringSlots = nStringsRequired; + return true; + } + else { + return false; + } +} + +void gddEnumStringTable::reserve ( unsigned nStringsIn ) +{ + if ( nStringsIn > this->nStringSlots ) { + this->expand ( nStringsIn ); + } +} + +void gddEnumStringTable::clear () +{ + for ( unsigned i = 0u; i < this->nStringSlots; i++ ) { + delete [] this->pStringTable[i].pString; + } + delete [] this->pStringTable; + this->pStringTable = 0; + this->nStringSlots = 0; + this->nStrings = 0; +} + +bool gddEnumStringTable::setString ( unsigned index, const char *pString ) +{ + if ( index >= this->nStringSlots ) { + unsigned nStringsNext; + if ( this->nStringSlots < 16 ) { + nStringsNext = 16; + } + else { + nStringsNext = this->nStringSlots; + } + while ( index >= nStringsNext ) { + nStringsNext += nStringsNext; + } + if ( ! this->expand ( nStringsNext ) ) { + return false; + } + } + unsigned nChar = strlen ( pString ); + char *pNewString = new ( std::nothrow ) char [ nChar + 1 ]; + if ( ! pNewString ) { + return false; + } + delete [] this->pStringTable[index].pString; + this->pStringTable[index].pString = pNewString; + strcpy ( this->pStringTable[index].pString, pString ); + this->pStringTable[index].length = nChar; + if ( this->nStrings <= index ) { + this->nStrings = index + 1; + } + return true; +} + +void gddEnumStringTable::getString ( unsigned index, char *pBuf, unsigned size ) const +{ + if ( index < this->nStrings && size ) { + if ( this->pStringTable[index].pString ) { + strncpy ( pBuf, this->pStringTable[index].pString, size ); + pBuf[ size - 1 ] = '\0'; + } + else { + pBuf[ 0 ] = '\0'; + } + } + else { + pBuf[ 0 ] = '\0'; + } +} + +const char * gddEnumStringTable::getString ( unsigned index ) const +{ + if ( index < this->nStrings ) { + if ( this->pStringTable[index].pString ) { + return this->pStringTable[index].pString; + } + else { + return ""; + } + } + else { + return ""; + } +} + +unsigned gddEnumStringTable::getStringLength ( unsigned index ) const +{ + if ( index < this->nStrings ) { + return this->pStringTable[index].length; + } + else { + return 0; + } +} + +bool gddEnumStringTable::getIndex ( const char * pString, unsigned & indexOut ) const +{ + for ( unsigned index = 0u; index < this->nStrings; index++ ) { + if ( ! strcmp ( pString, this->pStringTable[index].pString ) ) { + indexOut = index; + return true; + } + } + return false; +} + diff --git a/src/gdd/gddEnumStringTable.h b/src/gdd/gddEnumStringTable.h new file mode 100644 index 000000000..5d92433a8 --- /dev/null +++ b/src/gdd/gddEnumStringTable.h @@ -0,0 +1,50 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// gddEnumStringTable.h +// Author: Jeff Hill +// + +#ifndef gddEnumStringTableh +#define gddEnumStringTableh + +#include "shareLib.h" + +class epicsShareClass gddEnumStringTable { +public: + gddEnumStringTable (); + ~gddEnumStringTable (); + void clear (); + void reserve ( unsigned nStrings ); + bool setString ( unsigned index, const char *pString ); + void getString ( unsigned index, char *pBuf, unsigned size ) const; + const char * getString ( unsigned index ) const; + unsigned getStringLength ( unsigned index ) const; + bool getIndex ( const char * pString, unsigned & index ) const; + unsigned numberOfStrings () const; +private: + unsigned nStrings; + unsigned nStringSlots; + struct stringEntry { + char * pString; + unsigned length; + } * pStringTable; + bool expand ( unsigned nStringsRequired ); +}; + +inline gddEnumStringTable::gddEnumStringTable () : + nStrings ( 0 ), nStringSlots ( 0 ), pStringTable ( 0 ) {} + +inline unsigned gddEnumStringTable::numberOfStrings () const +{ + return this->nStrings; +} + +#endif // ifndef gddEnumStringTableh diff --git a/src/gdd/gddErrorCodes.cc b/src/gdd/gddErrorCodes.cc new file mode 100644 index 000000000..4902e20bf --- /dev/null +++ b/src/gdd/gddErrorCodes.cc @@ -0,0 +1,33 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// Author: Jim Kowalkowski +// Date: 3/97 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +// -------------------------- Error messages ------------------------- + +const char* gddErrorMessages[]= +{ + "Invalid", + "TypeMismatch", + "NotAllowed", + "AlreadyDefined", + "NewFailed", + "OutOfBounds", + "AtLimit", + "NotDefined", + "NotSupported", + "Overflow", + "Underflow" +}; + diff --git a/src/gdd/gddErrorCodes.h b/src/gdd/gddErrorCodes.h new file mode 100644 index 000000000..cd915e64e --- /dev/null +++ b/src/gdd/gddErrorCodes.h @@ -0,0 +1,57 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_ERROR_CODES_H +#define GDD_ERROR_CODES_H + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#include "shareLib.h" + +/* + gdd.cc contains a table (gddErrorMessages) that has all the text + strings for each of the error codes +*/ + +typedef long gddStatus; + +#define gddErrorTypeMismatch -1 +#define gddErrorNotAllowed -2 +#define gddErrorAlreadyDefined -3 +#define gddErrorNewFailed -4 +#define gddErrorOutOfBounds -5 +#define gddErrorAtLimit -6 +#define gddErrorNotDefined -7 +#define gddErrorNotSupported -8 +#define gddErrorOverflow -9 +#define gddErrorUnderflow -10 + +epicsShareExtern char* gddErrorMessages[]; + +#define gddPrintError(x) \ + fprintf(stderr,"gdd Error: %s\n",gddErrorMessages[x*(-1)]); + +#define gddPrintErrorWithMessage(msg,x) \ + fprintf(stderr,"gdd Error: %s (%s)\n",gddErrorMessages[x*(-1)],msg); + +#define gddGetErrorMessage(x) gddErrorMessages[x*(-1)] + +#ifdef GDDAUTOPRINT +#define gddAutoPrint(s,x) if(x) gddPrintErrorWithMessage(s,x) +#else +#define gddAutoPrint(s,x) ; +#endif + +#endif diff --git a/src/gdd/gddI.h b/src/gdd/gddI.h new file mode 100644 index 000000000..74c943a92 --- /dev/null +++ b/src/gdd/gddI.h @@ -0,0 +1,692 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDDI_H +#define GDDI_H + +/* + * Author: Jim Kowalkowski + * Date: 3/97 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +inline void gdd::setData(void* d) { data.Pointer=d; } +inline const gddDestructor* gdd::destructor(void) const { return destruct; } + +inline gdd::gdd(void) { init(0,aitEnumInvalid,0); } +inline gdd::gdd(int app) { init(app,aitEnumInvalid,0); } +inline gdd::gdd(int app,aitEnum prim) { init(app,prim,0); } + +inline unsigned gdd::applicationType(void) const{ return appl_type; } +inline aitEnum gdd::primitiveType(void) const { return (aitEnum)prim_type; } +inline const gddBounds* gdd::getBounds(void) const { return bounds; } +inline const gddBounds* gdd::getBounds(int bn) const { return &bounds[bn]; } + +inline gdd* gdd::next(void) { return nextgdd; } +inline const gdd* gdd::next(void) const { return nextgdd; } +inline void gdd::setNext(gdd* n) { nextgdd=n; } +inline unsigned gdd::dimension(void) const { return dim; } +inline aitType& gdd::getData(void) { return data; } +inline const aitType& gdd::getData(void) const { return data; } +inline aitType* gdd::dataUnion(void) { return &data; } +inline const aitType* gdd::dataUnion(void)const { return &data; } +inline void gdd::setApplType(int t) { appl_type=(aitUint16)t; } +inline gddStatus gdd::copyInfo(const gdd* dd) { return copyStuff(dd,0); } +inline gddStatus gdd::copy(const gdd* dd) { return copyStuff(dd,1); } +inline gddStatus gdd::Dup(const gdd* dd) { return copyStuff(dd,2); } +inline const void* gdd::dataAddress(void) const { return (void*)&data; } +inline void* gdd::dataAddress(void) { return (void*)&data; } +inline const void* gdd::dataPointer(void) const { return data.Pointer; } +inline void* gdd::dataPointer(void) { return data.Pointer; } + +inline const void* gdd::dataVoid(void) const +{ + return (dimension()||primitiveType()==aitEnumFixedString)? + dataPointer():dataAddress(); +} + +inline void* gdd::dataVoid(void) +{ + return (dimension()||primitiveType()==aitEnumFixedString)? + dataPointer():dataAddress(); +} + +inline aitUint32 gdd::align8(unsigned long count) const +{ + unsigned long tmp=count&(~((unsigned long)0x07)); + return (tmp!=count)?tmp+8:tmp; +} + +inline const void* gdd::dataPointer(aitIndex f) const + { return (void*)(((aitUint8*)dataPointer())+aitSize[primitiveType()]*f); } + +inline void* gdd::dataPointer(aitIndex f) + { return (void*)(((aitUint8*)dataPointer())+aitSize[primitiveType()]*f); } + +inline int gdd::isManaged(void) const { return flags&GDD_MANAGED_MASK; } +inline int gdd::isFlat(void) const { return flags&GDD_FLAT_MASK; } +inline int gdd::isNoRef(void) const { return flags&GDD_NOREF_MASK; } +inline int gdd::isConstant(void) const { return flags&GDD_CONSTANT_MASK; } +inline int gdd::isLocalDataFormat(void) const { return !(flags&GDD_NET_MASK); } +inline int gdd::isNetworkDataFormat(void) const + { return !isLocalDataFormat() || aitLocalNetworkDataFormatSame; } + +inline void gdd::markConstant(void) { flags|=GDD_CONSTANT_MASK; } // X aCC 818 +inline void gdd::markFlat(void) { flags|=GDD_FLAT_MASK; } // X aCC 818 +inline void gdd::markManaged(void) { flags|=GDD_MANAGED_MASK; } // X aCC 818 +inline void gdd::markUnmanaged(void) { flags&=~GDD_MANAGED_MASK; } // X aCC 818 +inline void gdd::markLocalDataFormat(void) { flags&=~GDD_NET_MASK; } // X aCC 818 +inline void gdd::markNotLocalDataFormat(void) { flags|=GDD_NET_MASK; } // X aCC 818 + +inline void gdd::getTimeStamp(struct timespec* const ts) const { time_stamp.get(*ts); } +inline void gdd::setTimeStamp(const struct timespec* const ts) { time_stamp=*ts; } + +inline void gdd::getTimeStamp(aitTimeStamp* const ts) const { *ts = time_stamp; } +inline void gdd::setTimeStamp(const aitTimeStamp* const ts) { time_stamp=*ts; } + +inline void gdd::getTimeStamp(struct epicsTimeStamp* const ts) const { time_stamp.get(*ts); } +inline void gdd::setTimeStamp(const struct epicsTimeStamp* const ts) { time_stamp=*ts; } + +inline void gdd::setStatus(aitUint32 s) { status.u = s; } +inline void gdd::getStatus(aitUint32& s) const { s = status.u; } +inline void gdd::setStatus(aitUint16 high, aitUint16 low) + { status.u = (aitUint32)high << 16 | low; } +inline void gdd::getStatus(aitUint16& high, aitUint16& low) const + { high = (aitUint16)(status.u >> 16); + low = (aitUint16)(status.u & 0x0000ffff); } + +inline void gdd::setStat(aitUint16 s) + { status.s.aitStat = s; } +inline void gdd::setSevr(aitUint16 s) + { status.s.aitSevr = s; } +inline aitUint16 gdd::getStat(void) const + { return status.s.aitStat; } +inline aitUint16 gdd::getSevr(void) const + { return status.s.aitSevr; } +inline void gdd::getStatSevr(aitInt16& stat, aitInt16& sevr) const + { stat = status.s.aitStat; sevr = status.s.aitSevr; } +inline void gdd::setStatSevr(aitInt16 stat, aitInt16 sevr) + { status.s.aitStat = stat; status.s.aitSevr = sevr; } + +inline gdd& gdd::operator=(const gdd& v) + { memcpy(this,&v,sizeof(gdd)); return *this; } + +inline int gdd::isScalar(void) const { return dimension()==0?1:0; } +inline int gdd::isContainer(void) const + { return (primitiveType()==aitEnumContainer)?1:0; } +inline int gdd::isAtomic(void) const + { return (dimension()>0&&primitiveType()!=aitEnumContainer)?1:0; } +inline gddStatus gdd::noReferencing(void) +{ + int rc=0; + if(ref_cnt>1) + { + gddAutoPrint("gdd::noReferencing()",gddErrorNotAllowed); + rc=gddErrorNotAllowed; + } + else flags|=GDD_NOREF_MASK; // X aCC 818 + return rc; +} +inline gddStatus gdd::reference(void) const +{ + epicsGuard < epicsMutex > guard ( * gdd::pGlobalMutex ); + + int rc=0; + + if(isNoRef()) + { + fprintf(stderr,"reference of gdd marked \"no-referencing\" ignored!!\n"); + gddAutoPrint("gdd::reference()",gddErrorNotAllowed); + rc = gddErrorNotAllowed; + } + else if ( this->ref_cnt < 0xffffffff ) { + this->ref_cnt++; // X aCC 818 + } + else { + fprintf(stderr,"gdd reference count overflow!!\n"); + gddAutoPrint("gdd::reference()",gddErrorOverflow); + rc=gddErrorOverflow; + } + return rc; +} + +inline gddStatus gdd::unreference(void) const +{ + epicsGuard < epicsMutex > guard ( * gdd::pGlobalMutex ); + + int rc=0; + + if ( ref_cnt > 1u ) { + ref_cnt--; + } + else if ( ref_cnt == 1u ) + { + if ( isManaged() ) { + // managed dd always destroys the entire thing + if(destruct) destruct->destroy((void *)this); + destruct=NULL; + } + else if(!isFlat()) { + // hopefully catch ref/unref missmatches while + // gdd is on free list + ref_cnt = 0; + delete this; + } + } + else { + fprintf(stderr,"gdd reference count underflow!!\n"); + gddAutoPrint("gdd::unreference()",gddErrorUnderflow); + rc=gddErrorUnderflow; + } + return rc; +} + +inline void gdd::adjust(gddDestructor* d, void* v, aitEnum type,aitDataFormat) +{ + if(destruct) destruct->destroy(dataPointer()); + destruct=d; + if(destruct) destruct->reference(); + setPrimType(type); + setData(v); +} + +// These function do NOT work well for aitFixedString because +// fixed strings are always stored by reference. You WILL get +// into trouble is the gdd does not contain a fixed string +// before you putConvert() something into it. + +#if aitLocalNetworkDataFormatSame == AIT_FALSE +inline void gdd::get(aitEnum t,void* v,aitDataFormat f) const +#else +inline void gdd::get(aitEnum t,void* v,aitDataFormat) const +#endif +{ + if(primitiveType()==aitEnumFixedString) + { + if(dataPointer()) aitConvert(t,v,primitiveType(),dataPointer(),1); + } + else + { +#if aitLocalNetworkDataFormatSame == AIT_FALSE + if(f!=aitLocalDataFormat) + aitConvertToNet(t,v,primitiveType(),dataAddress(),1); + else +#endif + aitConvert(t,v,primitiveType(),dataAddress(),1); + } +} + +#if aitLocalNetworkDataFormatSame == AIT_FALSE +inline void gdd::set(aitEnum t,const void* v,aitDataFormat f) +#else +inline void gdd::set(aitEnum t,const void* v,aitDataFormat) +#endif +{ + if (primitiveType()==aitEnumInvalid) { + this->setPrimType (t); + } + +#if aitLocalNetworkDataFormatSame == AIT_FALSE + if(f!=aitLocalDataFormat) + aitConvertFromNet(primitiveType(),dataVoid(),t,v,1); + else +#endif + aitConvert(primitiveType(),dataVoid(),t,v,1); + + markLocalDataFormat(); +} + +// -------------------getRef(data pointer) functions---------------- +inline void gdd::getRef(const aitFloat64*& d)const { d=(aitFloat64*)dataVoid(); } +inline void gdd::getRef(const aitFloat32*& d)const { d=(aitFloat32*)dataVoid(); } +inline void gdd::getRef(const aitUint32*& d)const { d=(aitUint32*)dataVoid(); } +inline void gdd::getRef(const aitInt32*& d)const { d=(aitInt32*)dataVoid(); } +inline void gdd::getRef(const aitUint16*& d)const { d=(aitUint16*)dataVoid(); } +inline void gdd::getRef(const aitInt16*& d)const { d=(aitInt16*)dataVoid(); } +inline void gdd::getRef(const aitUint8*& d)const { d=(aitUint8*)dataVoid(); } +inline void gdd::getRef(const aitInt8*& d)const { d=(aitInt8*)dataVoid(); } +inline void gdd::getRef(const void*& d)const { d=dataVoid(); } +inline void gdd::getRef(const aitFixedString*& d)const { d=(aitFixedString*)dataVoid(); } +inline void gdd::getRef(const aitString*& d)const { d=(aitString*)dataVoid(); } +inline void gdd::getRef(aitFloat64*& d) { d=(aitFloat64*)dataVoid(); } +inline void gdd::getRef(aitFloat32*& d) { d=(aitFloat32*)dataVoid(); } +inline void gdd::getRef(aitUint32*& d) { d=(aitUint32*)dataVoid(); } +inline void gdd::getRef(aitInt32*& d) { d=(aitInt32*)dataVoid(); } +inline void gdd::getRef(aitUint16*& d) { d=(aitUint16*)dataVoid(); } +inline void gdd::getRef(aitInt16*& d) { d=(aitInt16*)dataVoid(); } +inline void gdd::getRef(aitUint8*& d) { d=(aitUint8*)dataVoid(); } +inline void gdd::getRef(aitInt8*& d) { d=(aitInt8*)dataVoid(); } +inline void gdd::getRef(void*& d) { d=dataVoid(); } +inline void gdd::getRef(aitFixedString*& d) { d=(aitFixedString*)dataVoid(); } +inline void gdd::getRef(aitString*& d) { d=(aitString*)dataVoid(); } + +// -------------------putRef(data pointer) functions---------------- +inline void gdd::putRef(void* v,aitEnum code, gddDestructor* d) + { adjust(d, v, code); } +inline void gdd::putRef(aitFloat64* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumFloat64); } +inline void gdd::putRef(aitFloat32* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumFloat32); } +inline void gdd::putRef(aitUint8* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumUint8); } +inline void gdd::putRef(aitInt8* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumInt8); } +inline void gdd::putRef(aitUint16* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumUint16); } +inline void gdd::putRef(aitInt16* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumInt16); } +inline void gdd::putRef(aitUint32* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumUint32); } +inline void gdd::putRef(aitInt32* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumInt32); } +inline void gdd::putRef(aitString* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumString); } +inline void gdd::putRef(aitFixedString* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumFixedString); } + +// -------------------putRef(const data pointer) functions---------------- +inline void gdd::putRef(const aitFloat64* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumFloat64); markConstant(); } +inline void gdd::putRef(const aitFloat32* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumFloat32); markConstant(); } +inline void gdd::putRef(const aitUint8* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumUint8); markConstant(); } +inline void gdd::putRef(const aitInt8* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumInt8); markConstant(); } +inline void gdd::putRef(const aitUint16* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumUint16); markConstant(); } +inline void gdd::putRef(const aitInt16* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumInt16); markConstant(); } +inline void gdd::putRef(const aitUint32* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumUint32); markConstant(); } +inline void gdd::putRef(const aitInt32* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumInt32); markConstant(); } +inline void gdd::putRef(const aitString* v, gddDestructor* d) + { adjust(d, (void*)v, aitEnumString); markConstant(); } +inline void gdd::putRef(const aitFixedString* v,gddDestructor* d) + { adjust(d, (void*)v, aitEnumFixedString); markConstant(); } + +// ---------------------get(pointer) functions-------------------------- +inline void gdd::get(void* d) const { + if(isScalar()) + aitConvert(primitiveType(),d,primitiveType(),dataAddress(),1); + else + aitConvert(primitiveType(),d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(void* d,aitEnum e) const { + if(isScalar()) + aitConvert(e,d,primitiveType(),dataAddress(),1); + else + aitConvert(e,d,primitiveType(),dataPointer(),getDataSizeElements()); +} +inline void gdd::get(aitFloat64* d) const +{ + if(isScalar()) + aitConvert(aitEnumFloat64,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumFloat64,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitFloat32* d) const { + if(isScalar()) + aitConvert(aitEnumFloat32,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumFloat32,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitUint32* d) const { + if(isScalar()) + aitConvert(aitEnumUint32,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumUint32,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitInt32* d) const { + if(isScalar()) + aitConvert(aitEnumInt32,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumInt32,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitUint16* d) const { + if(isScalar()) + aitConvert(aitEnumUint16,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumUint16,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitInt16* d) const { + if(isScalar()) + aitConvert(aitEnumInt16,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumInt16,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitUint8* d) const { + if(isScalar()) + aitConvert(aitEnumUint8,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumUint8,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitString* d) const { + if(isScalar()) + aitConvert(aitEnumString,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumString,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} +inline void gdd::get(aitFixedString* d) const { + if(isScalar()) + aitConvert(aitEnumFixedString,d,primitiveType(),dataAddress(),1); + else + aitConvert(aitEnumFixedString,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} + +// special case for string scalar to aitInt8 array! +inline void gdd::get(aitInt8* d) const +{ + if(primitiveType()==aitEnumString && dim==0) + { + aitString* str = (aitString*)dataAddress(); + strcpy((char*)d,str->string()); // X aCC 392 + } + else if(primitiveType()==aitEnumFixedString && dim==0) + strcpy((char*)d,data.FString->fixed_string); // X aCC 392 + else + aitConvert(aitEnumInt8,d,primitiveType(),dataPointer(), + getDataSizeElements()); +} + +// -------------------getConvert(scalar) functions ---------------------- +inline void gdd::getConvert(aitFloat64& d) const { get(&d, aitEnumFloat64); } +inline void gdd::getConvert(aitFloat32& d) const { get(&d, aitEnumFloat32); } +inline void gdd::getConvert(aitUint32& d) const { get(&d, aitEnumUint32); } +inline void gdd::getConvert(aitInt32& d) const { get(&d, aitEnumInt32); } +inline void gdd::getConvert(aitUint16& d) const { get(&d, aitEnumUint16); } +inline void gdd::getConvert(aitInt16& d) const { get(&d, aitEnumInt16); } +inline void gdd::getConvert(aitUint8& d) const { get(&d, aitEnumUint8); } +inline void gdd::getConvert(aitInt8& d) const { get(&d, aitEnumInt8); } + +// -------------------putConvert(scalar) functions ---------------------- +inline void gdd::putConvert(aitFloat64 d){ set(aitEnumFloat64,&d); } +inline void gdd::putConvert(aitFloat32 d){ set(aitEnumFloat32,&d); } +inline void gdd::putConvert(aitUint32 d) { set(aitEnumUint32,&d); } +inline void gdd::putConvert(aitInt32 d) { set(aitEnumInt32,&d); } +inline void gdd::putConvert(aitUint16 d) { set(aitEnumUint16,&d); } +inline void gdd::putConvert(aitInt16 d) { set(aitEnumInt16,&d); } +inline void gdd::putConvert(aitUint8 d) { set(aitEnumUint8,&d); } +inline void gdd::putConvert(aitInt8 d) { set(aitEnumInt8,&d); } + +// ------------------------put(pointer) functions---------------------- +inline gddStatus gdd::put(const aitFloat64* const d) + { return genCopy(aitEnumFloat64,d); } +inline gddStatus gdd::put(const aitFloat32* const d) + { return genCopy(aitEnumFloat32,d); } +inline gddStatus gdd::put(const aitUint32* const d) + { return genCopy(aitEnumUint32,d); } +inline gddStatus gdd::put(const aitInt32* const d) + { return genCopy(aitEnumInt32,d); } +inline gddStatus gdd::put(const aitUint16* const d) + { return genCopy(aitEnumUint16,d); } +inline gddStatus gdd::put(const aitInt16* const d) + { return genCopy(aitEnumInt16,d); } +inline gddStatus gdd::put(const aitUint8* const d) + { return genCopy(aitEnumUint8,d); } + +// special case for aitInt8 array to aitString scalar +inline gddStatus gdd::put(const aitInt8* const d) +{ + gddStatus rc=0; + + if(primitiveType()==aitEnumString && dim==0) + { + aitString* p = (aitString*)dataAddress(); + p->copy((char*)d); + } + else if(primitiveType()==aitEnumFixedString && dim==0) { + strncpy(data.FString->fixed_string,(char*)d, + sizeof(aitFixedString)); + data.FString->fixed_string[sizeof(aitFixedString)-1u]='\0'; + } + else + rc=genCopy(aitEnumInt8,d); + + return rc; +} + +// ----------------put(scalar) functions---------------- +inline gddStatus gdd::put(aitFloat64 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumFloat64); data.Float64=d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitFloat32 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumFloat32); data.Float32=d;} + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitUint32 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumUint32); data.Uint32=d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitInt32 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumInt32); data.Int32=d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitUint16 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumUint16); data.Uint16=d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitInt16 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumInt16); data.Int16=d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitUint8 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumUint8); data.Uint8=d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitInt8 d) { + gddStatus rc=0; + if(isScalar()) { setPrimType(aitEnumInt8); data.Int8=d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} +inline gddStatus gdd::put(aitType* d) { + gddStatus rc=0; + if(isScalar()) { data=*d; } + else { rc=gddErrorNotAllowed; gddAutoPrint("gdd:put()",rc); } + return rc; +} + +// ------------------get(scalar) functions----------------- +inline void gdd::get(aitFloat64& d) const { + if(primitiveType()==aitEnumFloat64) d=getData().Float64; + else get(aitEnumFloat64,&d); +} +inline void gdd::get(aitFloat32& d) const { + if(primitiveType()==aitEnumFloat32) d=getData().Float32; + else get(aitEnumFloat32,&d); +} +inline void gdd::get(aitUint32& d) const { + if(primitiveType()==aitEnumUint32) d=getData().Uint32; + else get(aitEnumUint32,&d); +} +inline void gdd::get(aitInt32& d) const { + if(primitiveType()==aitEnumInt32) d=getData().Int32; + else get(aitEnumInt32,&d); +} +inline void gdd::get(aitUint16& d) const { + if(primitiveType()==aitEnumUint16) d=getData().Uint16; + else get(aitEnumUint16,&d); +} +inline void gdd::get(aitInt16& d) const { + if(primitiveType()==aitEnumInt16) d=getData().Int16; + else get(aitEnumInt16,&d); +} +inline void gdd::get(aitUint8& d) const { + if(primitiveType()==aitEnumUint8) d=getData().Uint8; + else get(aitEnumUint8,&d); +} +inline void gdd::get(aitInt8& d) const { + if(primitiveType()==aitEnumInt8) d=getData().Int8; + else get(aitEnumInt8,&d); +} +inline void gdd::get(aitType& d) const { d=data; } + +// ---------- gdd x = primitive data type pointer functions---------- +inline gdd& gdd::operator=(aitFloat64* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitFloat32* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitUint32* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitInt32* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitUint16* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitInt16* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitUint8* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitInt8* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitString* v) { putRef(v); return *this;} +inline gdd& gdd::operator=(aitFixedString* v){ putRef(v); return *this;} + +// ----------------- gdd x = primitive data type functions---------------- +inline gdd& gdd::operator=(aitFloat64 d) + { setPrimType(aitEnumFloat64); data.Float64=d; return *this; } +inline gdd& gdd::operator=(aitFloat32 d) + { setPrimType(aitEnumFloat32); data.Float32=d;return *this; } +inline gdd& gdd::operator=(aitUint32 d) + { setPrimType(aitEnumUint32); data.Uint32=d; return *this; } +inline gdd& gdd::operator=(aitInt32 d) + { setPrimType(aitEnumInt32); data.Int32=d; return *this; } +inline gdd& gdd::operator=(aitUint16 d) + { setPrimType(aitEnumUint16); data.Uint16=d; return *this; } +inline gdd& gdd::operator=(aitInt16 d) + { setPrimType(aitEnumInt16); data.Int16=d; return *this; } +inline gdd& gdd::operator=(aitUint8 d) + { setPrimType(aitEnumUint8); data.Uint8=d; return *this; } +inline gdd& gdd::operator=(aitInt8 d) + { setPrimType(aitEnumInt8); data.Int8=d; return *this; } +inline gdd& gdd::operator=(const aitString& d) + { put(d); return *this; } + +// ------------- primitive type pointer = gdd x functions -------------- + +inline gdd::operator aitFloat64*(void) + { return (aitFloat64*)dataPointer(); } +inline gdd::operator aitFloat32*(void) + { return (aitFloat32*)dataPointer(); } +inline gdd::operator aitUint32*(void) + { return (aitUint32*)dataPointer(); } +inline gdd::operator aitInt32*(void) + { return (aitInt32*)dataPointer(); } +inline gdd::operator aitUint16*(void) + { return (aitUint16*)dataPointer(); } +inline gdd::operator aitInt16*(void) + { return (aitInt16*)dataPointer(); } +inline gdd::operator aitUint8*(void) + { return (aitUint8*)dataPointer(); } +inline gdd::operator aitInt8*(void) + { return (aitInt8*)dataPointer(); } +inline gdd::operator aitString*(void) + { return (aitString*)dataPointer(); } +inline gdd::operator aitFixedString*(void) + { return (aitFixedString*)dataPointer(); } + +inline gdd::operator const aitFloat64*(void) const + { return (aitFloat64*)dataPointer(); } +inline gdd::operator const aitFloat32*(void) const + { return (aitFloat32*)dataPointer(); } +inline gdd::operator const aitUint32*(void) const + { return (aitUint32*)dataPointer(); } +inline gdd::operator const aitInt32*(void) const + { return (aitInt32*)dataPointer(); } +inline gdd::operator const aitUint16*(void) const + { return (aitUint16*)dataPointer(); } +inline gdd::operator const aitInt16*(void) const + { return (aitInt16*)dataPointer(); } +inline gdd::operator const aitUint8*(void) const + { return (aitUint8*)dataPointer(); } +inline gdd::operator const aitInt8*(void) const + { return (aitInt8*)dataPointer(); } +inline gdd::operator const aitString*(void) const + { return (aitString*)dataPointer(); } +inline gdd::operator const aitFixedString*(void) const + { return (aitFixedString*)dataPointer(); } + +// ------------- primitive type = gdd x functions -------------- +inline gdd::operator aitFloat64(void) const { aitFloat64 d; get(d); return d; } +inline gdd::operator aitFloat32(void) const { aitFloat32 d; get(d); return d; } +inline gdd::operator aitUint32(void) const { aitUint32 d; get(d); return d; } +inline gdd::operator aitInt32(void) const { aitInt32 d; get(d); return d; } +inline gdd::operator aitUint16(void) const { aitUint16 d; get(d); return d; } +inline gdd::operator aitInt16(void) const { aitInt16 d; get(d); return d; } +inline gdd::operator aitUint8(void) const { aitUint8 d; get(d); return d; } +inline gdd::operator aitInt8(void) const { aitInt8 d; get(d); return d; } +inline gdd::operator aitString(void) const { aitString d; get(d); return d; } + +inline gdd & gdd::operator [] (int index) +{ + assert (index>=0); + return (gdd &) *this->indexDD(index); +} + +inline const gdd & gdd::operator [] (int index) const +{ + assert (index>=0); + return *this->indexDD(index); +} + +inline const gdd* gdd::getDD(aitIndex index) const + { return indexDD(index); } + +inline gdd* gdd::getDD(aitIndex index) + { return (gdd *) indexDD(index); } + +inline gdd* gdd::getDD(aitIndex index,gddScalar*& dd) + { return (gdd*)(dd=(gddScalar*)indexDD(index)); } + +inline gdd* gdd::getDD(aitIndex index,gddArray*& dd) + { return (gdd*)(dd=(gddAtomic*)indexDD(index)); } + +inline gdd* gdd::getDD(aitIndex index,gddContainer*& dd) + { return (gdd*)(dd=(gddContainer*)indexDD(index)); } + +inline const gdd* gdd::getDD(aitIndex index, const gddScalar*& dd) const + { return (const gdd*)(dd=(const gddScalar*)indexDD(index)); } + +inline const gdd* gdd::getDD(aitIndex index, const gddArray*& dd) const + { return (const gdd*)(dd=(const gddAtomic*)indexDD(index)); } + +inline const gdd* gdd::getDD(aitIndex index, const gddContainer*& dd) const + { return (const gdd*)(dd=(const gddContainer*)indexDD(index)); } + +inline gdd & gdd::operator [] (aitIndex index) +{ + return *this->getDD(index); +} + +inline const gdd & gdd::operator [] (aitIndex index) const +{ + return *this->getDD(index); +} + +#endif diff --git a/src/gdd/gddNewDel.cc b/src/gdd/gddNewDel.cc new file mode 100644 index 000000000..7897dcaf8 --- /dev/null +++ b/src/gdd/gddNewDel.cc @@ -0,0 +1,81 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#define epicsExportSharedSymbols +#include "gddNewDel.h" +#include + +class gddCleanUpNode +{ +public: + void* buffer; + gddCleanUpNode* next; +}; + +class gddCleanUp +{ +public: + gddCleanUp(); + ~gddCleanUp(); + void Add(void*); +private: + gddCleanUpNode * bufs; + epicsMutex lock; +}; + +static gddCleanUp * pBufferCleanUpGDD = NULL; + +static epicsThreadOnceId gddCleanupOnce = EPICS_THREAD_ONCE_INIT; + +extern "C" { +static void gddCleanupInit ( void * ) +{ + pBufferCleanUpGDD = new gddCleanUp; + assert ( pBufferCleanUpGDD ); +} +} + +void gddGlobalCleanupAdd ( void * pBuf ) +{ + epicsThreadOnce ( & gddCleanupOnce, gddCleanupInit, 0 ); + pBufferCleanUpGDD->Add ( pBuf ); +} + +gddCleanUp::gddCleanUp() : bufs ( NULL ) {} + +gddCleanUp::~gddCleanUp() +{ + gddCleanUpNode *p1,*p2; + + for(p1=gddCleanUp::bufs;p1;) + { + p2=p1; + p1=p1->next; + free((char*)p2->buffer); + delete p2; + } +} + +void gddCleanUp::Add(void* v) +{ + gddCleanUpNode* p = new gddCleanUpNode; + p->buffer=v; + { + epicsGuard < epicsMutex > guard ( lock ); + p->next=gddCleanUp::bufs; + gddCleanUp::bufs=p; + } +} + diff --git a/src/gdd/gddNewDel.h b/src/gdd/gddNewDel.h new file mode 100644 index 000000000..2c99b2f6d --- /dev/null +++ b/src/gdd/gddNewDel.h @@ -0,0 +1,121 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_NEWDEL_H +#define GDD_NEWDEL_H + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +// this file if formatted with tab stop = 4 + +#include +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsThread.h" + +// Avoid using templates at the cost of very poor readability. +// This forces the user to have a static data member named "gddNewDel_freelist" + +// To use this stuff: +// +// ** In class description header file: +// class myClass +// { +// public: +// gdd_NEWDEL_FUNC(address_to_be_used_for_freelist_next_pointer) +// private: +// gdd_NEWDEL_DATA(myClass) +// }; +// +// ** In source file where functions for class are written: +// gdd_NEWDEL_STAT(myClass) +// gdd_NEWDEL_DEL(myClass) +// gdd_NEWDEL_NEW(myClass) + +#define gdd_CHUNK_NUM 20 +#define gdd_CHUNK(mine) (gdd_CHUNK_NUM*sizeof(mine)) + +void gddGlobalCleanupAdd ( void * pBuf ); + +// private data to add to a class +#define gdd_NEWDEL_DATA \ + static char* newdel_freelist; \ + static epicsMutex *pNewdel_lock; \ + static epicsThreadOnceId once; + +// public interface for the new/delete stuff +// user gives this macro the address they want to use for the next pointer +#define gdd_NEWDEL_FUNC(fld) \ + void* operator new(size_t); \ + void operator delete(void*); \ + char* newdel_next(void) { char* pfld = (char *)&fld; \ + char** x = (char**)pfld; return *x; } \ + void newdel_setNext(char* n) { char* pfld = (char *)&fld; \ + char** x=(char**)pfld; *x=n; } \ + static void gddNewDelInit (void) { pNewdel_lock = new epicsMutex; } + + +// declaration of the static variable for the free list +#define gdd_NEWDEL_STAT(clas) \ + char* clas::newdel_freelist=NULL; \ + epicsMutex * clas::pNewdel_lock = NULL; \ + epicsThreadOnceId clas::once = EPICS_THREAD_ONCE_INIT; + +// code for the delete function +#define gdd_NEWDEL_DEL(clas) \ + void clas::operator delete(void* v) { \ + clas* dn = (clas*)v; \ + if(dn->newdel_next()==(char*)(-1)) free((char*)v); \ + else { \ + epicsGuard < epicsMutex > guard ( *clas::pNewdel_lock ); \ + dn->newdel_setNext(clas::newdel_freelist); \ + clas::newdel_freelist=(char*)dn; \ + } \ + } + +// following function assumes that reading/writing address is atomic + +// code for the new function +#define gdd_NEWDEL_NEW(clas) \ + extern "C" { void clas##_gddNewDelInit ( void * ) { \ + clas::gddNewDelInit(); } } \ + void* clas::operator new(size_t size) { \ + int tot; \ + clas *nn,*dn; \ + epicsThreadOnce ( &once, clas##_gddNewDelInit, 0 ); \ + epicsGuard < epicsMutex > guard ( *clas::pNewdel_lock ); \ + if(!clas::newdel_freelist) { \ + tot=gdd_CHUNK_NUM; \ + nn=(clas*)malloc(gdd_CHUNK(clas)); \ + gddGlobalCleanupAdd (nn); \ + for(dn=nn;--tot;dn++) dn->newdel_setNext((char*)(dn+1)); \ + (dn)->newdel_setNext(clas::newdel_freelist); \ + clas::newdel_freelist=(char*)nn; \ + } \ + if(size==sizeof(clas)) { \ + { \ + dn=(clas*)clas::newdel_freelist; \ + clas::newdel_freelist=((clas*)clas::newdel_freelist)->newdel_next(); \ + } \ + dn->newdel_setNext(NULL); \ + } else { \ + dn=(clas*)malloc(size); \ + dn->newdel_setNext((char*)(-1)); \ + } \ + return (void*)dn; \ + } + +#endif + diff --git a/src/gdd/gddScalar.h b/src/gdd/gddScalar.h new file mode 100644 index 000000000..e735a04cc --- /dev/null +++ b/src/gdd/gddScalar.h @@ -0,0 +1,52 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_SCALAR_H +#define GDD_SCALAR_H + +/* + * Author: Jim Kowalkowski + * Date: 3/97 + * + */ + +#include "shareLib.h" + +// ------------------------------------------------------------------------ +// Add handling of the special case where the data is a scaler - the +// dimension is zero + +class epicsShareClass gddScalar : public gdd +{ +public: + gddScalar(void); + gddScalar(gddScalar* ad); + gddScalar(int app); + gddScalar(int app,aitEnum prim); + + void dump(void) const; + void test(void); + + gddScalar& operator=(aitFloat64 d); + gddScalar& operator=(aitFloat32 d); + gddScalar& operator=(aitUint32 d); + gddScalar& operator=(aitInt32 d); + gddScalar& operator=(aitUint16 d); + gddScalar& operator=(aitInt16 d); + gddScalar& operator=(aitUint8 d); + gddScalar& operator=(aitInt8 d); + +protected: + gddScalar(int app, aitEnum prim, int dimen, aitUint32* size_array); + ~gddScalar(void); + +private: +}; + +#endif diff --git a/src/gdd/gddScalarI.h b/src/gdd/gddScalarI.h new file mode 100644 index 000000000..0f4d724b3 --- /dev/null +++ b/src/gdd/gddScalarI.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef GDD_SCALARI_H +#define GDD_SCALARI_H + +inline gddScalar::gddScalar(void) { } +inline gddScalar::gddScalar(gddScalar* ad) : gdd(ad) { } +inline gddScalar::gddScalar(int app) : gdd(app) { } +inline gddScalar::gddScalar(int app,aitEnum prim) : gdd(app,prim) { } + +inline gddScalar& gddScalar::operator=(aitFloat64 d) { *((gdd*)this)=d; return *this; } +inline gddScalar& gddScalar::operator=(aitFloat32 d) { *((gdd*)this)=d; return *this; } +inline gddScalar& gddScalar::operator=(aitUint32 d) { *((gdd*)this)=d; return *this; } +inline gddScalar& gddScalar::operator=(aitInt32 d) { *((gdd*)this)=d; return *this; } +inline gddScalar& gddScalar::operator=(aitUint16 d) { *((gdd*)this)=d; return *this; } +inline gddScalar& gddScalar::operator=(aitInt16 d) { *((gdd*)this)=d; return *this; } +inline gddScalar& gddScalar::operator=(aitUint8 d) { *((gdd*)this)=d; return *this; } +inline gddScalar& gddScalar::operator=(aitInt8 d) { *((gdd*)this)=d; return *this; } + +inline gddScalar::gddScalar(int app, aitEnum prim, int dimen, aitUint32* size_array): + gdd(app,prim,dimen,size_array) { } + +inline gddScalar::~gddScalar(void) { } + +#endif diff --git a/src/gdd/gddTest.cc b/src/gdd/gddTest.cc new file mode 100644 index 000000000..1c50adc79 --- /dev/null +++ b/src/gdd/gddTest.cc @@ -0,0 +1,541 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#include +#define epicsExportSharedSymbols +#include "gdd.h" + +// -----------------------test routines------------------------ + +void gdd::dump(void) const +{ + gddScalar* sdd; + gddAtomic* add; + gddContainer* cdd; + + if(isScalar()) + { + sdd=(gddScalar*)this; + sdd->dump(); + return; + } + if(isAtomic()) + { + add=(gddAtomic*)this; + add->dump(); + return; + } + if(isContainer()) + { + cdd=(gddContainer*)this; + cdd->dump(); + return; + } +} + +void gdd::dumpInfo(void) const +{ + unsigned i; + aitIndex f,c; + unsigned long sz_tot,sz_data,sz_elem; + const aitIndex max=20u; + aitIndex prt_tot; + + sz_tot = getTotalSizeBytes(); + sz_data = getDataSizeBytes(); + sz_elem = getDataSizeElements(); + + prt_tot=sz_elem>max?max:sz_elem; + + fprintf(stderr,"----------dump This=%p---------\n", this); + fprintf(stderr," dimension=%u ", dimension()); + fprintf(stderr,"app-type=%u ", applicationType()); + + if(isScalar()) fprintf(stderr,"Scalar\n"); + if(isAtomic()) fprintf(stderr,"Atomic\n"); + if(isContainer()) fprintf(stderr,"Container\n"); + + fprintf(stderr," prim-type=%s",aitName[primitiveType()]); + switch(primitiveType()) + { + case aitEnumInvalid: + fprintf(stderr,"(aitEnumInvalid)"); + break; + case aitEnumInt8: + fprintf(stderr,"(aitEnumInt8)"); + if(isScalar()) fprintf(stderr," value=0x%2.2x ",data.Int8); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitInt8* i8=(aitInt8*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumInt16: + fprintf(stderr,"(aitEnumInt16)"); + if(isScalar()) fprintf(stderr," value=%hd ",data.Int16); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitInt16* i16=(aitInt16*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumInt32: + fprintf(stderr,"(aitEnumInt32)"); + if(isScalar()) fprintf(stderr," value=%d ",data.Int32); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitInt32* i32=(aitInt32*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumUint8: + fprintf(stderr,"(aitEnumUint8)"); + if(isScalar()) fprintf(stderr," value=0x%2.2x ",data.Uint8); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitUint8* ui8=(aitUint8*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumUint16: + fprintf(stderr,"(aitEnumUint16)"); + if(isScalar()) fprintf(stderr," value=%hu ",data.Uint16); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitUint16* ui16=(aitUint16*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumEnum16: + fprintf(stderr,"(aitEnumEnum16)"); + if(isScalar()) fprintf(stderr," value=%hu ",data.Enum16); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitEnum16* e16=(aitEnum16*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumUint32: + fprintf(stderr,"(aitEnumUint32)"); + if(isScalar()) fprintf(stderr," value=%u ",data.Uint32); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitUint32* ui32=(aitUint32*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumFloat32: + fprintf(stderr,"(aitEnumFloat32)"); + if(isScalar()) fprintf(stderr," value=%f ",data.Float32); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitFloat32* f32=(aitFloat32*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumFloat64: + fprintf(stderr,"(aitEnumFloat64)"); + if(isScalar()) fprintf(stderr," value=%f ",data.Float64); + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitFloat64* f64=(aitFloat64*)dataPointer(); + for(i=0;i\n"); + } + break; + case aitEnumFixedString: + fprintf(stderr,"(aitEnumFixedString)"); + if(isScalar()) + { + if(data.FString) + fprintf(stderr," value=<%s>\n",(char *)data.FString); + else + fprintf(stderr," value=\n"); + } + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitFixedString* fs=(aitFixedString*)dataPointer(); + for(i=0;i ",fs[i].fixed_string); + fprintf(stderr,">\n"); + } + break; + case aitEnumString: + fprintf(stderr,"(aitEnumString)"); + if(isScalar()) + { + aitString* str = (aitString*)dataAddress(); + fprintf(stderr,"\n"); + str->dump(); + } + if(isAtomic()&&dataPointer()) + { + fprintf(stderr,"\n %d values=<\n",(int)prt_tot); + aitString* ss=(aitString*)dataPointer(); + for(i=0;i ",ss[i].string()); + fprintf(stderr,">\n"); + } + break; + case aitEnumContainer: + fprintf(stderr,"(aitEnumContainer)"); + break; + default: break; + } + + fprintf(stderr," ref-count=%d\n",ref_cnt); + fprintf(stderr," total-bytes=%ld,",sz_tot); + fprintf(stderr," data-size=%ld,",sz_data); + fprintf(stderr," element-count=%ld\n",sz_elem); + + if(!isScalar()) + { + if(destruct) + fprintf(stderr," destructor=%p\n", destruct); + else + fprintf(stderr," destructor=NULL\n"); + } + + for(i=0;igetCursor(); + for(dd=cur.first();dd;dd=cur.next()) + { + if(dd->isAtomic()) { add=(gddAtomic*)dd; add->dump(); } + if(dd->isScalar()) { sdd=(gddScalar*)dd; sdd->dump(); } + if(dd->isContainer()) { cdd=(gddContainer*)dd; cdd->dump(); } + } +} + +#ifdef NO_DUMP_TEST +void gdd::test() { } +#else +void gdd::test() +{ + aitInt32 i32[3] = { -32,4,3 }; + aitIndex bnds = 3; + gddAtomic* add = (gddAtomic*)this; + gddAtomic* dd = new gddAtomic(98,aitEnumInt32,1,3); + + reset(aitEnumInt32,1,&bnds); + add->put(i32); + fprintf(stderr,"----TESTING DD DUMP:\n"); + add->dump(); + + // test copy(), copyInfo(), Dup() + fprintf(stderr,"----TESTING COPYINFO(): (1)COPYINFO, (2)ORIGINAL\n"); + dd->copyInfo(this); dd->dump(); add->dump(); + fprintf(stderr,"----TESTING DUP(): (1)DUP, (2)ORIGINAL\n"); + dd->clear(); dd->Dup(this); dd->dump(); add->dump(); + fprintf(stderr,"----TESTING COPY(): (1)COPY, (2)ORIGINAL\n"); + dd->clear(); dd->copy(this); dd->dump(); add->dump(); + dd->unreference(); + + // test flatten functions and Convert functions + size_t sz = getTotalSizeBytes(); + aitUint8* buf = new aitUint8[sz]; + gddAtomic* pdd = (gddAtomic*)buf; + flattenWithAddress(buf,sz); + fprintf(stderr,"----TESTING FLATTENWITHADDRESS():\n"); + pdd->dump(); + fprintf(stderr,"----CONVERTADDRESSTOOFFSETS() THEN BACK AND DUMP:\n"); + pdd->convertAddressToOffsets(); + pdd->convertOffsetsToAddress(); + pdd->dump(); + pdd->unreference(); + delete buf; +} +#endif + +#ifndef NO_DUMP_TEST +class gddAtomicDestr : public gddDestructor +{ +public: + gddAtomicDestr(void) { } + void run(void*); +}; + +void gddAtomicDestr::run(void* v) +{ + fprintf(stderr,"**** gddAtomicDestr::run from gddAtomic::test %p\n",v); +} +#endif + +#ifdef NO_DUMP_TEST +void gddAtomic::test(void) { } +#else +void gddAtomic::test(void) +{ + aitFloat32 f32[6] = { 32.0f,2.0f,1.0f, 7.0f,8.0f,9.0f }; + aitFloat64 f64[6] = { 64.0,5.0,4.0, 10.0,11.0,12.0 }; + aitInt8 i8[6] = { -8,2,1, 13,14,15 }; + aitInt16 i16[6] = { -16,3,2, 16,17,18 }; + aitInt32 i32[6] = { -32,4,3, 19,20,21 }; + aitUint8 ui8[6] = { 8,5,4, 22,23,24 }; + aitUint16 ui16[6] = { 16,6,5, 25,26,27 }; + aitUint32 ui32[6] = { 32,7,6, 28,29,30 }; + aitIndex bnds[2] = { 2,3 }; + + // Rules: + // reset() clear out everything and set data pointer to NULL + // put() will auto allocate if data pointer is NULL, otherwise copy + // putRef() will clear current data pointer and set new one + + // if the data pointer is NULL when put() is called, then a + // generic gddDestructor will be created to delete the buffer + + reset(aitEnumFloat32,2,bnds); + put(f32); dump(); + putRef(f32,new gddAtomicDestr); dump(); + + reset(aitEnumFloat64,2,bnds); + put(f64); dump(); + putRef(f64,new gddAtomicDestr); dump(); + + reset(aitEnumInt8,2,bnds); + put(i8); dump(); + putRef(i8,new gddAtomicDestr); dump(); + + reset(aitEnumUint8,2,bnds); + put(ui8); dump(); + putRef(ui8,new gddAtomicDestr); dump(); + + reset(aitEnumInt16,2,bnds); + put(i16); dump(); + putRef(i16,new gddAtomicDestr); dump(); + + reset(aitEnumUint16,2,bnds); + put(ui16); dump(); + putRef(ui16,new gddAtomicDestr); dump(); + + reset(aitEnumInt32,2,bnds); + put(i32); dump(); + putRef(i32,new gddAtomicDestr); dump(); + + reset(aitEnumUint32,2,bnds); + put(ui32); dump(); + putRef(ui32,new gddAtomicDestr); dump(); +} +#endif + +#ifdef NO_DUMP_TEST +void gddScalar::test(void) { } +#else +void gddScalar::test(void) +{ + int i; + aitFloat32 fa32,f32 = 32.0f; + aitFloat64 fa64,f64 = 64.0; + aitInt8 ia8,i8 = -8; + aitInt16 ia16,i16 = -16; + aitInt32 ia32,i32 = -32; + aitUint8 uia8,ui8 = 8; + aitUint16 uia16,ui16 = 16; + aitUint32 uia32,ui32 = 32; + + // printf("get float32 = %f\n",fa32); + // printf("op= float32 = %f\n",fa32); + + fprintf(stderr,"float32===="); + for(i=0;ireference(); add1->reference(); sdd2->reference(); + insert(sdd1); insert(sdd2); insert(add1); dump(); + + fprintf(stderr,"=====TESTING CURSOR:\n"); + gddCursor cur = getCursor(); + gdd* dd; + int i; + for(i=0; (dd=cur[i]); i++) fprintf(stderr,"%p ",dd); + fprintf(stderr,"\n"); + for(dd=cur.first();dd;dd=cur.next()) fprintf(stderr,"%p ",dd); + fprintf(stderr,"\n"); + + remove(0); remove(0); remove(0); dump(); + sdd1->reference(); add1->reference(); sdd2->reference(); + insert(add1); insert(sdd1); insert(sdd2); dump(); + + sz = getTotalSizeBytes(); + buf = new aitUint8[sz]; + + fprintf(stderr,"=====TESTING FLATTEN FUNCTION BUFFER=%p:\n",buf); + flattenWithAddress(buf,sz); + cdd1=(gddContainer*)buf; + cdd1->dump(); + fprintf(stderr,"=====CHANGE ADDRESSES TO OFFSETS:\n"); + cdd1->convertAddressToOffsets(); + fprintf(stderr,"=====CHANGE OFFSETS TO ADDRESSES:\n"); + cdd1->convertOffsetsToAddress(); + fprintf(stderr,"=====RE-DUMP OF FLATTENED CONTAINER:\n"); + cdd1->dump(); + fprintf(stderr,"=====RE-DUMP OF ORIGINAL CONTAINER:\n"); + dump(); + cdd1->unreference(); + delete buf; + + // test copy(), Dup(), copyInfo() + fprintf(stderr,"=======CREATING TEST CONTAINER FOR *COPY* TEST:\n"); + cdd1 = new gddContainer; + fprintf(stderr,"=======COPYINFO():\n"); + cdd1->copyInfo(this); cdd1->dump(); + fprintf(stderr,"=======DUP():\n"); + cdd1->Dup(this); cdd1->dump(); + fprintf(stderr,"=======COPY():\n"); + cdd1->copy(this); cdd1->dump(); + fprintf(stderr,"=======UNREFERENCE THE TEST CONTAINER:\n"); + cdd1->unreference(); + + fprintf(stderr,"=====DUMPING ORIGINAL:\n"); + dump(); + clear(); + + fprintf(stderr,"=======TEST COMPLETE, DELETE STUFF:\n"); + fprintf(stderr," first scaler:\n "); sdd1->unreference(); + fprintf(stderr," first atomic:\n "); add1->unreference(); + fprintf(stderr," second scaler:\n "); sdd2->unreference(); + dump(); +} +#endif + diff --git a/src/gdd/gddUtils.cc b/src/gdd/gddUtils.cc new file mode 100644 index 000000000..dfcc8a4be --- /dev/null +++ b/src/gdd/gddUtils.cc @@ -0,0 +1,62 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// Author: Jim Kowalkowski +// Date: 3/97 +// +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +// + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "gddNewDel.h" +#include "gddUtils.h" + +gdd_NEWDEL_NEW(gddBounds1D) +gdd_NEWDEL_DEL(gddBounds1D) +gdd_NEWDEL_STAT(gddBounds1D) + +gdd_NEWDEL_NEW(gddBounds2D) +gdd_NEWDEL_DEL(gddBounds2D) +gdd_NEWDEL_STAT(gddBounds2D) + +gdd_NEWDEL_NEW(gddBounds3D) +gdd_NEWDEL_DEL(gddBounds3D) +gdd_NEWDEL_STAT(gddBounds3D) + +gdd_NEWDEL_NEW(gddDestructor) +gdd_NEWDEL_DEL(gddDestructor) +gdd_NEWDEL_STAT(gddDestructor) + +// --------------------------The gddBounds functions------------------- + +// gddBounds::gddBounds(void) { first=0; count=0; } + +// --------------------------The gddDestructor functions------------------- + +gddStatus gddDestructor::destroy(void* thing) +{ + if(ref_cnt==0 || --ref_cnt==0) + { + run(thing); + delete this; + } + return 0; +} + +void gddDestructor::run(void* thing) +{ + aitInt8* pd = (aitInt8*)thing; + delete [] pd; +} + diff --git a/src/gdd/gddUtils.h b/src/gdd/gddUtils.h new file mode 100644 index 000000000..223dc8190 --- /dev/null +++ b/src/gdd/gddUtils.h @@ -0,0 +1,150 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_UTILS_H +#define GDD_UTILS_H + +/* + * Author: Jim Kowalkowski + * Date: 2/96 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +#ifdef vxWorks +#include +#include +#include +#include +#endif + +#include "aitTypes.h" +#include "gddErrorCodes.h" +#include "gddNewDel.h" +#include "shareLib.h" + +// --------------------------------------------------------------------- +// Describe the bounds of a single dimension in terms of the first +// element and the number of elements in the dimension. + +class epicsShareClass gddBounds +{ +public: + // I found a weird memory management quirk with SunOS when constructors + // are present in a class that is used as an array in another class + // as it is in gddBounds2D. The memory manager reallocates the entire + // gddBounds2D in some sort of buffer pool which stays allocated when the + // program completes + + // gddBounds(void); + // gddBounds(aitIndex c); + // gddBounds(aitIndex f, aitIndex c); + + void setSize(aitIndex c); + void setFirst(aitIndex c); + void set(aitIndex f, aitIndex c); + void get(aitIndex& f, aitIndex& c); + aitIndex size(void) const; + aitIndex first(void) const; + +private: + aitIndex start; + aitIndex count; +}; + + +// --------------------------------------------------------------------- +// This is a class for managing the destruction of user data held within +// a DD. It allows multiple DDs to reference the same chunk of user +// data. The data would remain alive until the reference count goes to +// zero, at this time the user destructor function will be called with +// a pointer to the piece of data to deal with. The intention here is +// that user will create a subclass of this and supply the virtual +// function run(void*). The default behavior will be to treat the void* +// data as an array of aitInt8 and call remove. + +// **** WARNING! +// The reference count is initialized to zero. This means that if you +// create one and want to keep it, you must reference it twice - the +// initial time and a time for your personal use. This is because the +// gdd library automatically increments the reference count on +// destructors when data is referenced into a gdd with a destructor. +// Remember the rule: if you pass a destructor to someone, then that +// someone now owns the destructor. A reference count of 1 indicates that +// the thing will go away when unreferenced - which is what you will +// have if you only reference the destructor once after it is created. + +class epicsShareClass gddDestructor +{ +public: + gddDestructor(void); + gddDestructor(void* user_arg); + + void reference(void); + int refCount(void) const; + + gddStatus destroy(void* thing_to_remove); + virtual void run(void* thing_to_remove); + + gdd_NEWDEL_FUNC(arg) // for using generic new and remove +protected: + aitUint16 ref_cnt; + void* arg; + virtual ~gddDestructor () {} +private: + gdd_NEWDEL_DATA +}; + +#include "gddUtilsI.h" + +// --------------------------------------------------------------------- +// Special managment for 1D-2D-3D bounds - others use normal new/remove. +// Since bounds are maintained as arrays, where the length of the array is +// the dimension of the data, we will manage the normal cases using +// free lists. + +class epicsShareClass gddBounds1D +{ +public: + gddBounds1D(void) { } + gddBounds* boundArray(void); + gdd_NEWDEL_FUNC(b[0]) // required for using generic new and remove +private: + gddBounds b[1]; + gdd_NEWDEL_DATA // required for using generic new/remove +}; +inline gddBounds* gddBounds1D::boundArray(void) { return b; } + +class epicsShareClass gddBounds2D +{ +public: + gddBounds2D(void) { } + gddBounds* boundArray(void); + gdd_NEWDEL_FUNC(b[0]) // required for using generic new and remove +private: + gddBounds b[2]; + gdd_NEWDEL_DATA // required for using generic new/remove +}; +inline gddBounds* gddBounds2D::boundArray(void) { return b; } + +class epicsShareClass gddBounds3D +{ +public: + gddBounds3D(void) { } + gddBounds* boundArray(void); + gdd_NEWDEL_FUNC(b[0]) // for using generic new and remove +private: + gddBounds b[3]; + gdd_NEWDEL_DATA // required for using generic new/remove +}; +inline gddBounds* gddBounds3D::boundArray(void) { return b; } + +#endif + diff --git a/src/gdd/gddUtilsI.h b/src/gdd/gddUtilsI.h new file mode 100644 index 000000000..228a27404 --- /dev/null +++ b/src/gdd/gddUtilsI.h @@ -0,0 +1,36 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef GDD_UTILSI_H +#define GDD_UTILSI_H + +/* + * Author: Jim Kowalkowski + * Date: 3/97 + * + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + */ + +// inline gddBounds::gddBounds(aitIndex c) { start=0; count=c; } +// inline gddBounds::gddBounds(aitIndex f, aitIndex c) { start=f; count=c; } + +inline void gddBounds::setSize(aitIndex c) { count=c; } +inline void gddBounds::setFirst(aitIndex f) { start=f; } +inline void gddBounds::set(aitIndex f, aitIndex c) { start=f; count=c; } +inline void gddBounds::get(aitIndex& f, aitIndex& c){ f=start; c=count; } +inline aitIndex gddBounds::size(void) const { return count; } +inline aitIndex gddBounds::first(void) const { return start; } + +inline gddDestructor::gddDestructor(void) { ref_cnt=0; arg=NULL; } +inline gddDestructor::gddDestructor(void* usr_arg) { ref_cnt=0; arg=usr_arg; } +inline void gddDestructor::reference(void) { ref_cnt++; } // X aCC 818 +inline int gddDestructor::refCount(void) const { return ref_cnt; } + +#endif diff --git a/src/gdd/gddref.html b/src/gdd/gddref.html new file mode 100644 index 000000000..2a0bf9ba8 --- /dev/null +++ b/src/gdd/gddref.html @@ -0,0 +1,518 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +GDD Reference Manual +

    GDD Reference Manual

    + +gdd
    +gddAtomic
    +gddScalar
    +gddContainer
    +gddApplicationTypeTable
    +

    +gddDestructor
    +gddBounds
    +gddBounds1D gddBounds2D gddBounds3D
    +gddCursor
    +gddSemaphore
    +gddIntLock
    +

    +aitConvert
    +aitTimeStamp
    +aitString
    +aitTypes/aitEnums
    +

    +new/delete Free List Management
    +Application Type Code Index Labels
    +gddStatus - Error Codes
    +

    +DBR To ait Conversion Functions
    +gddMakeMapDBR
    +gddMapDbr
    +gddAitToDbr
    +gddDbrToAit
    +

    +User's Guide
    + +


    gdd

    +Main GDD library base class. A fundemental class for describing a scalar +value, an array or an array of GDDs know as a container. See the User's +Guide for a detailed explanation. + +

    #include "gdd.h"

    + +gdd(void)
    +gdd(gdd*)
    + +Construct a new gdd that is empty or that is a copy of another gdd instance. + +

    +gdd(int ApplicationType);
    +gdd(int ApplicationType, aitEnum PrimitiveType);
    +gdd(int ApplicationType, aitEnum PrimitiveType, int Dimension);
    +gdd(int ApplicationType, aitEnum PrimitiveType, int Dimension, + aitUint32* ElementCounts);
    + +Construct a new gdd, describing various data properties. If Dimension +is greater that zero, then ElementCounts is an array with Dimension +number of elements in it. Each element of the array describes how many +elements are in that dimension. The ElementCount information is used to +initialize the bounds of a GDD. + +

    +unsigned applicationType(void) const;
    +aitEnum primitiveType(void) const;
    +unsigned dimension(void) const;
    +gddStatus changeType(int AppType, aitEnum PrimType) const;
    + +Return the GDDs application type code. Return the type of data stored in the +GDD. Remember that the application type code is arbitrarily set by the user +and it imposes a user-level meaning to the data. The primitive type code +is generally a compiler supported data type with a predetermined size, such +as double, float, short, int, etc.. Return the dimension of the data in +the GDD; zero indicates a scalar. ChangeType() allows for a GDD that is +a scalar or a GDD with an undefined primitive type to be converted to +a new primitive type and application type code. Changes in this case +are safe. + +

    changeType() return codes: +
    +
    0 - Success +
    gddErrorTypeMismatch - GDD is not scalar or primitive type valid +
    +
    + +

    +gddDestructor* destructor(void) const;
    +gddStatus registerDestructor(gddDestructor*);
    +gddStatus replaceDestructor(gddDestructor*);
    + +Methods for manipulating the user-defined GDD destructor. Destructor() +just returns a reference to the GDDs destructor. RegisterDestructor() +installs a destructor in the GDD if one is not already present. +ReplaceDestructor() forces a GDDs destructor to be replaced. + +

    registerDestructor() return codes: +
    +
    0 - Success +
    gddErrorAlreadyDefined - gddDestructor already registered in GDD +
    +
    + +

    +const gddBounds* getBounds(void) const;
    +const gddBounds* getBounds(int dimension) const;
    +gddStatus getBound(unsigned dim_to_get, aitIndex& first, aitIndex& count);
    +gddStatus setBound(unsigned dim_to_set, aitIndex first, aitIndex count);
    + +Retrieve the bounds array from the GDD or get a gddBounds for a +particular dimension with the first two methods listed. Get and set +information contained within the bounds structure of a GDD. Dimension +bounds in the GDD library are always expressed as (first,count), see the +User's guide for details. + +

    getBound() and setBound return codes: +
    +
    0 - Success +
    gddErrorOutOfBounds - requested dimension does not exist +
    +
    + +

    +void dump(void);
    + +Print out all the information contained in the fields of a GDD in a +useful,readable fashion. Good tool for learning about the information +a GDD holds. + +

    +void setPrimType(aitEnum PrimitiveEnumType);
    +void setApplType(int ApplicationTypeCode);
    + +Force the GDD to change the primitive type of the data it describes. +Changing the primitive type code is generally an unnatural thing to do. +Force a GDD to change the application type, which effectively changes +the high-level meaning of the data held within the GDD. + +

    +void setDimension(int);
    + +Describe the data a GDD hold in terms of a different dimension. This call +does not alter the data, it does however deallocate bounds and reallocate +them to match the new dimension. This call can really cause probably when +dealing with packed or flattened GDDs. This operation is not advisable +is simple applications, users are better off deleting the GDD and +creating one that suites the data in mosts instances. + +

    +void* dataAddress(void) const;
    +void* dataPointer(void) const;
    +void* dataPointer(aitIndx element_offset) const;
    + +All of these methods are designed to give the user general access to +the data within a GDD. These calls are meant to be used internally and +by specialized libraries. The last method listed will give the starting +address of a portion of a simple single dimensional array given an +element offset. + +

    +void destroyData(void);
    + +If the GDD is describing an array and a destructor is present, then run +the destructor. If the GDD is a container, then run the destructor +giving the argument to the run() function the address of the container +rather then the data that the GDD holds. + +

    +gddStatus reset(aitEnum primtype,int dimension, aitIndex* dim_counts);
    +gddStatus clear(void);
    +gddStatus clearData(void);
    + +The clear() method completely clears out a GDD, this includes removing +bounds information, running destructors, and resetting every field in a +GDD to invalid. The clearData() method keeps the GDD information intact, +but runs the data destructors and clears out the data field. Reset() +first runs clear() on the GDD, and then resets the primitive type, dimension, +and bounds information, the appliacation type code remains the same. + +

    clear() return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if GDD is flattened or managed +
    +
    +
    clearData() return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if GDD is a container +
    +
    +
    reset() return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if GDD is flattened, managed, or a container +
    +
    + +

    +void getTimeStamp(struct timespec* const ts) const;
    +void getTimeStamp(aitTimeStamp* const ts) const;
    +void setTimeStamp(const struct timespec* const ts);
    +void setTimeStamp(const aitTimeStamp* const ts);
    + +Manipulate the time stamp field of the GDD using an aitTimeStamp structure +or a standard struct timespec. + +

    +void setStatus(aitUint32);
    +void getStatus(aitUint32&);
    + +Use the status field of the GDD is a general fashion. The status field +is assigned a value arbitrarily by the user. + +

    +void setStat(aitUint16);
    +void setSevr(aitUint16);
    +aitUint16 getStat(void) const;
    +aitUint16 getSevr(void) const;
    +void setStatSevr(aitInt16 stat, aitInt16 sevr);
    +void getStatSevr(aitInt16& stat, aitInt16& sevr);
    + +Manipulate the status field of a GDD as a combination 16-bit status +and 16-bit severity field. + +

    size_t getTotalSizeBytes(void) const;
    + +Return the total size in bytes of the entire GDD including all the data +it references and bounds array. This is the length of the GDD and all +the items it references. + +

    size_t getDataSizeBytes(void) const;
    +Return the total size in bytes of the data that the GDD contains or references. + +

    aitUint32 getDataSizeElements(void) const;
    +Return the number of elements that the GDD describes. + +

    gddStatus copyData(const gdd* dd);
    +Copy the data from gdd dd to this gdd. This function resets the bounds, the +dimension, the primitive type and the application. In addition, it +allocates a buffer large enough to hold all the data from dd and copies +the data from dd to the new buffer and references it into this gdd. A new +standard gddDestructor is allocated and registered into this GDD that +will be used to delete the newly created data buffer. +

    return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if this GDD is flattened, managed, or a container +
    +
    + +

    gddStatus copyInfo(gdd* dd);
    +Copy the data description fields from dd to this gdd. The primitive type, +application type, dimension, and bounds are copied. This gdd is cleared +completely before the information from dd is copied. If dd is a container, +then this gdd becomes a container which looks just like dd without any +data. +

    return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if this GDD is flattened or managed +
    +
    + +

    +gddStatus copy(gdd* dd);
    +gddStatus dup(gdd* dd);
    +Copy() does everything that copyInfo() does, but also copies the data just as +copyData() would. Dup() does everything that copyInfo() does, but references +the data that dd contains. If dd has a user registered destructor, the this +gdd registers the same destructor instance and bumps up the destructor +reference count. +

    return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if this GDD is flattened or managed +
    +
    + +

    +size_t flattenWithAddress(void* buf, size_t buf_size, + aitIndex* total_dds);
    +size_t flattenWithOffsets(void* buf, size_t buf_size, + aitIndex* total_dds);
    +These functions pack this gdd instance into buffer buf. Both functions +return the amount of space in bytes used by this gdd. The function will +not go beyond buf_size. The parameter total_dds will be modified to +reflect the total gdds packed into buf. Flattened GDDs can be +directly indexed, the total_dds variable indicates the number of elements +in the gdd array starting at buf. This function can pack scalar GDDs, +atomic GDDs, or container GDDs recursively. The organization of the +buffer is as follows: All GDDs are first, all bounds information follows, +and array data for all the GDDs follow the bounds. The flattenWithAddress() +method is the standard way to produce a useable, packed GDD. The method +flattenWithOffsets() puts offsets in where address normally go in GDD +fields. This function allows GDD to be transfered from one machine or +address space to another when used in conjunction with +convertOffsetsToAddress() and convertAddressToOffsets(). + +

    +gddStatus convertAddressToOffsets(void);
    +gddStatus convertOffsetsToAddress(void);
    +These functions run through this gdd and convert all addressing information +from offsets (from zero) to real addresses or real addresses to offsets. +These function work recursively with container type GDDs. The function +convertAddressToOffsets() can only work with a GDD that is flattened. +

    convertAddressToOffsets() return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if this GDD is not flattened +
    +
    + +

    +int isScalar(void) const;
    +int isAtomic(void) const;
    +int isContainer(void) const;
    +Each return true to indicate the type of GDD that this is. + +

    +int isManaged(void) const;
    +void markManaged(void);
    +void markUnmanaged(void);
    +Manipulate the GDD managed state flag. A managed GDD is managed by a +higher level facility such as the application type table. Managed GDDs +have special properties, such as a destructor that knows how to place the +GDD back on a free list buffer pool. All GDDs retrieved from the +application type table that are associated with a prototype will be +managed. Certain copy operations are not allows on managed GDDs. See +the User's Guide for further details. + +

    int isFlat(void) const;
    +Discover if this GDD has been flattened. + +

    +int isLocalDataFormat(void) const;
    +int isNetworkDataFormat(void) const;
    +void markLocalDataFormat(void);
    +Indicate that data stored in the GDD is in a local byte order or floating +point format. Discover if data in the GDD is in local data format. + +

    +int isConstant(void) const;
    +void markConstant(void);
    +Control the constant state flag of this GDD. The data in a constant GDD +cannot be changed. + +

    +int isNoRef(void) const;
    +gddStatus noReferencing(void);
    +gddStatus reference(void);
    +gddStatus unreference(void);
    +Control and manipulate the reference counts of a GDD. Reference() bumps +up the GDD reference count. Unreference() decrements the reference count of +this GDD, if the reference count drops to zero, then the GDD is deleted. +NoReferencing() locks the GDD reference count and disallows further +referencing of this GDD. +

    noReferencing() return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if this GDD has reference count > 1 already +
    +
    +
    reference() return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - if referencing is not allowed on this GDD +
    +
    +
    unreference() return codes: +
    +
    0 - Success +
    gddErrorUnderflow - if reference count is zero before decrement +
    +
    + +

    gddStatus genCopy(aitEnum dtype, const void* buffer);
    +Generalized copy routine that takes data from buffer of type dtype and +puts it into this gdd. If this gdd is a scalar, then one element from +buffer is converted to the primitive type of the GDD and copied in. If +this GDD is an array, then the number of elements copied is determined by +the bounds information stored in this GDD. Is this GDD described an array, +but has no data buffer associated with it, then a new buffer is +allocated which is the size the bounds describes, and the data from +parameter buffer is converted to the GDD primitive type and put into +the newly created GDD buffer. Not a commonly used function. +

    return codes: +
    +
    0 - Success +
    gddErrorTypeMismatch - If this GDD is a container +
    +
    + +

    +void adjust(gddDestructor* dest, void* dbuf, aitEnum dtype);
    +This function runs the destructor in this GDD if present, effectively +destroying the data is this GDD. The parameter dest becomes the new +destructor, dbuf becomes the GDD's referenced data buffer, and dtype +becomes the new primitive type for this GDD. Not a commonly used function. + +

    +void set(aitEnum dtype, void* dbuf);
    +void get(aitEnum dtype, void* dbuf);
    +Generalized functions for copying the first element of this GDD into and out +from dbuf. Set() takes one element pointed to by dbuf of type dtype and +converts it to this GDD primitive type and copies it to this GDDs data field. +Get() takes one element of this GDD's data field and convert it to dtype and +copies it into dbuf. Not a commonly used function. + +

    void getRef(ait_xxxxx*& dbuf);
    +Standard way to get access to the data buffer an array-type GDD references. +There is one of these methods for each of the supported ait types. The +dbuf parameter is modified to point to the GDD's data buffer. There is a +danger here. Data pointers in a GDD are stored as void*. The primitive +type indicates the true type of data. These methods can be used to +cast the data pointer to any of the ait types, completely ingoring the +primitive type code. The idea here is that the user will dicover the +correct primitive type and call the correct method. + +

    +void putRef(ait_xxxxx* dbuf, gddDestructor dest=NULL);
    +void putRef(const ait_xxxxx* dbuf, gddDestructor dest=NULL);
    +void putRef(void* dbuf,aitEnum code,gddDestructor dest=NULL);
    +Standard way to reference data into a GDD and optionally supply a destructor. +These methods are designed to work with array or atomic type GDDs. +There is one of these methods for each of the support ait types. All these +methods invoke adjust() to do the work with the correct aitEnum type code. +See adjust() for further details. The second method listed marks the GDD +state as constant. + +

    void getConvert(ait_xxxxx& dbuf);
    +Standard way to retreive scalar data from this GDD. There is one of these +methods for each of the ait types. The data in the GDD will be converted +from it's primitive type to the ait_xxxxx data type and placed into dbuf. +These methods invoke "void get(aitEnum dtype, void* dbuf)". + +

    void putConvert(ait_xxxxx dbuf);
    +Standard way to place a scalar value into a GDD. There is one of these +methods for each of the ait types. The data will be converted from +ait_xxxxx to the GDD's primitive type and placed into the data field. +These methods invoke set(). + +

    gddStatus put(const ait_xxxxx* const dbuf);
    +Standard way to copy data from dbuf of type ait_xxxxx into this GDD's buffer. +There is one of these methods for each of the ait types. +These methods invoke genCopy() with proper arguments, see genCopy() for +further details. The return codes of these methods are the same as +genCopy(). + +

    gddStatus put(ait_xxxxx dbuf);
    +Standard way to place a scalar value into this scalar GDD and reset the +There is one of these methods for each of the ait types. +primtive type to match ait_xxxxx. No conversions take place in these +methods, they are meant to adjust the primitive type. +

    return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - If this GDD is not a scalar +
    +
    + +

    gddStatus put(const gdd* dbuf);
    +Put the data from dbuf into this GDD. Resets all information in this GDD +to match dbuf. +

    return codes: +
    +
    0 - Success +
    gddErrorNotSupported - If this GDD or dbuf is a container. Also if + dbuf has dimension greater than one. +
    gddErrorOutOfBounds - Attempting to copying more dbuf elements + into this GDD then this GDD can hold. +
    gddErrorNotAllowed - If this GDD has no allocated data but has a + destructor registered. +
    +
    + +

    +void get(ait_xxxxx* dbuf);
    +void get(void* dbuf, aitEnum dtype);
    +Standard way to retrieve array data from this GDD into dbuf with +primitive type conversion. +There is one of these methods for each of the ait types. +The amount of data copied out of this GDD depends on the primitive type +(for size of element information), and the dimension/bounds for the +total element count. These methods will convert the data from the GDDs +primitive type to the ait_xxxxx type as the copy is taking place. +Depending on the conversion, the size required by dbuf may be more or +less then the size of this GDD's data buffer. + +

    void get(ait_xxxxx& dbuf);
    +Standard way to get scalar data out of this GDD and perform data conversions +from this GDD primitive type to ait_xxxxx. +There is one of these methods for each of the ait types. +These methods simply invoke "void get(aitEnum dtype, void* dbuf)", see this +method for further details. + +

    gdd& operator=(ait_xxxxx* v);
    +Convenience methods so the equal operator can be used. Same as putRef(...) +methods. + +

    gdd& operator=(ait_xxxxx v);
    +Convenience methods so the equal operator can be used. Same as put(...) +methods. + +

    operator ait_xxxxx*(void) const;
    +

    operator ait_xxxxx(void) const;
    +Convenience methods so casting can be done from this GDD to any supported +ait_xxxxx pointer variable. Same as getRef(...) and get(...) methods. + +


    Argonne National Laboratory +Copyright +Information
    +
    Jim Kowalkowski (jbk@aps.anl.gov)
    updated 4/12/95 + diff --git a/src/gdd/gddref2.html b/src/gdd/gddref2.html new file mode 100644 index 000000000..672dfebc7 --- /dev/null +++ b/src/gdd/gddref2.html @@ -0,0 +1,665 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +GDD Reference Manual +

    GDD Reference Manual

    + +gdd
    +gddAtomic
    +gddScalar
    +gddContainer
    +gddApplicationTypeTable
    +

    +gddDestructor
    +gddBounds
    +gddBounds1D gddBounds2D gddBounds3D
    +gddCursor
    +gddSemaphore
    +gddIntLock
    +

    +aitConvert
    +aitTimeStamp
    +aitString
    +aitTypes/aitEnums
    +

    +new/delete Free List Management
    +Application Type Code Index Labels
    +gddStatus - Error Codes
    +

    +DBR To ait Conversion Functions
    +gddMakeMapDBR
    +gddMapDbr
    +gddAitToDbr
    +gddDbrToAit
    +

    +User's Guide
    + +


    gddAtomic

    +Specialized subclass of gdd that provides conveniences ways to create +and manage array-type GDDs. + +

    #include "gdd.h"

    + +gddAtomic(void)
    +gddAtomic(gddAtomic* dd)
    + +Construct a new gdd that is empty or that is a copy of another gddAtomic +instance. + +

    +gddAtomic(int ApplicationType);
    +gddAtomic(int ApplicationType, aitEnum PrimitiveType);
    +gdd(int ApplicationType, aitEnum PrimitiveType, int Dimension, + aitUint32* ElementCounts);
    +gddAtomic(int ApplicationType, aitEnum PrimitiveType, int Dimension, ...); +
    + +Construct a new gdd, describing various data properties. If Dimension +is greater that zero, then ElementCounts is an array with Dimension +number of elements in it. Each element of the array describes how many +elements are in that dimension. The ElementCount information is used to +initialize the bounds of a GDD. The last constructor takes a variable +number of arguments which are the upper bounds for each dimension; the +number of variable arguments must be equal to the Dimension argument. + +

    +gddStatus getBoundingBoxSize(aitUint32* buf);
    +gddStatus getBoundingBoxOrigin(aitUint32* buf);
    +gddStatus setBoundingBoxSize(const aitUint32* const buf);
    +gddStatus setBoundingBoxOrigin(const aitUint32* const buf);
    +Convenience routines for setting bounds information using bounding box +information. The number of elements of buf must be equal to the dimension +defined in the GDD. These methods can be used to set the bounds information +within a GDD in a convenience way by specifying the origin and size. +Bounds in GDDs are descibed (origin,element_count) for each dimension. These +methods provide an alternative method to set all the origins at once or +all the element_counts at once. +

    changeType() return codes: +
    +
    0 - Success +
    gddErrorOutOfBounds - If this GDD has dimension zero +
    +
    + +

    gddScalar

    +Specialized subclass of gddAtomic that provides convenient ways to create +and manage scalar-type GDDs. + +

    #include "gdd.h"

    + +gddAtomic(void)
    +gddAtomic(gddAtomic* dd)
    + +Construct a new gddScalar that is empty or that is a copy of another gddScalar +instance. + +

    +gddAtomic(int ApplicationType);
    +gddAtomic(int ApplicationType, aitEnum PrimitiveType);
    + +Construct a new gddScalar, describing various data properties. Dimension is +a gddScalar is always zero. + +


    gddContainer

    +Specialized subclass of gdd that provides convenient ways to create +and manage container-type GDDs. + +

    #include "gdd.h"

    + +gddContainer(void)
    +gddContainer(gddContainer* dd)
    + +Construct a new gddContainer that is empty or that is a copy of another +gddContainer instance. A gddContainer always has dimension one and a +single bounds structure allocated to tell how many GDDs are in the container. + +

    +gddContainer(int ApplicationType);
    +gddContainer(int ApplicationType, int total_gdds);
    + +Construct a new gddContainer, describing the application type. The argument +total_gdds sets the number GDDs contained within the gddContainer in +the element count of this GDDs bound structure. The last constructor listed +also creates total_gdds GDDs, puts them together in a linked list and +sets the data field of this GDD to point to the linked list. The last +constructor creates the container with total_gdds empty GDDs in it. + +

    +gddStatus insert(gdd* dd);
    +gddStatus remove(aitIndex index);
    +Insert dd to the container. Care must be taken not to use the insert method +on a flattened container. Remove GDD number index from the container. The +GDD at index is removed from the container and unreferenced. +

    remove() return codes: +
    +
    0 - Success +
    gddErrorOutOfBounds - If GDD at index does not exist. +
    +
    + +

    +gdd* operator[](aitIndex index);
    +gdd* getDD(aitIndex index);
    +gdd* getDD(aitIndex index, gddScalar*&);
    +gdd* getDD(aitIndex index, gddAtomic*&);
    +gdd* getDD(aitIndex index, gddContainer*&);
    +Return the GDD at index. + +

    +gddCursor getCursor(void) const;
    +Get a simple linked list iterator which is useful for manipulating containers. +See gddCursor explanation. + +


    gddCursor

    +A simple class for moving forward through the GDDs stored in a container. + +

    #include "gdd.h"

    + +gddCursor(const gddContainer* c)
    +Construct a new gddCursor that can be used to walk through gddContainer c. + +

    gddStatus first(.....);
    +Set this iterator to the first element of the container. Returns the +first GDD in the container. + +

    gddStatus next(.....);
    +Set this iterator to the next element of the container. Returns the +next GDD in the container. + +

    gddStatus current(.....);
    +Returns the current GDD that the cursor points to within the container. + +


    gddApplicationTypeTable

    +A database for registering application type names and retrieving application +type codes. The database also manages GDD prototype structures associated +with application type names. Methods exist for allocating and freeing GDDs +given a type code. See User's Guide for details. + +

    #include "gddAppTable.h"

    + +gddApplicationTypeTable(aitUint32 total)
    +Construct an application type table capable of holding total amount +of application type codes. The user does not need to create one of these, +the library automatically generates one instance of this class large enough +to hold 512 type codes. This automatically generated instance registers +all the standard EPICS control system type codes, see User's Guide. + +

    +gddStatus registerApplicationType(const char* const name, aitUint32& app);
    +gddStatus registerApplicationTypeWithProto(const char* const name, + gdd* protoDD, aitUint32& app);
    +
    +Register an application type name. The type table will return the +application type code in app that the system has assigned to the name. +The latter function allow a prototype GDD to be registered aloong with +the name. This prototype is describes the structure of the data associated +with the application type name. See User's Guide for more details. +

    return codes: +
    +
    0 - Success +
    gddErrorAlreadyDefined - If name is already refined, app + still returns the type code assigned to the name. +
    gddErrorAtLimit - The type table is filled, no more names can be + stored in it. +
    +
    + +

    +aitUint32 getApplicationType(const char* const name) const;
    +char* getName(aitUint32 app) const;
    +
    +Convert a character string type name to it's integer value. Convert an +integer value to it's character string name. + +

    gddStatus mapAppToIndex(aitUint32 container_app, + aitUint32 app_to_map, aitUint32& index);
    +Special function for converting type codes to container GDD array indexes. +The GDD must be a flattened GDD managed by the application type table for +mapping to take place. The container_app argument is the main +structure that contains app_to_map. The index is populated +with the position where app_to_map is within container_app. +See User's Guide for details. +

    return codes: +
    +
    0 - Success +
    gddErrorOutOfBounds - app_to_map cannot be mapped +
    gddErrorNotDefined - container_app does not exist or app_to_map not + in container_app +
    +
    + +

    gddStatus smartCopy(gdd* dest, gdd* src);
    +Given any GDD dest and any GDD src, copy all the data from src to dest +where application type codes match. The src and dest can be any type of +GDD: scalar, array, or container. The src dset do not need to be the +same type of GDD. If dest and src are both containers. The function will +look through each of the src GDDs to find matches for each of the dest GDDs. +SmartCopy() may result in no data being copied if none of the src type codes +match the dest type codes. The dest and src container GDDs do not need to +be in the same order. +

    return codes: +
    +
    0 - Success +
    gddErrorNotAllowed - src or dest is container and is not managed +
    other - see put(const gdd*) +
    +
    + +

    gdd* getDD(aitUint32 app);
    +Allocate a GDD for the app and return it. If app has a prototype +registered with it, then the returned gdd will be managed and match the +prototype. + +

    gddStatus freeDD(gdd* dd);
    +Free any gdd. The method basically just calls unreference on dd. If the +GDD is managed, then it puts it onto a free list for it's application type +code. + +

    +aitUint32 maxAttributes(void) const;
    +aitUint32 totalregistered(void) const;
    +
    +Return the total number of names registered in this application type table. +Return the total number of names that can be registered in this application +type table. + +

    void describe(FILE* fd);
    +Print out "#define" statements for each of the names registered in this +table. Also print out container index labels in the form of #defines. See +User's Guide for details. The file gddApps.h is generated using this +function. + +

    static gddApplicationTypeTable& AppTable(void);
    +Return the automatically generated application type table for use in a +program. + +


    gddDestructor

    +The gdd class knows how to run and manage gddDestructors. User's assign +destructors to gdd by deriving classes from gddDestructor. + +

    #include "gdd.h"

    + + +gddDestructor(void);
    +gddDestructor(void* user_arg);
    +
    +Create a gddDestructor, optionally placing an arbitrary pointer in the +destructor which can be referenced when the destructor is actual run. + +

    +gddStatus destroy(void* thing_to_remove);
    +virtual void run(void* thing_to_remove);
    +
    +The destroy method in invoked by anyone withing to request the destructor +to be run. Destroy invokes the user-written function run when +it's reference count goes to zero. Run should not be invoked +directly by the user. The argument to destroy is the buffer that +needs to be freed by by the destructor. The gdd class invokes destroy +when unreference is called and the gdd's reference count goes to zero. The +default behavior of run is to cast the buffer thing_to_remove to a +character array and delete it. + +

    +void reference(void);
    +int refCount(void) const;
    +
    +Calling refCount will return the current value in the destructor +reference count. Reference will bump up the reference count of this +destructor. + +


    gddBounds

    +A simple class to describe the boundaries of a dimension. + +

    #include "gdd.h"

    + +void setSize(aitIndex c);
    +Sets the number of elements for this bounds. + +

    +void set(aitIndex first, aitIndex cnt);
    +void get(aitIndex& first, aitIndex& cnt);
    +
    +Set and get the first element in this bounds and the number of elements. + +

    aitIndex size(void) const;
    +Return the number of element in this bounds. + +

    aitIndex first(void) const;
    +Return the first element in this bounds. + +


    gddBounds1D gddBounds2D gddBounds3D

    +Specialized classes for describing with one, two, and three dimension data. +Since three of less dimension are the most common, bounds array classes for +each type exist and are managed on free lists. All of these classes +overload the new/delete operators so that the memory allocater is not used. +All of these classes are just arrays of gddBounds. Only one method exists +in these classes, gddBounds* boundArray(void), which returns a pointer +to the array of gddBounds. + +

    #include "gdd.h"

    + +


    gddSemaphore

    +Simple wrapper around the underlying OS semphore functions. Implimentation +is OS dependant and is not really relevant in single threaded application. +This is a binary semaphore meant to be used for locking sections of code +or perhaps in a simple producer/consumer program. + +

    #include "gddUtils.h"

    + + +gddSemaphore(void)
    +gddSemaphore(gddSemType init)
    +
    +Create a simple binary gddSemaphore which defaults to full. Create a +gddSemaphore which is set initially to state init. Init +is either gddSemEmpty or gddSemFull. + +

    +void give(void)
    +void take(void)
    +
    +Operations for manipulating the semaphore. Take will block if the +semaphore is empty, waiting until it is full. Give sets the +semaphore back to full state. + +

    int take(aitUint32 usec)
    +Attempt to perform the semaphore take operation. Only block for usec +microseconds. Returns true if time-out occurred and semaphore is not +really taken. + +


    gddIntLock

    +A simple wrapper around OS interrupt locking services. + +

    #include "gddUtils.h"

    + +


    aitConvert

    +A set of convenience functions for quickly converting arrays of data from one +aitType to another. The header file for these functions also contains a +set of standard functions for converting each of the ait types from/to +network byte order. + +

    #include "aitConvert.h"

    + +

    void aitConvert(aitEnum desttype, void* dest, + aitEnum srctype, const void* src, aitIndex count)
    +Take count number of elements from src of type srctype, +copy then to dest and at the same time convert them to type +desttype. + +

    +void aitConvertToNet(aitEnum desttype, void* dest, + aitEnum srctype, const void* src, aitIndex count)
    +void aitConvertFromNet(aitEnum desttype, void* dest, + aitEnum srctype, const void* src, aitIndex count)
    +
    +Same as aitConvert but change the byte order or data format to/from +network format. If the local host data format is network data format, +then these function are exactly the same as aitConvert. + +

    aitLocalNetworkDataFormatSame
    +This is a #define which is true if local host byte order is the same +as network byte order, otherwise it is false. + +


    aitTimeStamp

    +A class for storing and manipulating standard time stamps. This time +is similar to POSIX time stamps, it maintains time in seconds/nanoseconds +past a standard epoch. POSIX defines a time stamp as a long for seconds +and a type time_t for nanoseconds. Time_t is OS dependant and so is a +long. aitTimeStamp forces seconds and nanoseconds to be unsigned 32 bit +integers so the storage size is fixed and known. + +

    #include "aitHelpers.h"

    +Automatically included if aitTypes.h is included. + +

    +operator double()
    +operator float()
    +
    +Convert (cast) the seconds/nanoseconds to a double or float seconds with +decimal fraction of a second past epoch. + +

    static aitTimeStamp getCurrent();
    +Retrieve the current time in aitTimeStamp format. + +

    void get(unsigned long &tv_secOut,unsigned long &tv_nsecOut);
    +Retreive the various parts of the time stamp as separate quantities. + +

    aitTimeStamp()
    +aitTimeStamp(const aitTimeStamp &t)
    +aitTimeStamp(const unsigned long sec, const unsigned long nsec)
    +Create an aitTimeStamp that is initially zero or set to various values. + +

    +const unsigned NSecPerSec = 1000000000u;
    +const unsigned NSecPerUSec = 1000u;
    +const unsigned SecPerMin = 60u;
    +
    +Various constants for working with time. + +

    +aitTimeStamp operator+ (const aitTimeStamp &lhs, const aitTimeStamp &rhs);
    +aitTimeStamp operator- (const aitTimeStamp &lhs, const aitTimeStamp &rhs);
    +int operator>= (const aitTimeStamp &lhs, const aitTimeStamp &rhs);
    +
    +They are global functions for adding and subtracting and comparing time stamps. + +


    aitString

    +Standard class for holding and managing a variable length string. Strings +is GDD have two attributes associated with them: length and state. Length +is simply the total length of the string and state either constant or +allocated. + +

    #include "aitHelpers.h"

    +Automatically included if aitTypes.h is included. + +

    aitString(void);
    +Construct an aitString that is empty. + +

    +aitString(char* str);
    +aitString(aitString* str);
    +aitString(aitString& str);
    +
    +Construct an aitString which contains a copy of str. The +state of this aitString will be "allocated". + +

    +aitString(const char* str);
    +aitString(const aitString* str);
    +aitString(const aitString& str);
    +
    +Construct an aitString which contains a reference to str. The +state of this aitString will be "constant". + +

    void clear(void);
    +Clear out this aitString, freeing up any storage that may be allocated. + +

    +void dump(void) const;
    +void dump(const char* id) const;
    +
    +Print the contents of this aitString to stdout in a readable format. Good +tool for debugging or understanding the aitString class. + +

    +operator const char*(void) const;
    +operator char*(void) const;
    +
    +Pull out a reference to the string that this aitString is holding. These +are casting operators from aitString to char*. + +

    int isConstant(void) const;
    +Returns true is this aitString hold a constant string. + +

    +aitUint32 length(void) const;
    +const char* string(void) const;
    +
    +Return the length of the string in this aitString. Return a reference to +the string in this aitString. + +

    +aitString& operator=(......);
    +int copy(......);
    +
    +Create a copy of the variable string arguments in this aitString. This +aitString attributes will match that of the arguments. The assignment +operators just invoke copy(......). + +

    void replaceData(......);
    +Replace the data in this aitString with the string in the argument. This +function will only copy this aitStrings length number of bytes into it's +string from the argument string. + +

    void installString(......);
    +Invoke replaceData if this aitString is "constant". Invoke copy if this +aitString is not "constant". + +

    +static aitUint32 totalLength(aitString* array,aitIndex arraySize);
    +static aitUint32 stringsLength(aitString* array,aitIndex arraySize);
    +static aitIndex compact(aitString* array, aitIndex arraySize, + void* buf, aitIndex bufSize);
    +
    +totalLength() returns the total number of bytes that an array of aitStrings +requires. This length include the size of all the strings in the aitStrings. +Array is the aitString array, arraySize is the number of +elements in array array. The function stringsLength +returns the total length of all the strings in aitString array array. +The function compact takes aitString array array with +arraySize elements and places all the data contiguously into buffer +buf. The compacted buffer is bufSize number of bytes. The +compact function places all the aitString instances of the array at +the front of buf. The aitStrings in buf reference strings that are +further into the buffer buf. Compact returns the total +number of bytes used in buf. + +


    aitTypes/aitEnums

    +Standard GDD data types and enumerations for them. This header file contains +many macros for dicovering information about aitTypes. See the header. + +

    #include "aitTypes.h"

    + +

    +	aitInt8			8 bit signed integer
    +	aitUint8			8 bit unsigned integer
    +	aitInt16			16 bit signed integer
    +	aitUint16		16 bit unsigned integer
    +	aitEnum16		16 bit unsigned integer
    +	aitInt32			32 bit signed integer
    +	aitUint32		32 bit unsigned integer
    +	aitFloat32		32 bit floating point number
    +	aitFloat64		64 bit floating point number
    +	aitIndex			32 bit unsigned integer for indexing arrays
    +	aitPointer		largest pointer a system can hold
    +	aitStatus		32 bit unsigned integer
    +	aitBool			standard "c" enum - value aitTrue or aitFalse
    +	aitFixedString	A fixed string of length AIT_FIXED_STRING_SIZE
    +	aitTimeStamp	The standard seconds/nanoseconds time stamp
    +	aitString		Class for manipulating and storing strings
    +
    + +

    +aitTotal
    +aitConvertTotal
    +aitValid(x)
    +aitConvertValid(x)
    +
    +Value of the first two are the number of valid types. All the ait types +cannot be automatically converted using aitConvert. AitValid(x) returns +true if x has a valid ait type enumeration associated with it. +AitConvertValid(x) returns true if enumerated value x can be converted using +aitConvert. + +

    aitEnum
    +This is an enumerated data type, there is one name per ait types. See +header file for the definition. + +

    aitType
    +Data union of all the ait types. + +

    +aitSize[aitTotal]
    +aitName[aitTotal]
    +aitStringType[aitTotal]
    +
    +Arrays used for discovering information about ait types using the enumerated +ait type name as the index into the array. aitSize returns the size in +bytes of the ait type. aitName returns a character string name of the +ait type. aitStringType returns a string which can be used in sscanf to +read or convert a string into the actual ait type. + +


    gddStatus - Error Codes

    +Error codes returned by various GDD library functions and methods. See header +file. +

    #include "gddErrorsCodes.h"

    +char* gddErrorMessages[]
    +Return an error message string name for an error code. + +


    new/delete Free List Management

    +

    #include "gddNewDel.h"

    +A set of generic member function macros that can be added to any class +to allow the class to be managed on a free list using the new/delete +operators. See the header file for details on how to use this +facility. Most GDD library classes use this facility. + +


    Application Type Code Index Labels

    +

    #include "gddApps.h"

    +This file is generated from the standard application type table. It +contains all the #define statements for preregistered containers and +application type codes. + +


    +DBR To ait Conversion Functions
    +gddMapDbr
    +gddAitToDbr
    +gddDbrToAit
    +

    +

    #include "dbMapper.h"

    +Large set of utility functions for converting between EPICS DBR data types +and GDD containers. + +

    void gddMakeMapDBR(gddApplicationTypeTable& table);
    +The DBR mapping facility is not initialized automatically. It must be +initialized using this function before any of the functions are called. +It should be passed the standard default +application type table as an argument. + +

    chtype gddAitToDbr[]
    +Given an ait type code, return the equivalent EPICS DBR type code. + +

    gddDbrToAitTable gddDbrToAit[]
    +Given a DBR type code, return a structure that describe the ait/GDD type code +informtion for that DBR type code. The information in gddDbrToAitTable is +the GDD application type code and GDD application type name for a given DBR +type. This information is not extremely useful for simple applications. + +

    gddDbrMapFuncTable gddMapDbr[]
    +Array of conversion functions that convert between DBR structures and GDD +types. The index of this array is always the DBR type code, not the +GDD application type code. Each element of the array contains to functions: +conv_gdd and conv_dbr. Conv_gdd takes a DBR structure and returns a GDD +equivalent to the DBR structure. Conv_dbr takes a GDD and puts all the data +into a DBR structure. + +

    +struct gddDbrMapFuncTable {
    +	to_gdd conv_gdd;
    +	to_dbr conv_dbr;
    +};
    +
    +gdd* to_gdd(void* dbr_data_structure, aitIndex dbr_element_count);
    +int to_dbr(void* dbr_data_structure, gdd* gdd_instance); 
    +
    + +
    Argonne National Laboratory +Copyright +Information
    +
    Jim Kowalkowski (jbk@aps.anl.gov)
    updated 4/12/95 + diff --git a/src/gdd/genApps.cc b/src/gdd/genApps.cc new file mode 100644 index 000000000..ca9b12a0b --- /dev/null +++ b/src/gdd/genApps.cc @@ -0,0 +1,54 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// Author: Jim Kowalkowski +// Date: 2/96 +// +// Revision-Id: anj@aps.anl.gov-20101005214806-11v23zjlhk2pxaal +// + +#define epicsExportSharedSymbols +#include "gddAppTable.h" + +#if 0 +#ifndef GDD_NO_EPICS +#include "dbrMapper.h" +#endif +#endif + +int main(int argc, char* argv[]) +{ + FILE* fd; + + gddApplicationTypeTable& tt = gddApplicationTypeTable::AppTable(); + + if(argc<2) + { + fprintf(stderr,"You must enter a file name on command line\n"); + return -1; + } + + if((fd=fopen(argv[1],"w"))==NULL) + { + fprintf(stderr,"Cannot open file %s\n",argv[1]); + return -1; + } + +#if 0 +#ifndef GDD_NO_EPICS + gddMakeMapDBR(tt); +#endif +#endif + + tt.describe(fd); + fclose(fd); + + return 0; +} + diff --git a/src/gdd/smartGDDPointer.h b/src/gdd/smartGDDPointer.h new file mode 100644 index 000000000..ca835d503 --- /dev/null +++ b/src/gdd/smartGDDPointer.h @@ -0,0 +1,186 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// Smart Pointer Class for GDD +// ( handles referencing / unreferencing a gdd each time it is used ) +// +// Author: Jeff Hill +// + +#ifndef smartGDDPointer_h +#define smartGDDPointer_h + +#include "gdd.h" +#include "shareLib.h" + +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !! WARNING WARNING WARNING WARNING WARNING WARNING WARNING !! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// // be careful to manually unreference here +// // (the reference count is initialized to one by the gdd ctor) +// Gdd * pDD0 = new gdd; +// smartGDDPointer pDD1 (pDD0); +// pDD0->unreference (); +// +// // from here on down the smart pointer maintains the ref count +// smartGDDPointer pDD2 = pDD1; +// smartGDDPointer pDD3 = pDD2; +// pDD2 = pDD3; +// +template < class T > +class smartGDDPointerTemplate { +public: + smartGDDPointerTemplate (); + smartGDDPointerTemplate ( T & valueIn ); + smartGDDPointerTemplate ( T * pValueIn ); + smartGDDPointerTemplate ( const smartGDDPointerTemplate & ptrIn ); + ~smartGDDPointerTemplate (); + smartGDDPointerTemplate < T > & operator = ( T * rhs ); + smartGDDPointerTemplate < T > & operator = ( const smartGDDPointerTemplate & rhs ); + operator smartGDDPointerTemplate < const T > () const; + void set ( T * pNewValue ); + T * get () const; + T * operator -> () const; + T & operator * () const; + bool operator ! () const; + bool valid () const; + void swap ( smartGDDPointerTemplate < T > & ); + // + // see Meyers, "more effective C++", for explanation on + // why conversion to dumb pointer is ill advised here + // + //operator const gdd * () const; +protected: + T * pValue; +}; + +typedef smartGDDPointerTemplate < gdd > smartGDDPointer; +typedef smartGDDPointerTemplate < const gdd > smartConstGDDPointer; + +template < class T > +inline smartGDDPointerTemplate < T >::smartGDDPointerTemplate () : + pValue (0) {} + +template < class T > +inline smartGDDPointerTemplate < T >::smartGDDPointerTemplate ( T & valueIn ) : + pValue ( & valueIn ) +{ + gddStatus status = this->pValue->reference (); + assert ( ! status ); +} + +template < class T > +inline smartGDDPointerTemplate < T >::smartGDDPointerTemplate ( T * pValueIn ) : + pValue ( pValueIn ) +{ + if ( this->pValue != NULL ) { + gddStatus status = this->pValue->reference (); + assert ( ! status ); + } +} + +template < class T > +inline smartGDDPointerTemplate < T > :: + smartGDDPointerTemplate ( const smartGDDPointerTemplate < T > & ptrIn ) : + pValue ( ptrIn.pValue ) +{ + if ( this->pValue != NULL ) { + gddStatus status = this->pValue->reference (); + assert ( ! status ); + } +} + +template < class T > +inline smartGDDPointerTemplate < T > :: ~smartGDDPointerTemplate () +{ + if ( this->pValue != NULL ) { + gddStatus status = this->pValue->unreference (); + assert ( ! status ); + } +} + +template < class T > +inline void smartGDDPointerTemplate < T > :: set ( T * pNewValue ) +{ + if ( this->pValue != pNewValue ) { + if ( pNewValue ) { + gddStatus status = pNewValue->reference (); + assert ( ! status ); + } + if ( this->pValue ) { + this->pValue->unreference (); + } + this->pValue = pNewValue; + } +} + +template < class T > +inline T * smartGDDPointerTemplate < T > :: get () const +{ + return this->pValue; +} + +template < class T > +inline smartGDDPointerTemplate < T > & + smartGDDPointerTemplate < T >::operator = ( T * rhs ) +{ + this->set ( rhs ); + return *this; +} + +template < class T > +inline smartGDDPointerTemplate < T > & + smartGDDPointerTemplate < T >::operator = ( const smartGDDPointerTemplate < T > & rhs ) +{ + this->set ( rhs.pValue ); + return *this; +} + +template < class T > +inline T * smartGDDPointerTemplate < T > :: operator -> () const +{ + return this->pValue; +} + +template < class T > +inline T & smartGDDPointerTemplate < T > :: operator * () const +{ + return * this->pValue; +} + +template < class T > +inline bool smartGDDPointerTemplate < T > :: operator ! () const // X aCC 361 +{ + return this->pValue ? false : true; +} + +template < class T > +inline bool smartGDDPointerTemplate < T > :: valid () const // X aCC 361 +{ + return this->pValue ? true : false; +} + +template < class T > +inline void smartGDDPointerTemplate < T > :: swap ( smartGDDPointerTemplate < T > & in ) +{ + T * pTmp = this->pValue; + this->pValue = in.pValue; + in.pValue = pTmp; +} + +template < class T > +smartGDDPointerTemplate < T > :: operator smartGDDPointerTemplate < const T > () const +{ + return smartGDDPointerTemplate < const T > ( this->pValue ); +} + +#endif // smartGDDPointer_h + diff --git a/src/libCom/Com.rc b/src/libCom/Com.rc new file mode 100755 index 000000000..d0edc440b --- /dev/null +++ b/src/libCom/Com.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Common Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Common Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "Com\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "Com.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/libCom/Makefile b/src/libCom/Makefile new file mode 100644 index 000000000..7f0168fb0 --- /dev/null +++ b/src/libCom/Makefile @@ -0,0 +1,332 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP = ../.. +include $(TOP)/configure/CONFIG + +LIBCOM = $(TOP)/src/libCom + +# Command-line input support +epicsReadline_CFLAGS += -DEPICS_COMMANDLINE_LIBRARY=EPICS_COMMANDLINE_LIBRARY_$(COMMANDLINE_LIBRARY) +epicsReadline_INCLUDES += $(INCLUDES_$(COMMANDLINE_LIBRARY)) + +#POSIX thread priority scheduling flag +THREAD_CPPFLAGS_NO += -DDONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING +osdThread_CPPFLAGS += $(THREAD_CPPFLAGS_$(USE_POSIX_THREAD_PRIORITY_SCHEDULING)) + +#epicsVersion is created by this Makefile +INC += epicsVersion.h + +SRC_DIRS += $(LIBCOM)/bucketLib +INC += bucketLib.h +SRCS += bucketLib.c + +SRC_DIRS += $(LIBCOM)/ring +#following needed for locating epicsRingPointer.h and epicsRingBytes.h +INC += epicsRingPointer.h +INC += epicsRingBytes.h +SRCS += epicsRingPointer.cpp +SRCS += epicsRingBytes.c + +SRC_DIRS += $(LIBCOM)/calc +INC += postfix.h +SRCS += postfix.c +SRCS += calcPerform.c + +SRC_DIRS += $(LIBCOM)/cvtFast +INC += cvtFast.h +SRCS += cvtFast.c + +# From cppStd +SRC_DIRS += $(LIBCOM)/cppStd +INC += epicsAlgorithm.h +INC += epicsExcept.h +INC += epicsMemory.h + +# From cxxTemplates +SRC_DIRS += $(LIBCOM)/cxxTemplates +INC += resourceLib.h +INC += tsDLList.h +INC += tsSLList.h +INC += tsMinMax.h +INC += tsBTree.h +INC += tsFreeList.h +INC += epicsSingleton.h +INC += epicsGuard.h +INC += epicsOnce.h +SRCS += resourceLib.cpp +SRCS += epicsOnce.cpp +SRCS += epicsSingletonMutex.cpp + +SRC_DIRS += $(LIBCOM)/dbmf +INC += dbmf.h +SRCS += dbmf.c + +SRC_DIRS += $(LIBCOM)/ellLib +INC += ellLib.h +SRCS += ellLib.c + +SRC_DIRS += $(LIBCOM)/env +INC += envDefs.h +SRCS += envSubr.c +SRCS += envData.c + +SRC_DIRS += $(LIBCOM)/error +INC += epicsPrint.h +INC += errMdef.h +INC += errSymTbl.h +INC += errlog.h +INC += error.h +SRCS += errlog.c +SRCS += errSymLib.c +SRCS += errSymTbl.c + +SRC_DIRS += $(LIBCOM)/fdmgr +INC += fdManager.h +INC += fdmgr.h +SRCS += fdmgr.cpp +SRCS += fdManager.cpp + +SRC_DIRS += $(LIBCOM)/freeList +INC += freeList.h +SRCS += freeListLib.c +HTMLS += freeList/freeList.html + +SRC_DIRS += $(LIBCOM)/gpHash +INC += gpHash.h +SRCS += gpHashLib.c +HTMLS += gpHash/gpHash.html + +SRC_DIRS += $(LIBCOM)/iocsh +INC += iocsh.h +INC += registry.h +INC += libComRegister.h +SRCS += iocsh.cpp +SRCS += registry.c +SRCS += libComRegister.c + +SRC_DIRS += $(LIBCOM)/logClient +INC += iocLog.h +INC += logClient.h +SRCS += iocLog.c +SRCS += logClient.c + +SRC_DIRS += $(LIBCOM)/macLib +INC += macLib.h +SRCS += macCore.c +SRCS += macEnv.c +SRCS += macUtil.c + +SRC_DIRS += $(LIBCOM)/misc +INC += adjustment.h +INC += cantProceed.h +INC += dbDefs.h +INC += epicsConvert.h +INC += epicsExit.h +INC += epicsStdlib.h +INC += epicsString.h +INC += epicsTypes.h +INC += shareLib.h +INC += epicsExport.h +INC += unixFileName.h +INC += locationException.h +INC += ipAddrToAsciiAsynchronous.h +INC += compilerDependencies.h +INC += epicsUnitTest.h +INC += testMain.h +SRCS += aToIPAddr.c +SRCS += adjustment.c +SRCS += cantProceed.c +SRCS += epicsConvert.c +SRCS += epicsExit.c +SRCS += epicsStdlib.c +SRCS += epicsString.c +SRCS += truncateFile.c +SRCS += ipAddrToAsciiAsynchronous.cpp +SRCS += epicsUnitTest.c + +# From osi +SRC_DIRS += $(LIBCOM)/osi +INC += osiFileName.h +INC += osiSock.h +INC += osdSock.h +INC += epicsInterrupt.h +INC += osdInterrupt.h + +INC += epicsMutex.h +INC += osdMutex.h +INC += epicsEvent.h +INC += osdEvent.h +INC += epicsMath.h +INC += osdMessageQueue.h +INC += osdStrtod.h + +INC += epicsAssert.h +INC += epicsFindSymbol.h +INC += osiPoolStatus.h +INC += osdPoolStatus.h +INC += osdThread.h + +INC += epicsThread.h +INC += epicsTime.h +INC += epicsGeneralTime.h +INC += osdTime.h +INC += generalTimeSup.h +INC += osiClockTime.h +INC += epicsSignal.h +INC += osiProcess.h +INC += osiUnistd.h +INC += osiWireFormat.h +INC += osdWireFormat.h +INC += osdWireConfig.h +INC += epicsEndian.h +INC += epicsReadline.h +INC += epicsMessageQueue.h +INC += epicsStdio.h +INC += epicsStdioRedirect.h +INC += epicsGetopt.h + +INC += devLib.h +INC += devLibVME.h +INC += devLibVMEImpl.h +INC += osdVME.h + +SRCS += epicsThread.cpp +SRCS += epicsMutex.cpp +SRCS += epicsEvent.cpp +SRCS += epicsTime.cpp +SRCS += epicsMessageQueue.cpp +SRCS += epicsMath.cpp + +SRCS += epicsGeneralTime.c +SRCS += osiClockTime.c + +SRCS += osdSock.c +SRCS += osdSockAddrReuse.cpp +SRCS += osiSock.c +SRCS += systemCallIntMech.cpp +SRCS += epicsSocketConvertErrnoToString.cpp +SRCS += osdAssert.c +SRCS += osdFindSymbol.c +SRCS += osdInterrupt.c +SRCS += osdPoolStatus.c +SRCS += osdSignal.cpp +SRCS += osdEnv.c +SRCS += epicsReadline.c +SRCS += epicsTempFile.cpp +SRCS += epicsStdio.c +SRCS += osdStdio.c + +osdEnv_CFLAGS_WIN32= -U__STDC__ + +SRCS += osdThread.c +SRCS += osdMutex.c +SRCS += osdEvent.c +SRCS += osdTime.cpp +SRCS += osdProcess.c +SRCS += osdNetIntf.c +SRCS += osdMessageQueue.c + +SRCS += devLibVME.c +SRCS += devLibVMEOSD.c + +SRC_DIRS += $(LIBCOM)/taskwd +INC += taskwd.h +SRCS += taskwd.c + +SRC_DIRS += $(LIBCOM)/timer +INC += epicsTimer.h +SRCS += epicsTimer.cpp +SRCS += timer.cpp +SRCS += timerQueue.cpp +SRCS += timerQueueActive.cpp +SRCS += timerQueueActiveMgr.cpp +SRCS += timerQueuePassive.cpp + +#tsDefs contains R3.13 compatibility tsStamp code +SRC_DIRS += $(LIBCOM)/tsDefs +INC += tsDefs.h +SRCS += tsDefs.c + +# Time providers, in osi +SRCS_vxWorks += osiNTPTime.c +SRCS_RTEMS += osiNTPTime.c + +# These files are in osi/os/vxWorks +# Special reboot hook +SRCS_vxWorks += atReboot.cpp +# For old vxWorks applications +INC_vxWorks += camacLib.h +INC_vxWorks += epicsDynLink.h +INC_vxWorks += module_types.h +INC_vxWorks += task_params.h +SRCS_vxWorks += epicsDynLink.c +SRCS_vxWorks += veclist.c +SRCS_vxWorks += logMsgToErrlog.cpp + +#This forces the vxWorks compatibility stuff to be loaded +OBJS_vxWorks = vxComLibrary + +# These files are in osi/os/WIN32 +SRCS_WIN32 += epicsGetopt.c +SRCS_WIN32 += setThreadName.cpp +#SRCS_WIN32 += dllmain.cpp +SRCS_WIN32 += forceBadAllocException.cpp + +# Library to build: +# lib$(LIBRARY).a or ..dll/..exp/..lib +# +LIBRARY=Com + +Com_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 + +Com_RCS = Com.rc + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=Com +OBJLIB_SRCS = $(SRCS) +endif + +# libs needed for PROD and TESTPRODUCT +PROD_LIBS = Com +PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 + +# for bldErrSymTbl: +# +ERR_S_FILES += $(TOP)/src/libCom/osi/devLib.h +ERR_S_FILES += $(TOP)/src/as/asLib.h +ERR_S_FILES += $(TOP)/src/db/dbAccessDefs.h +ERR_S_FILES += $(TOP)/src/dbStatic/devSup.h +ERR_S_FILES += $(TOP)/src/dbStatic/drvSup.h +ERR_S_FILES += $(TOP)/src/dbStatic/recSup.h +ERR_S_FILES += $(TOP)/src/dbStatic/dbStaticLib.h +ERR_S_FILES += $(LIBCOM)/error/errMdef.h +ERR_S_FILES += $(TOP)/src/cas/generic/casdef.h +ERR_S_FILES += $(TOP)/src/gdd/gddAppFuncTable.h + +include $(TOP)/configure/RULES + +osdAssert$(OBJ): $(COMMON_DIR)/epicsVersion.h +epicsTime$(OBJ): $(COMMON_DIR)/epicsVersion.h +osdNetIntf$(OBJ): $(COMMON_DIR)/epicsVersion.h +osdSock$(OBJ): $(COMMON_DIR)/epicsVersion.h + +envData.c: $(LIBCOM)/env/envDefs.h $(LIBCOM)/env/bldEnvData.pl \ + $(CONFIG)/CONFIG_ENV $(CONFIG)/CONFIG_SITE_ENV + $(PERL) $(LIBCOM)/env/bldEnvData.pl $(CONFIG) + +errSymTbl.c: $(ERR_S_FILES) $(LIBCOM)/error/makeStatTbl.pl + $(PERL) $(LIBCOM)/error/makeStatTbl.pl $(ERR_S_FILES) + +$(COMMON_DIR)/epicsVersion.h: $(CONFIG)/CONFIG_BASE_VERSION $(CONFIG)/CONFIG_SITE + $(PERL) $(LIBCOM)/misc/makeEpicsVersion.pl $(CONFIG)/CONFIG_BASE_VERSION $(@D) $(EPICS_SITE_VERSION) + +clean:: + @$(RM) errSymTbl.c envData.c + diff --git a/src/libCom/bucketLib/bucketLib.c b/src/libCom/bucketLib/bucketLib.c new file mode 100644 index 000000000..9fd887b6d --- /dev/null +++ b/src/libCom/bucketLib/bucketLib.c @@ -0,0 +1,522 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeffrey O. Hill + * hill@atdiv.lanl.gov + * (505) 665 1831 + * Date: 9-93 + * + * NOTES: + * .01 Storage for identifier must persist until an item is deleted + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "freeList.h" /* bucketLib uses freeListLib inside the DLL */ +#include "bucketLib.h" + +/* + * these data type dependent routines are + * provided in the bucketLib.c + */ +typedef BUCKETID bucketHash(BUCKET *pb, const void *pId); +typedef ITEM **bucketCompare(ITEM **ppi, const void *pId); + +static bucketCompare bucketUnsignedCompare; +static bucketCompare bucketPointerCompare; +static bucketCompare bucketStringCompare; +static bucketHash bucketUnsignedHash; +static bucketHash bucketPointerHash; +static bucketHash bucketStringHash; + +typedef struct { + bucketHash *pHash; + bucketCompare *pCompare; + buckTypeOfId type; +}bucketSET; + +static bucketSET BSET[] = { + {bucketUnsignedHash, bucketUnsignedCompare, bidtUnsigned}, + {bucketPointerHash, bucketPointerCompare, bidtPointer}, + {bucketStringHash, bucketStringCompare, bidtString} +}; + +static int bucketAddItem(BUCKET *prb, bucketSET *pBSET, + const void *pId, const void *pApp); +static void *bucketLookupItem(BUCKET *pb, bucketSET *pBSET, const void *pId); + + + +/* + * bucket id bit width + */ +#define BUCKETID_BIT_WIDTH (sizeof(BUCKETID)*CHAR_BIT) + +/* + * Maximum bucket size + */ +#define BUCKET_MAX_WIDTH 12 + + +/* + * bucketUnsignedCompare() + */ +static ITEM **bucketUnsignedCompare (ITEM **ppi, const void *pId) +{ + unsigned id; + unsigned *pItemId; + ITEM *pi; + + id = * (unsigned *) pId; + while ( (pi = *ppi) ) { + if (bidtUnsigned == pi->type) { + pItemId = (unsigned *) pi->pId; + if (id == *pItemId) { + return ppi; + } + } + ppi = &pi->pItem; + } + return NULL; +} + + +/* + * bucketPointerCompare() + */ +static ITEM **bucketPointerCompare (ITEM **ppi, const void *pId) +{ + void *ptr; + void **pItemId; + ITEM *pi; + + ptr = * (void **) pId; + while ( (pi = *ppi) ) { + if (bidtPointer == pi->type ) { + pItemId = (void **) pi->pId; + if (ptr == *pItemId) { + return ppi; + } + } + ppi = &pi->pItem; + } + return NULL; +} + + +/* + * bucketStringCompare () + */ +static ITEM **bucketStringCompare (ITEM **ppi, const void *pId) +{ + const char *pStr = pId; + ITEM *pi; + int status; + + while ( (pi = *ppi) ) { + if (bidtString == pi->type) { + status = strcmp (pStr, (char *)pi->pId); + if (status == '\0') { + return ppi; + } + } + ppi = &pi->pItem; + } + return NULL; +} + + +/* + * bucketUnsignedHash () + */ +static BUCKETID bucketUnsignedHash (BUCKET *pb, const void *pId) +{ + const unsigned *pUId = pId; + unsigned src; + BUCKETID hashid; + + src = *pUId; + hashid = src; + src = src >> pb->hashIdNBits; + while (src) { + hashid = hashid ^ src; + src = src >> pb->hashIdNBits; + } + hashid = hashid & pb->hashIdMask; + + return hashid; +} + + +/* + * bucketPointerHash () + */ +static BUCKETID bucketPointerHash (BUCKET *pb, const void *pId) +{ + void * const *ppId = (void * const *) pId; + unsigned long src; + BUCKETID hashid; + + /* + * This makes the assumption that + * a pointer will fit inside of a long + * (this assumption may not port to all + * CPU architectures) + */ + src = (unsigned long) *ppId; + hashid = src; + src = src >> pb->hashIdNBits; + while(src){ + hashid = hashid ^ src; + src = src >> pb->hashIdNBits; + } + hashid = hashid & pb->hashIdMask; + + return hashid; +} + + +/* + * bucketStringHash () + */ +static BUCKETID bucketStringHash (BUCKET *pb, const void *pId) +{ + const char *pStr = pId; + BUCKETID hashid; + unsigned i; + + hashid = 0; + i = 1; + while(*pStr){ + hashid += *pStr * i; + pStr++; + i++; + } + + hashid = hashid % (pb->hashIdMask+1); + + return hashid; +} + + + +/* + * bucketCreate() + */ +epicsShareFunc BUCKET * epicsShareAPI bucketCreate (unsigned nHashTableEntries) +{ + BUCKETID mask; + unsigned nbits; + BUCKET *pb; + + /* + * no absurd sized buckets + */ + if (nHashTableEntries<=1) { + fprintf (stderr, "Tiny bucket create failed\n"); + return NULL; + } + + /* + * count the number of bits in the bucket id + */ + if ( BUCKETID_BIT_WIDTH > 0 ) { + for (nbits=0; nbits=BUCKETID_BIT_WIDTH) { + fprintf ( + stderr, + "%s at %d: Requested index width=%d to large. max=%ld\n", + __FILE__, + __LINE__, + nbits, + (long)(BUCKETID_BIT_WIDTH-1)); + return NULL; + } + + pb = (BUCKET *) calloc(1, sizeof(*pb)); + if (!pb) { + return pb; + } + + pb->hashIdMask = mask; + pb->hashIdNBits = nbits; + freeListInitPvt(&pb->freeListPVT, sizeof(ITEM), 1024); + + pb->pTable = (ITEM **) calloc (mask+1, sizeof(*pb->pTable)); + if (!pb->pTable) { + freeListCleanup(pb->freeListPVT); + free (pb); + return NULL; + } + return pb; +} + + +/* + * bucketFree() + */ +epicsShareFunc int epicsShareAPI bucketFree (BUCKET *prb) +{ + /* + * deleting a bucket with entries in use + * will cause memory leaks and is not allowed + */ + assert (prb->nInUse==0); + + /* + * free the free list + */ + freeListCleanup(prb->freeListPVT); + free (prb->pTable); + free (prb); + + return S_bucket_success; +} + + +/* + * bucketAddItem() + */ +epicsShareFunc int epicsShareAPI + bucketAddItemUnsignedId(BUCKET *prb, const unsigned *pId, const void *pApp) +{ + return bucketAddItem(prb, &BSET[bidtUnsigned], pId, pApp); +} +epicsShareFunc int epicsShareAPI + bucketAddItemPointerId(BUCKET *prb, void * const *pId, const void *pApp) +{ + return bucketAddItem(prb, &BSET[bidtPointer], pId, pApp); +} +epicsShareFunc int epicsShareAPI + bucketAddItemStringId(BUCKET *prb, const char *pId, const void *pApp) +{ + return bucketAddItem(prb, &BSET[bidtString], pId, pApp); +} +static int bucketAddItem(BUCKET *prb, bucketSET *pBSET, const void *pId, const void *pApp) +{ + BUCKETID hashid; + ITEM **ppi; + ITEM **ppiExists; + ITEM *pi; + + /* + * try to get it off the free list first. If + * that fails then malloc() + */ + pi = (ITEM *) freeListMalloc(prb->freeListPVT); + if (!pi) { + return S_bucket_noMemory; + } + + /* + * create the hash index + */ + hashid = (*pBSET->pHash) (prb, pId); + + pi->pApp = pApp; + pi->pId = pId; + pi->type = pBSET->type; + assert ((hashid & ~prb->hashIdMask) == 0); + ppi = &prb->pTable[hashid]; + /* + * Dont reuse a resource id ! + */ + ppiExists = (*pBSET->pCompare) (ppi, pId); + if (ppiExists) { + freeListFree(prb->freeListPVT,pi); + return S_bucket_idInUse; + } + pi->pItem = *ppi; + prb->pTable[hashid] = pi; + prb->nInUse++; + + return S_bucket_success; +} + +/* + * bucketLookupAndRemoveItem () + */ +static void *bucketLookupAndRemoveItem (BUCKET *prb, bucketSET *pBSET, const void *pId) +{ + BUCKETID hashid; + ITEM **ppi; + ITEM *pi; + void *pApp; + + /* + * create the hash index + */ + hashid = (*pBSET->pHash) (prb, pId); + + assert((hashid & ~prb->hashIdMask) == 0); + ppi = &prb->pTable[hashid]; + ppi = (*pBSET->pCompare) (ppi, pId); + if(!ppi){ + return NULL; + } + prb->nInUse--; + pi = *ppi; + *ppi = pi->pItem; + + pApp = (void *) pi->pApp; + + /* + * stuff it on the free list + */ + freeListFree(prb->freeListPVT, pi); + + return pApp; +} +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtUnsigned], pId); +} +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemPointerId (BUCKET *prb, void * const *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtPointer], pId); +} +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemStringId (BUCKET *prb, const char *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtString], pId); +} + + +/* + * bucketRemoveItem() + */ +epicsShareFunc int epicsShareAPI + bucketRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtUnsigned], pId)?S_bucket_success:S_bucket_uknId; +} +epicsShareFunc int epicsShareAPI + bucketRemoveItemPointerId (BUCKET *prb, void * const *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtPointer], pId)?S_bucket_success:S_bucket_uknId; +} +epicsShareFunc int epicsShareAPI + bucketRemoveItemStringId (BUCKET *prb, const char *pId) +{ + return bucketLookupAndRemoveItem(prb, &BSET[bidtString], pId)?S_bucket_success:S_bucket_uknId; +} + + +/* + * bucketLookupItem() + */ +epicsShareFunc void * epicsShareAPI + bucketLookupItemUnsignedId (BUCKET *prb, const unsigned *pId) +{ + return bucketLookupItem(prb, &BSET[bidtUnsigned], pId); +} +epicsShareFunc void * epicsShareAPI + bucketLookupItemPointerId (BUCKET *prb, void * const *pId) +{ + return bucketLookupItem(prb, &BSET[bidtPointer], pId); +} +epicsShareFunc void * epicsShareAPI + bucketLookupItemStringId (BUCKET *prb, const char *pId) +{ + return bucketLookupItem(prb, &BSET[bidtString], pId); +} +static void *bucketLookupItem (BUCKET *pb, bucketSET *pBSET, const void *pId) +{ + BUCKETID hashid; + ITEM **ppi; + + /* + * create the hash index + */ + hashid = (*pBSET->pHash) (pb, pId); + assert((hashid & ~pb->hashIdMask) == 0); + + /* + * at the bottom level just + * linear search for it. + */ + ppi = (*pBSET->pCompare) (&pb->pTable[hashid], pId); + if(ppi){ + return (void *) (*ppi)->pApp; + } + return NULL; +} + + + +/* + * bucketShow() + */ +epicsShareFunc int epicsShareAPI bucketShow(BUCKET *pb) +{ + ITEM **ppi; + ITEM *pi; + unsigned nElem; + double X; + double XX; + double mean; + double stdDev; + unsigned count; + unsigned maxEntries; + + printf( "Bucket entries in use = %d bytes in use = %ld\n", + pb->nInUse, + (long) (sizeof(*pb)+(pb->hashIdMask+1)* + sizeof(ITEM *)+pb->nInUse*sizeof(ITEM))); + + ppi = pb->pTable; + nElem = pb->hashIdMask+1; + X = 0.0; + XX = 0.0; + maxEntries = 0; + while (ppi < &pb->pTable[nElem]) { + pi = *ppi; + count = 0; + while (pi) { + count++; + pi = pi->pItem; + } + X += count; + XX += count*count; + if (count > maxEntries) maxEntries = count; + ppi++; + } + + mean = X/nElem; + stdDev = sqrt(XX/nElem - mean*mean); + printf( "Bucket entries/hash id - mean = %f std dev = %f max = %d\n", + mean, + stdDev, + maxEntries); + + return S_bucket_success; +} + + diff --git a/src/libCom/bucketLib/bucketLib.h b/src/libCom/bucketLib/bucketLib.h new file mode 100644 index 000000000..0d22c52cd --- /dev/null +++ b/src/libCom/bucketLib/bucketLib.h @@ -0,0 +1,93 @@ +/*************************************************************************\ +* 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 is 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: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 9-93 + * + * NOTES: + * .01 Storage for identifier must persist until an item is deleted + */ + +#ifndef INCbucketLibh +#define INCbucketLibh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "errMdef.h" +#include "epicsTypes.h" +#include "shareLib.h" + +typedef unsigned BUCKETID; + +typedef enum {bidtUnsigned, bidtPointer, bidtString} buckTypeOfId; + +typedef struct item{ + struct item *pItem; + const void *pId; + const void *pApp; + buckTypeOfId type; +}ITEM; + +typedef struct bucket{ + ITEM **pTable; + void *freeListPVT; + unsigned hashIdMask; + unsigned hashIdNBits; + unsigned nInUse; +}BUCKET; + +epicsShareFunc BUCKET * epicsShareAPI bucketCreate (unsigned nHashTableEntries); +epicsShareFunc int epicsShareAPI bucketFree (BUCKET *prb); +epicsShareFunc int epicsShareAPI bucketShow (BUCKET *pb); + +/* + * !! Identifier must exist (and remain constant) at the specified address until + * the item is deleted from the bucket !! + */ +epicsShareFunc int epicsShareAPI bucketAddItemUnsignedId (BUCKET *prb, + const unsigned *pId, const void *pApp); +epicsShareFunc int epicsShareAPI bucketAddItemPointerId (BUCKET *prb, + void * const *pId, const void *pApp); +epicsShareFunc int epicsShareAPI bucketAddItemStringId (BUCKET *prb, + const char *pId, const void *pApp); + +epicsShareFunc int epicsShareAPI bucketRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId); +epicsShareFunc int epicsShareAPI bucketRemoveItemPointerId (BUCKET *prb, void * const *pId); +epicsShareFunc int epicsShareAPI bucketRemoveItemStringId (BUCKET *prb, const char *pId); + +epicsShareFunc void * epicsShareAPI bucketLookupItemUnsignedId (BUCKET *prb, const unsigned *pId); +epicsShareFunc void * epicsShareAPI bucketLookupItemPointerId (BUCKET *prb, void * const *pId); +epicsShareFunc void * epicsShareAPI bucketLookupItemStringId (BUCKET *prb, const char *pId); + +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId); +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemPointerId (BUCKET *prb, void * const *pId); +epicsShareFunc void * epicsShareAPI bucketLookupAndRemoveItemStringId (BUCKET *prb, const char *pId); + + +/* + * Status returned by bucketLib functions + */ +#define BUCKET_SUCCESS S_bucket_success +#define S_bucket_success 0 +#define S_bucket_noMemory (M_bucket | 1) /*Memory allocation failed*/ +#define S_bucket_idInUse (M_bucket | 2) /*Identifier already in use*/ +#define S_bucket_uknId (M_bucket | 3) /*Unknown identifier*/ + +#ifdef __cplusplus +} +#endif + +#endif /*INCbucketLibh*/ + diff --git a/src/libCom/calc/calcPerform.c b/src/libCom/calc/calcPerform.c new file mode 100644 index 000000000..2f7eb9311 --- /dev/null +++ b/src/libCom/calc/calcPerform.c @@ -0,0 +1,485 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Revision-Id: anj@aps.anl.gov-20101007191624-sqws79ec9gxn7reb */ +/* + * Author: Julie Sander and Bob Dalesio + * Date: 07-27-87 + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "osiUnistd.h" +#include "dbDefs.h" +#include "epicsMath.h" +#include "errlog.h" +#include "postfix.h" +#include "postfixPvt.h" + +static double calcRandom(void); +static int cond_search(const char **ppinst, int match); + +#ifndef PI +#define PI 3.14159265358979323 +#endif + + +/* calcPerform + * + * Evalutate the postfix expression + */ +epicsShareFunc long + calcPerform(double *parg, double *presult, const char *pinst) +{ + double stack[CALCPERFORM_STACK+1]; /* zero'th entry not used */ + double *ptop; /* stack pointer */ + double top; /* value from top of stack */ + int itop; /* integer from top of stack */ + int op; + int nargs; + + /* initialize */ + ptop = stack; + + /* RPN evaluation loop */ + while ((op = *pinst++) != END_EXPRESSION){ + switch (op){ + + case LITERAL_DOUBLE: + memcpy((void *)++ptop, pinst, sizeof(double)); + pinst += sizeof(double); + break; + + case LITERAL_INT: + memcpy(&itop, pinst, sizeof(int)); + *++ptop = itop; + pinst += sizeof(int); + break; + + case FETCH_VAL: + *++ptop = *presult; + break; + + case FETCH_A: + case FETCH_B: + case FETCH_C: + case FETCH_D: + case FETCH_E: + case FETCH_F: + case FETCH_G: + case FETCH_H: + case FETCH_I: + case FETCH_J: + case FETCH_K: + case FETCH_L: + *++ptop = parg[op - FETCH_A]; + break; + + case STORE_A: + case STORE_B: + case STORE_C: + case STORE_D: + case STORE_E: + case STORE_F: + case STORE_G: + case STORE_H: + case STORE_I: + case STORE_J: + case STORE_K: + case STORE_L: + parg[op - STORE_A] = *ptop--; + break; + + case CONST_PI: + *++ptop = PI; + break; + + case CONST_D2R: + *++ptop = PI/180.; + break; + + case CONST_R2D: + *++ptop = 180./PI; + break; + + case UNARY_NEG: + *ptop = - *ptop; + break; + + case ADD: + top = *ptop--; + *ptop += top; + break; + + case SUB: + top = *ptop--; + *ptop -= top; + break; + + case MULT: + top = *ptop--; + *ptop *= top; + break; + + case DIV: + top = *ptop--; + *ptop /= top; + break; + + case MODULO: + itop = (long) *ptop--; + if (itop) + *ptop = (long) *ptop % itop; + else + *ptop = epicsNAN; /* NaN */ + break; + + case POWER: + top = *ptop--; + *ptop = pow(*ptop, top); + break; + + case ABS_VAL: + if (*ptop < 0.0) *ptop = - *ptop; + break; + + case EXP: + *ptop = exp(*ptop); + break; + + case LOG_10: + *ptop = log10(*ptop); + break; + + case LOG_E: + *ptop = log(*ptop); + break; + + case MAX: + nargs = *pinst++; + while (--nargs) { + top = *ptop--; + if (*ptop < top || isnan(top)) + *ptop = top; + } + break; + + case MIN: + nargs = *pinst++; + while (--nargs) { + top = *ptop--; + if (*ptop > top || isnan(top)) + *ptop = top; + } + break; + + case SQU_RT: + *ptop = sqrt(*ptop); + break; + + case ACOS: + *ptop = acos(*ptop); + break; + + case ASIN: + *ptop = asin(*ptop); + break; + + case ATAN: + *ptop = atan(*ptop); + break; + + case ATAN2: + top = *ptop--; + *ptop = atan2(top, *ptop); /* Ouch!: Args backwards! */ + break; + + case COS: + *ptop = cos(*ptop); + break; + + case SIN: + *ptop = sin(*ptop); + break; + + case TAN: + *ptop = tan(*ptop); + break; + + case COSH: + *ptop = cosh(*ptop); + break; + + case SINH: + *ptop = sinh(*ptop); + break; + + case TANH: + *ptop = tanh(*ptop); + break; + + case CEIL: + *ptop = ceil(*ptop); + break; + + case FLOOR: + *ptop = floor(*ptop); + break; + + case FINITE: + nargs = *pinst++; + top = finite(*ptop); + while (--nargs) { + --ptop; + top = top && finite(*ptop); + } + *ptop = top; + break; + + case ISINF: + *ptop = isinf(*ptop); + break; + + case ISNAN: + nargs = *pinst++; + top = isnan(*ptop); + while (--nargs) { + --ptop; + top = top || isnan(*ptop); + } + *ptop = top; + break; + + case NINT: + top = *ptop; + *ptop = (double)(long)(top >= 0 ? top + 0.5 : top - 0.5); + break; + + case RANDOM: + *++ptop = calcRandom(); + break; + + case REL_OR: + top = *ptop--; + *ptop = *ptop || top; + break; + + case REL_AND: + top = *ptop--; + *ptop = *ptop && top; + break; + + case REL_NOT: + *ptop = ! *ptop; + break; + + case BIT_OR: + itop = (long) *ptop--; + *ptop = (long) *ptop | itop; + break; + + case BIT_AND: + itop = (long) *ptop--; + *ptop = (long) *ptop & itop; + break; + + case BIT_EXCL_OR: + itop = (long) *ptop--; + *ptop = (long) *ptop ^ itop; + break; + + case BIT_NOT: + itop = (long) *ptop; + *ptop = ~itop; + break; + + case RIGHT_SHIFT: + itop = (long) *ptop--; + *ptop = (long) *ptop >> itop; + break; + + case LEFT_SHIFT: + itop = (long) *ptop--; + *ptop = (long) *ptop << itop; + break; + + case NOT_EQ: + top = *ptop--; + *ptop = *ptop != top; + break; + + case LESS_THAN: + top = *ptop--; + *ptop = *ptop < top; + break; + + case LESS_OR_EQ: + top = *ptop--; + *ptop = *ptop <= top; + break; + + case EQUAL: + top = *ptop--; + *ptop = *ptop == top; + break; + + case GR_OR_EQ: + top = *ptop--; + *ptop = *ptop >= top; + break; + + case GR_THAN: + top = *ptop--; + *ptop = *ptop > top; + break; + + case COND_IF: + if (*ptop-- == 0.0 && + cond_search(&pinst, COND_ELSE)) return -1; + break; + + case COND_ELSE: + if (cond_search(&pinst, COND_END)) return -1; + break; + + case COND_END: + break; + + default: + errlogPrintf("calcPerform: Bad Opcode %d at %p\n", op, pinst-1); + return -1; + } + } + + /* The stack should now have one item on it, the expression value */ + if (ptop != stack + 1) + return -1; + *presult = *ptop; + return 0; +} + + +epicsShareFunc long +calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) +{ + unsigned long inputs = 0; + unsigned long stores = 0; + char op; + while ((op = *pinst++) != END_EXPRESSION) { + switch (op) { + + case LITERAL_DOUBLE: + pinst += sizeof(double); + break; + case LITERAL_INT: + pinst += sizeof(int); + break; + case MIN: + case MAX: + case FINITE: + case ISNAN: + pinst++; + break; + + case FETCH_A: + case FETCH_B: + case FETCH_C: + case FETCH_D: + case FETCH_E: + case FETCH_F: + case FETCH_G: + case FETCH_H: + case FETCH_I: + case FETCH_J: + case FETCH_K: + case FETCH_L: + /* Don't claim to use an arg we already stored to */ + inputs |= (1 << (op - FETCH_A)) & ~stores; + break; + + case STORE_A: + case STORE_B: + case STORE_C: + case STORE_D: + case STORE_E: + case STORE_F: + case STORE_G: + case STORE_H: + case STORE_I: + case STORE_J: + case STORE_K: + case STORE_L: + stores |= (1 << (op - STORE_A)); + break; + + default: + break; + } + } + if (pinputs) *pinputs = inputs; + if (pstores) *pstores = stores; + return 0; +} + +/* Generate a random number between 0 and 1 using the algorithm + * seed = (multy * seed) + addy Random Number Generator by Knuth + * SemiNumerical Algorithms + * Chapter 1 + * randy = seed / 65535.0 To normalize the number between 0 - 1 + */ +static unsigned short seed = 0xa3bf; +static unsigned short multy = 191 * 8 + 5; /* 191 % 8 == 5 */ +static unsigned short addy = 0x3141; + +static double calcRandom(void) +{ + seed = (seed * multy) + addy; + + /* between 0 - 1 */ + return (double) seed / 65535.0; +} + +/* Search the instruction stream for a matching operator, skipping any + * other conditional instructions found, and leave *ppinst pointing to + * the next instruction to be executed. + */ +static int cond_search(const char **ppinst, int match) +{ + const char *pinst = *ppinst; + int count = 1; + int op; + + while ((op = *pinst++) != END_EXPRESSION) { + if (op == match && --count == 0) { + *ppinst = pinst; + return 0; + } + switch (op) { + case LITERAL_DOUBLE: + pinst += sizeof(double); + break; + case LITERAL_INT: + pinst += sizeof(int); + break; + case MIN: + case MAX: + case FINITE: + case ISNAN: + pinst++; + break; + case COND_IF: + count++; + break; + } + } + return 1; +} diff --git a/src/libCom/calc/postfix.c b/src/libCom/calc/postfix.c new file mode 100644 index 000000000..1f1a60789 --- /dev/null +++ b/src/libCom/calc/postfix.c @@ -0,0 +1,609 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Revision-Id: anj@aps.anl.gov-20101007191624-sqws79ec9gxn7reb + * + * Subroutines used to convert an infix expression to a postfix expression + * + * Original Author: Bob Dalesio + * Date: 12-12-86 +*/ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsStdlib.h" +#include "epicsString.h" +#include "postfix.h" +#include "postfixPvt.h" +#include "shareLib.h" + + +/* declarations for postfix */ + +/* element types */ +typedef enum { + OPERAND, + LITERAL_OPERAND, + STORE_OPERATOR, + UNARY_OPERATOR, + VARARG_OPERATOR, + BINARY_OPERATOR, + SEPERATOR, + CLOSE_PAREN, + CONDITIONAL, + EXPR_TERMINATOR, +} element_type; + + +/* element table + * + * structure of an element + */ +typedef struct expression_element{ + char *name; /* character representation of an element */ + char in_stack_pri; /* priority on translation stack */ + char in_coming_pri; /* priority in input string */ + signed char runtime_effect; /* stack change, positive means push */ + element_type type; /* element type */ + rpn_opcode code; /* postfix opcode */ +} ELEMENT; + +/* + * NOTE: Keep these lists sorted. Elements are searched in reverse order, + * and where two names start with the same substring we must pick out the + * longest name first (hence the sort requirement). + * NOTE: All VARARG_OPERATORs have to be made known to the calcExprDump() + * routine at the end of this file. + */ +static const ELEMENT operands[] = { +/* name prio's stack element type opcode */ +{"!", 7, 8, 0, UNARY_OPERATOR, REL_NOT}, +{"(", 0, 8, 0, UNARY_OPERATOR, NOT_GENERATED}, +{"-", 7, 8, 0, UNARY_OPERATOR, UNARY_NEG}, +{".", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"0", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"1", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"2", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"3", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"4", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"5", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"6", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"7", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"8", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"9", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"A", 0, 0, 1, OPERAND, FETCH_A}, +{"ABS", 7, 8, 0, UNARY_OPERATOR, ABS_VAL}, +{"ACOS", 7, 8, 0, UNARY_OPERATOR, ACOS}, +{"ASIN", 7, 8, 0, UNARY_OPERATOR, ASIN}, +{"ATAN", 7, 8, 0, UNARY_OPERATOR, ATAN}, +{"ATAN2", 7, 8, -1, UNARY_OPERATOR, ATAN2}, +{"B", 0, 0, 1, OPERAND, FETCH_B}, +{"C", 0, 0, 1, OPERAND, FETCH_C}, +{"CEIL", 7, 8, 0, UNARY_OPERATOR, CEIL}, +{"COS", 7, 8, 0, UNARY_OPERATOR, COS}, +{"COSH", 7, 8, 0, UNARY_OPERATOR, COSH}, +{"D", 0, 0, 1, OPERAND, FETCH_D}, +{"D2R", 0, 0, 1, OPERAND, CONST_D2R}, +{"E", 0, 0, 1, OPERAND, FETCH_E}, +{"EXP", 7, 8, 0, UNARY_OPERATOR, EXP}, +{"F", 0, 0, 1, OPERAND, FETCH_F}, +{"FINITE", 7, 8, 0, VARARG_OPERATOR,FINITE}, +{"FLOOR", 7, 8, 0, UNARY_OPERATOR, FLOOR}, +{"G", 0, 0, 1, OPERAND, FETCH_G}, +{"H", 0, 0, 1, OPERAND, FETCH_H}, +{"I", 0, 0, 1, OPERAND, FETCH_I}, +{"INF", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"ISINF", 7, 8, 0, UNARY_OPERATOR, ISINF}, +{"ISNAN", 7, 8, 0, VARARG_OPERATOR,ISNAN}, +{"J", 0, 0, 1, OPERAND, FETCH_J}, +{"K", 0, 0, 1, OPERAND, FETCH_K}, +{"L", 0, 0, 1, OPERAND, FETCH_L}, +{"LN", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"LOG", 7, 8, 0, UNARY_OPERATOR, LOG_10}, +{"LOGE", 7, 8, 0, UNARY_OPERATOR, LOG_E}, +{"MAX", 7, 8, 0, VARARG_OPERATOR,MAX}, +{"MIN", 7, 8, 0, VARARG_OPERATOR,MIN}, +{"NINT", 7, 8, 0, UNARY_OPERATOR, NINT}, +{"NAN", 0, 0, 1, LITERAL_OPERAND,LITERAL_DOUBLE}, +{"NOT", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, +{"PI", 0, 0, 1, OPERAND, CONST_PI}, +{"R2D", 0, 0, 1, OPERAND, CONST_R2D}, +{"RNDM", 0, 0, 1, OPERAND, RANDOM}, +{"SIN", 7, 8, 0, UNARY_OPERATOR, SIN}, +{"SINH", 7, 8, 0, UNARY_OPERATOR, SINH}, +{"SQR", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"SQRT", 7, 8, 0, UNARY_OPERATOR, SQU_RT}, +{"TAN", 7, 8, 0, UNARY_OPERATOR, TAN}, +{"TANH", 7, 8, 0, UNARY_OPERATOR, TANH}, +{"VAL", 0, 0, 1, OPERAND, FETCH_VAL}, +{"~", 7, 8, 0, UNARY_OPERATOR, BIT_NOT}, +}; + +static const ELEMENT operators[] = { +/* name prio's stack element type opcode */ +{"!=", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, +{"#", 3, 3, -1, BINARY_OPERATOR,NOT_EQ}, +{"%", 5, 5, -1, BINARY_OPERATOR,MODULO}, +{"&", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, +{"&&", 2, 2, -1, BINARY_OPERATOR,REL_AND}, +{")", 0, 0, 0, CLOSE_PAREN, NOT_GENERATED}, +{"*", 5, 5, -1, BINARY_OPERATOR,MULT}, +{"**", 6, 6, -1, BINARY_OPERATOR,POWER}, +{"+", 4, 4, -1, BINARY_OPERATOR,ADD}, +{",", 0, 0, 0, SEPERATOR, NOT_GENERATED}, +{"-", 4, 4, -1, BINARY_OPERATOR,SUB}, +{"/", 5, 5, -1, BINARY_OPERATOR,DIV}, +{":", 0, 0, -1, CONDITIONAL, COND_ELSE}, +{":=", 0, 0, -1, STORE_OPERATOR, STORE_A}, +{";", 0, 0, 0, EXPR_TERMINATOR,NOT_GENERATED}, +{"<", 3, 3, -1, BINARY_OPERATOR,LESS_THAN}, +{"<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT}, +{"<=", 3, 3, -1, BINARY_OPERATOR,LESS_OR_EQ}, +{"=", 3, 3, -1, BINARY_OPERATOR,EQUAL}, +{"==", 3, 3, -1, BINARY_OPERATOR,EQUAL}, +{">", 3, 3, -1, BINARY_OPERATOR,GR_THAN}, +{">=", 3, 3, -1, BINARY_OPERATOR,GR_OR_EQ}, +{">>", 2, 2, -1, BINARY_OPERATOR,RIGHT_SHIFT}, +{"?", 0, 0, -1, CONDITIONAL, COND_IF}, +{"AND", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, +{"OR", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, +{"XOR", 1, 1, -1, BINARY_OPERATOR,BIT_EXCL_OR}, +{"^", 6, 6, -1, BINARY_OPERATOR,POWER}, +{"|", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, +{"||", 1, 1, -1, BINARY_OPERATOR,REL_OR}, +}; + + +/* get_element + * + * find the next expression element in the infix expression + */ +static int + get_element(int opnd, const char **ppsrc, const ELEMENT **ppel) +{ + const ELEMENT *ptable, *pel; + + *ppel = NULL; + + while (isspace((int) (unsigned char) **ppsrc)) ++*ppsrc; + if (**ppsrc == '\0') return FALSE; + + if (opnd) { + ptable = operands; + pel = ptable + NELEMENTS(operands) - 1; + } else { + ptable = operators; + pel = ptable + NELEMENTS(operators) - 1; + } + + while (pel >= ptable) { + size_t len = strlen(pel->name); + + if (epicsStrnCaseCmp(*ppsrc, pel->name, len) == 0) { + *ppel = pel; + *ppsrc += len; + return TRUE; + } + --pel; + } + return FALSE; +} + + +/* postfix + * + * convert an infix expression to a postfix expression + */ +epicsShareFunc long + postfix(const char *psrc, char * const ppostfix, short *perror) +{ + ELEMENT stack[80]; + ELEMENT *pstacktop = stack; + const ELEMENT *pel; + int operand_needed = TRUE; + int runtime_depth = 0; + int cond_count = 0; + char *pout = ppostfix; + char *pnext; + double lit_d; + int lit_i; + + if (psrc == NULL || *psrc == '\0' || + pout == NULL || perror == NULL) { + if (perror) *perror = CALC_ERR_NULL_ARG; + if (pout) *pout = END_EXPRESSION; + return -1; + } + + /* place the expression elements into postfix */ + *pout = END_EXPRESSION; + *perror = CALC_ERR_NONE; + + while (get_element(operand_needed, &psrc, &pel)) { + switch (pel->type) { + + case OPERAND: + *pout++ = pel->code; + runtime_depth += pel->runtime_effect; + operand_needed = FALSE; + break; + + case LITERAL_OPERAND: + runtime_depth += pel->runtime_effect; + + psrc -= strlen(pel->name); + lit_d = epicsStrtod(psrc, &pnext); + if (pnext == psrc) { + *perror = CALC_ERR_BAD_LITERAL; + goto bad; + } + psrc = pnext; + lit_i = lit_d; + if (lit_d != (double) lit_i) { + *pout++ = pel->code; + memcpy(pout, (void *)&lit_d, sizeof(double)); + pout += sizeof(double); + } else { + *pout++ = LITERAL_INT; + memcpy(pout, (void *)&lit_i, sizeof(int)); + pout += sizeof(int); + } + + operand_needed = FALSE; + break; + + case STORE_OPERATOR: + if (pout == ppostfix || pstacktop > stack || + *--pout < FETCH_A || *pout > FETCH_L) { + *perror = CALC_ERR_BAD_ASSIGNMENT; + goto bad; + } + /* Convert fetch into a store on the stack */ + *++pstacktop = *pel; + pstacktop->code = STORE_A + *pout - FETCH_A; + runtime_depth -= 1; + operand_needed = TRUE; + break; + + case UNARY_OPERATOR: + case VARARG_OPERATOR: + /* Move operators of >= priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri >= pel->in_coming_pri)) { + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Push new operator onto stack */ + pstacktop++; + *pstacktop = *pel; + break; + + case BINARY_OPERATOR: + /* Move operators of >= priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri >= pel->in_coming_pri)) { + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Push new operator onto stack */ + pstacktop++; + *pstacktop = *pel; + + operand_needed = TRUE; + break; + + case SEPERATOR: + /* Move operators to the output until open paren */ + while (pstacktop->name[0] != '(') { + if (pstacktop <= stack+1) { + *perror = CALC_ERR_BAD_SEPERATOR; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + operand_needed = TRUE; + pstacktop->runtime_effect -= 1; + break; + + case CLOSE_PAREN: + /* Move operators to the output until matching paren */ + while (pstacktop->name[0] != '(') { + if (pstacktop <= stack+1) { + *perror = CALC_ERR_PAREN_NOT_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + pstacktop--; /* remove ( from stack */ + /* if there is a vararg operator before the opening paren, + it inherits the (opening) paren's stack effect */ + if ((pstacktop > stack) && + pstacktop->type == VARARG_OPERATOR) { + pstacktop->runtime_effect = (pstacktop+1)->runtime_effect; + /* check for no arguments */ + if (pstacktop->runtime_effect > 0) { + *perror = CALC_ERR_INCOMPLETE; + goto bad; + } + } + break; + + case CONDITIONAL: + /* Move operators of > priority to the output */ + while ((pstacktop > stack) && + (pstacktop->in_stack_pri > pel->in_coming_pri)) { + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + /* Add new element to the output */ + *pout++ = pel->code; + runtime_depth += pel->runtime_effect; + + /* For : operator, also push COND_END code to stack */ + if (pel->name[0] == ':') { + if (--cond_count < 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + pstacktop++; + *pstacktop = *pel; + pstacktop->code = COND_END; + pstacktop->runtime_effect = 0; + } else { + cond_count++; + } + + operand_needed = TRUE; + break; + + case EXPR_TERMINATOR: + /* Move everything from stack to the output */ + while (pstacktop > stack) { + if (pstacktop->name[0] == '(') { + *perror = CALC_ERR_PAREN_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + + if (cond_count != 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + if (runtime_depth > 1) { + *perror = CALC_ERR_TOOMANY; + goto bad; + } + + operand_needed = TRUE; + break; + + default: + *perror = CALC_ERR_INTERNAL; + goto bad; + } + + if (runtime_depth < 0) { + *perror = CALC_ERR_UNDERFLOW; + goto bad; + } + if (runtime_depth >= CALCPERFORM_STACK) { + *perror = CALC_ERR_OVERFLOW; + goto bad; + } + } + + if (*psrc != '\0') { + *perror = CALC_ERR_SYNTAX; + goto bad; + } + + /* Move everything from stack to the output */ + while (pstacktop > stack) { + if (pstacktop->name[0] == '(') { + *perror = CALC_ERR_PAREN_OPEN; + goto bad; + } + *pout++ = pstacktop->code; + if (pstacktop->type == VARARG_OPERATOR) { + *pout++ = 1 - pstacktop->runtime_effect; + } + runtime_depth += pstacktop->runtime_effect; + pstacktop--; + } + *pout = END_EXPRESSION; + + if (cond_count != 0) { + *perror = CALC_ERR_CONDITIONAL; + goto bad; + } + if (operand_needed || runtime_depth != 1) { + *perror = CALC_ERR_INCOMPLETE; + goto bad; + } + return 0; + +bad: + *ppostfix = END_EXPRESSION; + return -1; +} + + +/* calcErrorStr + * + * Return a message string appropriate for the given error code + */ +epicsShareFunc const char * + calcErrorStr(short error) +{ + static const char *errStrs[] = { + "No error", + "Too many results returned", + "Badly formed numeric literal", + "Bad assignment target", + "Comma without enclosing parentheses", + "Close parenthesis found without open", + "Parenthesis still open at end of expression", + "Unbalanced conditional ?: operators", + "Incomplete expression, operand missing", + "Not enough operands provided", + "Runtime stack overflow", + "Syntax error, unknown operator/operand", + "NULL or empty input argument to postfix()", + "Internal error, unknown element type", + }; + + if (error < CALC_ERR_NONE || error > CALC_ERR_INTERNAL) + return NULL; + return errStrs[error]; +} + + +/* calcExprDump + * + * Disassemble the given postfix instructions to stdout + */ +epicsShareFunc void + calcExprDump(const char *pinst) +{ + static const char *opcodes[] = { + "End Expression", + /* Operands */ + "LITERAL_DOUBLE", "LITERAL_INT", "VAL", + "FETCH_A", "FETCH_B", "FETCH_C", "FETCH_D", "FETCH_E", "FETCH_F", + "FETCH_G", "FETCH_H", "FETCH_I", "FETCH_J", "FETCH_K", "FETCH_L", + /* Assignment */ + "STORE_A", "STORE_B", "STORE_C", "STORE_D", "STORE_E", "STORE_F", + "STORE_G", "STORE_H", "STORE_I", "STORE_J", "STORE_K", "STORE_L", + /* Trigonometry Constants */ + "CONST_PI", + "CONST_D2R", + "CONST_R2D", + /* Arithmetic */ + "UNARY_NEG", + "ADD", + "SUB", + "MULT", + "DIV", + "MODULO", + "POWER", + /* Algebraic */ + "ABS_VAL", + "EXP", + "LOG_10", + "LOG_E", + "MAX", + "MIN", + "SQU_RT", + /* Trigonometric */ + "ACOS", + "ASIN", + "ATAN", + "ATAN2", + "COS", + "COSH", + "SIN", + "SINH", + "TAN", + "TANH", + /* Numeric */ + "CEIL", + "FLOOR", + "FINITE", + "ISINF", + "ISNAN", + "NINT", + "RANDOM", + /* Boolean */ + "REL_OR", + "REL_AND", + "REL_NOT", + /* Bitwise */ + "BIT_OR", + "BIT_AND", + "BIT_EXCL_OR", + "BIT_NOT", + "RIGHT_SHIFT", + "LEFT_SHIFT", + /* Relationals */ + "NOT_EQ", + "LESS_THAN", + "LESS_OR_EQ", + "EQUAL", + "GR_OR_EQ", + "GR_THAN", + /* Conditional */ + "COND_IF", + "COND_ELSE", + "COND_END", + /* Misc */ + "NOT_GENERATED" + }; + char op; + double lit_d; + int lit_i; + + while ((op = *pinst) != END_EXPRESSION) { + switch (op) { + case LITERAL_DOUBLE: + memcpy((void *)&lit_d, ++pinst, sizeof(double)); + printf("\tDouble %g\n", lit_d); + pinst += sizeof(double); + break; + case LITERAL_INT: + memcpy((void *)&lit_i, ++pinst, sizeof(int)); + printf("\tInteger %d\n", lit_i); + pinst += sizeof(int); + break; + case MIN: + case MAX: + case FINITE: + case ISNAN: + printf("\t%s, %d arg(s)\n", opcodes[(int) op], *++pinst); + pinst++; + break; + default: + printf("\t%s\n", opcodes[(int) op]); + pinst++; + } + } +} diff --git a/src/libCom/calc/postfix.h b/src/libCom/calc/postfix.h new file mode 100644 index 000000000..a7a2a3cbe --- /dev/null +++ b/src/libCom/calc/postfix.h @@ -0,0 +1,88 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* postfix.h + * Original Author: Bob Dalesio + * Date: 9-21-88 + */ + +#ifndef INCpostfixh +#define INCpostfixh + +#include "shareLib.h" + +#define CALCPERFORM_NARGS 12 +#define CALCPERFORM_STACK 80 + +#define INFIX_TO_POSTFIX_SIZE(n) (n*21/6) +/* The above expression is an estimate of the maximum postfix buffer + * size needed for a given infix expression buffer. The actual size + * needed is never larger than this value, although it is actually a + * few bytes smaller for some sizes. + * + * The maximum expansion from infix to postfix is for the sub-expression + * .1?.1: + * which is 6 characters long and results in 21 bytes of postfix: + * .1 => LITERAL_DOUBLE + 8 byte value + * ? => COND_IF + * .1 => LITERAL_DOUBLE + 8 byte value + * : => COND_ELSE + * ... + * => COND_END + * For other short expressions the factor 21/6 always gives a big enough + * postfix buffer (proven by hand, look at '1+' and '.1+' as well). + */ + +/* These are not hard limits, just default sizes for the database */ +#define MAX_INFIX_SIZE 100 +#define MAX_POSTFIX_SIZE INFIX_TO_POSTFIX_SIZE(MAX_INFIX_SIZE) + + +/* Error numbers from postfix */ + +#define CALC_ERR_NONE 0 /* No error */ +#define CALC_ERR_TOOMANY 1 /* Too many results returned */ +#define CALC_ERR_BAD_LITERAL 2 /* Bad numeric literal */ +#define CALC_ERR_BAD_ASSIGNMENT 3 /* Bad assignment target */ +#define CALC_ERR_BAD_SEPERATOR 4 /* Comma without parentheses */ +#define CALC_ERR_PAREN_NOT_OPEN 5 /* Close parenthesis without open */ +#define CALC_ERR_PAREN_OPEN 6 /* Open parenthesis at end of expression */ +#define CALC_ERR_CONDITIONAL 7 /* Unbalanced conditional ?: operators */ +#define CALC_ERR_INCOMPLETE 8 /* Incomplete expression, operand missing */ +#define CALC_ERR_UNDERFLOW 9 /* Runtime stack would underflow */ +#define CALC_ERR_OVERFLOW 10 /* Runtime stack would overflow */ +#define CALC_ERR_SYNTAX 11 /* Syntax error */ +#define CALC_ERR_NULL_ARG 12 /* NULL or empty input argument */ +#define CALC_ERR_INTERNAL 13 /* Internal error, bad element type */ +/* Changes in the above errors must also be made in calcErrorStr() */ + + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc long + postfix(const char *pinfix, char *ppostfix, short *perror); + +epicsShareFunc long + calcPerform(double *parg, double *presult, const char *ppostfix); + +epicsShareFunc long + calcArgUsage(const char *ppostfix, unsigned long *pinputs, unsigned long *pstores); + +epicsShareFunc const char * + calcErrorStr(short error); + +epicsShareFunc void + calcExprDump(const char *pinst); + +#ifdef __cplusplus +} +#endif + +#endif /* INCpostfixh */ diff --git a/src/libCom/calc/postfixPvt.h b/src/libCom/calc/postfixPvt.h new file mode 100644 index 000000000..53efb32e0 --- /dev/null +++ b/src/libCom/calc/postfixPvt.h @@ -0,0 +1,104 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* postfixPvt.h + * Original Author: Bob Dalesio + * Date: 9-21-88 + */ + +/* Notes: + * 1. The FETCH_A through FETCH_L and STORE_A through STORE_L opcodes must + * be contiguous. + * 2. The LITERAL opcodes are followed by a binary representation of their + * values, but these are not aligned properly. + * 3. The var-arg functions MIN, MAX, FINITE and ISNAN are followed by + * a byte giving the number of arguments to process. + * 4. You can't use strlen() on an RPN buffer since the literal values + * can contain zero bytes. + */ + +#ifndef INCpostfixPvth +#define INCpostfixPvth + + +/* RPN opcodes */ +typedef enum { + END_EXPRESSION = 0, + /* Operands */ + LITERAL_DOUBLE, LITERAL_INT, FETCH_VAL, + FETCH_A, FETCH_B, FETCH_C, FETCH_D, FETCH_E, FETCH_F, + FETCH_G, FETCH_H, FETCH_I, FETCH_J, FETCH_K, FETCH_L, + /* Assignment */ + STORE_A, STORE_B, STORE_C, STORE_D, STORE_E, STORE_F, + STORE_G, STORE_H, STORE_I, STORE_J, STORE_K, STORE_L, + /* Trigonometry Constants */ + CONST_PI, + CONST_D2R, + CONST_R2D, + /* Arithmetic */ + UNARY_NEG, + ADD, + SUB, + MULT, + DIV, + MODULO, + POWER, + /* Algebraic */ + ABS_VAL, + EXP, + LOG_10, + LOG_E, + MAX, + MIN, + SQU_RT, + /* Trigonometric */ + ACOS, + ASIN, + ATAN, + ATAN2, + COS, + COSH, + SIN, + SINH, + TAN, + TANH, + /* Numeric */ + CEIL, + FLOOR, + FINITE, + ISINF, + ISNAN, + NINT, + RANDOM, + /* Boolean */ + REL_OR, + REL_AND, + REL_NOT, + /* Bitwise */ + BIT_OR, + BIT_AND, + BIT_EXCL_OR, + BIT_NOT, + RIGHT_SHIFT, + LEFT_SHIFT, + /* Relationals */ + NOT_EQ, + LESS_THAN, + LESS_OR_EQ, + EQUAL, + GR_OR_EQ, + GR_THAN, + /* Conditional */ + COND_IF, + COND_ELSE, + COND_END, + /* Misc */ + NOT_GENERATED +} rpn_opcode; + +#endif /* INCpostfixPvth */ diff --git a/src/libCom/cppStd/epicsAlgorithm.h b/src/libCom/cppStd/epicsAlgorithm.h new file mode 100644 index 000000000..20850242d --- /dev/null +++ b/src/libCom/cppStd/epicsAlgorithm.h @@ -0,0 +1,79 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// epicsAlgorithm.h +// Authors: Jeff Hill & Andrew Johnson + +#ifndef __EPICS_ALGORITHM_H__ +#define __EPICS_ALGORITHM_H__ + +#include "epicsMath.h" + +// The C++ standard only requires types to be less-than comparable, so +// the epicsMin and epicsMax templates only use operator < + +// epicsMin + +template +inline const T& epicsMin (const T& a, const T& b) +{ + return (b < a) ? b : a; +} + +// If b is a NaN the above template returns a, but should return NaN. +// These specializations ensure that epicsMin(x,NaN) == NaN + +template <> +inline const float& epicsMin (const float& a, const float& b) +{ + return (b < a) || isnan(b) ? b : a; +} + +template <> +inline const double& epicsMin (const double& a, const double& b) +{ + return (b < a) || isnan(b) ? b : a; +} + + +// epicsMax + +template +inline const T& epicsMax (const T& a, const T& b) +{ + return (a < b) ? b : a; +} + +// If b is a NaN the above template returns a, but should return NaN. +// These specializations ensure that epicsMax(x,NaN) == NaN + +template <> +inline const float& epicsMax (const float& a, const float& b) +{ + return (a < b) || isnan(b) ? b : a; +} + +template <> +inline const double& epicsMax (const double& a, const double& b) +{ + return (a < b) || isnan(b) ? b : a; +} + + +// epicsSwap + +template +inline void epicsSwap(T& a, T& b) +{ + T temp = a; + a = b; + b = temp; +} + +#endif // __EPICS_ALGORITHM_H__ diff --git a/src/libCom/cppStd/epicsExcept.h b/src/libCom/cppStd/epicsExcept.h new file mode 100644 index 000000000..291eb7eb5 --- /dev/null +++ b/src/libCom/cppStd/epicsExcept.h @@ -0,0 +1,72 @@ +/*************************************************************************\ +* 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: Andrew Johnson & Jeff Hill +// Date: December 2000 + +#ifndef __EPICS_EXCEPT_H__ +#define __EPICS_EXCEPT_H__ + +#define epicsThrowHere(exc) \ + throw locationException(exc, __FILE__, __LINE__) + +class sourceLocation { +public: // Functions + sourceLocation(const char *fileName, int lineNumber); +// sourceLocation(const sourceLocation&); Copy constructable +// sourceLocation& operator=(const sourceLocation&); Assignable + + const char *fileName() const; + int lineNumber() const; + +private: // Hide compiler-generated member functions + sourceLocation(); // default constructor + +private: // Data + const char *file; + int line; +}; + +template +class locationException : public T, public sourceLocation { +public: + locationException(const T& exc, const char *fileName, int lineNumber); +}; + + +/* Example: + * if (status) epicsThrowHere(std::logic_error("operation failed!")); + * try { ... } catch(sourceLocation& where) { ... } + */ + +// END OF DECLARATIONS + +// INLINE FUNCTIONS + +// sourceFileLocation +inline sourceLocation::sourceLocation (const char *fileName, int lineNumber) : + file(fileName), line(lineNumber) {} + +inline const char* sourceLocation::fileName () const { + return this->file; +} + +inline int sourceLocation::lineNumber () const { + return this->line; +} + +// locationException +template +inline locationException::locationException + (const char *fileName, int lineNumber, const E& exc) : + T(exc), sourceLocation(fileName, lineNumber) {} + + +#endif // __EPICS_EXCEPT_H__ diff --git a/src/libCom/cppStd/epicsMemory.h b/src/libCom/cppStd/epicsMemory.h new file mode 100644 index 000000000..49c9ebecc --- /dev/null +++ b/src/libCom/cppStd/epicsMemory.h @@ -0,0 +1,115 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// epicsMemoryH.h +// Author: Jeff Hill +// Date: March 2001 + +#ifndef epicsMemoryH +#define epicsMemoryH + +enum epics_auto_ptr_type { + eapt_scalar, eapt_array }; + +template < class T, epics_auto_ptr_type PT = eapt_scalar > +class epics_auto_ptr { +public: + typedef T element_type; + explicit epics_auto_ptr ( T * p = 0 ) throw (); + epics_auto_ptr ( epics_auto_ptr & rhs ) throw (); + ~epics_auto_ptr() throw (); + epics_auto_ptr & operator = ( epics_auto_ptr & rhs ) throw (); + T & operator * () const throw (); + T * operator -> () const throw (); + T & operator [] ( unsigned index ) const throw (); + T * get () const throw (); + T * release () throw (); + void reset ( T * p = 0 ) throw (); +private: + T * p; + void destroyTarget () throw (); +}; + +template < class T, epics_auto_ptr_type PT > +inline epics_auto_ptr::epics_auto_ptr ( T *pIn ) throw () : +p ( pIn ) {} + +template < class T, epics_auto_ptr_type PT > +inline epics_auto_ptr:: + epics_auto_ptr ( epics_auto_ptr & ap ) throw () : + p ( ap.release() ) {} + +template < class T, epics_auto_ptr_type PT > +inline void epics_auto_ptr::destroyTarget () throw () +{ + if ( PT == eapt_scalar ) { + delete this->p; + } + else { + delete [] this->p; + } +} + +template < class T, epics_auto_ptr_type PT > +inline epics_auto_ptr::~epics_auto_ptr () throw () +{ + this->destroyTarget (); +} + +template < class T, epics_auto_ptr_type PT > +inline epics_auto_ptr & epics_auto_ptr::operator = + ( epics_auto_ptr & rhs ) throw () +{ + if ( &rhs != this) { + this->destroyTarget (); + this->p = rhs.release (); + } + return *this; +} + +template < class T, epics_auto_ptr_type PT > +inline T & epics_auto_ptr::operator * () const throw() +{ + return * this->p; +} + +template < class T, epics_auto_ptr_type PT > +inline T * epics_auto_ptr::operator -> () const throw () +{ + return this->p; +} + +template < class T, epics_auto_ptr_type PT > +T & epics_auto_ptr::operator [] ( unsigned index ) const throw () +{ + return this->p [ index ]; +} + +template < class T, epics_auto_ptr_type PT > +inline T * epics_auto_ptr::get () const throw () +{ + return this->p; +} + +template < class T, epics_auto_ptr_type PT > +inline T * epics_auto_ptr::release () throw () +{ + T *pTmp = this->p; + this->p = 0; + return pTmp; +} + +template < class T, epics_auto_ptr_type PT > +inline void epics_auto_ptr::reset ( T * pIn ) throw () +{ + this->destroyTarget (); + this->p = pIn; +} + +#endif // ifndef epicsMemoryH diff --git a/src/libCom/cvtFast/cvtFast.c b/src/libCom/cvtFast/cvtFast.c new file mode 100644 index 000000000..3671fb88f --- /dev/null +++ b/src/libCom/cvtFast/cvtFast.c @@ -0,0 +1,596 @@ +/*************************************************************************\ +* 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*/ +/* Very efficient routines to convert numbers to strings + * Author: Bob Dalesio wrote cvtFloatToString (called FF_TO_STR) + * Code is same for cvtDoubleToString + * Marty Kraimer wrote cvtCharToString,cvtUcharToString + * cvtShortToString,cvtUshortToString, + * cvtLongToString, and cvtUlongToString + * Mark Anderson wrote cvtLongToHexString, cvtLongToOctalString, + * adopted cvt[Float/Double]ExpString and + * cvt[Float/Double]CompactString from fToEStr + * and fixed calls to gcvt + * + * Date: 12 January 1993 + * + */ + +#include +#include +#include +#include /* XPG2/XPG3/POSIX.1/FIPS151-1/ANSI-C */ + +#define epicsExportSharedSymbols +#include "cvtFast.h" +#include "epicsMath.h" + +/* + * This routine converts numbers less than 10,000,000. It defers to f_to_str for + * numbers requiring more than 8 places of precision. There are only eight decimal + */ +static epicsInt32 frac_multiplier[] = + {1,10,100,1000,10000,100000,1000000,10000000,100000000}; + +int epicsShareAPI cvtFloatToString( + float flt_value, + char *pstr_value, + unsigned short precision) +{ + unsigned short got_one,i; + epicsInt32 whole,iplace,number,fraction,fplace; + float ftemp; + char *startAddr; + + /* can this routine handle this conversion */ + if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) { + sprintf(pstr_value,"%12.5e",(double)flt_value); + return((int)strlen(pstr_value)); + } + startAddr = pstr_value; + + /* determine the sign */ + if (flt_value < 0){ + *pstr_value++ = '-'; + flt_value = -flt_value; + }; + + /* remove the whole number portion */ + whole = (epicsInt32)flt_value; + ftemp = flt_value - whole; + + /* multiplier to convert fractional portion to integer */ + fplace = frac_multiplier[precision]; + fraction = (epicsInt32)(ftemp * fplace * 10); + fraction = (fraction + 5) / 10; /* round up */ + + /* determine rounding into the whole number portion */ + if ((fraction / fplace) >= 1){ + whole++; + fraction -= fplace; + } + + /* whole numbers */ + got_one = 0; + for (iplace = 10000000; iplace >= 1; iplace /= 10){ + if (whole >= iplace){ + got_one = 1; + number = whole / iplace; + whole = whole - (number * iplace); + *pstr_value = number + '0'; + pstr_value++; + }else if (got_one){ + *pstr_value = '0'; + pstr_value++; + } + } + if (!got_one){ + *pstr_value = '0'; + pstr_value++; + } + + /* fraction */ + if (precision > 0){ + /* convert fractional portional to ASCII */ + *pstr_value = '.'; + pstr_value++; + for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){ + number = fraction / fplace; + fraction -= number * fplace; + *pstr_value = number + '0'; + pstr_value++; + } + } + *pstr_value = 0; + + return((int)(pstr_value - startAddr)); +} + +int epicsShareAPI cvtDoubleToString( + double flt_value, + char *pstr_value, + unsigned short precision) +{ + unsigned short got_one,i; + epicsInt32 whole,iplace,number,fraction,fplace; + double ftemp; + char *startAddr; + + /* can this routine handle this conversion */ + if (isnan(flt_value) || precision > 8 || flt_value > 10000000.0 || flt_value < -10000000.0) { + if (precision > 8 || flt_value > 1e16 || flt_value < -1e16) { + if(precision>17) precision=17; + sprintf(pstr_value,"%*.*e",precision+7,precision, + flt_value); + } else { + if(precision>3) precision=3; + sprintf(pstr_value,"%.*f",precision,flt_value); + } + return((int)strlen(pstr_value)); + } + startAddr = pstr_value; + + /* determine the sign */ + if (flt_value < 0){ + *pstr_value++ = '-'; + flt_value = -flt_value; + }; + + /* remove the whole number portion */ + whole = (epicsInt32)flt_value; + ftemp = flt_value - whole; + + /* multiplier to convert fractional portion to integer */ + fplace = frac_multiplier[precision]; + fraction = (epicsInt32)(ftemp * fplace * 10); + fraction = (fraction + 5) / 10; /* round up */ + + /* determine rounding into the whole number portion */ + if ((fraction / fplace) >= 1){ + whole++; + fraction -= fplace; + } + + /* whole numbers */ + got_one = 0; + for (iplace = 10000000; iplace >= 1; iplace /= 10){ + if (whole >= iplace){ + got_one = 1; + number = whole / iplace; + whole = whole - (number * iplace); + *pstr_value = number + '0'; + pstr_value++; + }else if (got_one){ + *pstr_value = '0'; + pstr_value++; + } + } + if (!got_one){ + *pstr_value = '0'; + pstr_value++; + } + + /* fraction */ + if (precision > 0){ + /* convert fractional portional to ASCII */ + *pstr_value = '.'; + pstr_value++; + for (fplace /= 10, i = precision; i > 0; fplace /= 10,i--){ + number = fraction / fplace; + fraction -= number * fplace; + *pstr_value = number + '0'; + pstr_value++; + } + } + *pstr_value = 0; + + return((int)(pstr_value - startAddr)); +} + +/* + * cvtFloatToExpString + * + * converts floating point numbers to E-format NULL terminated strings + */ +int epicsShareAPI cvtFloatToExpString( + float f_value, + char *pstr_value, + unsigned short f_precision) +{ + /*sunos uses char*sprint as function prototype*/ + sprintf(pstr_value,"%.*e",(int)f_precision,(double)f_value); + return((int)strlen(pstr_value)); +} + +/* + * cvtFloatToCompactString + * + * Converts floating point numbers to %g format NULL terminated strings, + * resulting in the most "compact" expression of the value + * ("f" notation if 10-4 < |value| < 10+4, otherwise "e" notation) + */ +int epicsShareAPI cvtFloatToCompactString( + float f_value, + char *pstr_value, + unsigned short f_precision ) +{ + if ((f_value < 1.e4 && f_value > 1.e-4) || + (f_value > -1.e4 && f_value < -1.e-4) || f_value == 0.0) { + return(cvtFloatToString(f_value,pstr_value,f_precision)); + } else { + return(cvtFloatToExpString(f_value,pstr_value,f_precision)); + } +} + + + +/* + * cvtDoubleToExpString + * + * converts double precision floating point numbers to E-format NULL + * terminated strings + */ + +int epicsShareAPI cvtDoubleToExpString( + double f_value, + char *pstr_value, + unsigned short f_precision ) +{ + sprintf(pstr_value,"%.*e",(int)f_precision,f_value); + return((int)strlen(pstr_value)); +} + + +/* + * cvtDoubleToCompactString + * + * Converts double precision floating point numbers to %g format NULL + * terminated strings, resulting in the most "compact" expression + * of the value ("f" notation if 10-4 < |value| < 10+4, otherwise + * "e" notation) + */ +int epicsShareAPI cvtDoubleToCompactString( + double f_value, + char *pstr_value, + unsigned short f_precision ) +{ + if ((f_value < 1.e4 && f_value > 1.e-4) || + (f_value > -1.e4 && f_value < -1.e-4) || f_value == 0.0) { + return(cvtDoubleToString(f_value,pstr_value,f_precision)); + } else { + return(cvtDoubleToExpString(f_value,pstr_value,f_precision)); + } +} + +/* Convert various integer types to ascii */ + +static char digit_to_ascii[10]={'0','1','2','3','4','5','6','7','8','9'}; + +int epicsShareAPI cvtCharToString( + signed char source, + char *pdest) +{ + unsigned char val,temp; + char digit[3]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + if(source<0) { + if(source == CHAR_MIN) { + sprintf(pdest,"%d",CHAR_MIN); + return((int)strlen(pdest)); + } + *pdest++ = '-'; + source = -source; + } + val = source; + for(i=0; val!=0; i++) { + temp = val/10; + digit[i] = digit_to_ascii[val - temp*10]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + +int epicsShareAPI cvtUcharToString( + unsigned char source, + char *pdest) +{ + unsigned char val,temp; + char digit[3]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + val = source; + for(i=0; val!=0; i++) { + temp = val/10; + digit[i] = digit_to_ascii[val - temp*10]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + +int epicsShareAPI cvtShortToString( + short source, + char *pdest) +{ + short val,temp; + char digit[6]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + if(source<0) { + if(source == SHRT_MIN) { + sprintf(pdest,"%d",SHRT_MIN); + return((int)(strlen(pdest))); + } + *pdest++ = '-'; + source = -source; + } + val = source; + for(i=0; val!=0; i++) { + temp = val/10; + digit[i] = digit_to_ascii[val - temp*10]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + +int epicsShareAPI cvtUshortToString( + unsigned short source, + char *pdest) +{ + unsigned short val,temp; + char digit[5]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + val = source; + for(i=0; val!=0; i++) { + temp = val/10; + digit[i] = digit_to_ascii[val - temp*10]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + +int epicsShareAPI cvtLongToString( + epicsInt32 source, + char *pdest) +{ + epicsInt32 val,temp; + char digit[11]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + if(source<0) { + if(source == INT_MIN) { + sprintf(pdest,"%d",source); + return((int)strlen(pdest)); + } + *pdest++ = '-'; + source = -source; + } + val = source; + for(i=0; val!=0; i++) { + temp = val/10; + digit[i] = digit_to_ascii[val - temp*10]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + +int epicsShareAPI cvtUlongToString( + epicsUInt32 source, + char *pdest) +{ + epicsUInt32 val,temp; + char digit[10]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + val = source; + for(i=0; val!=0; i++) { + temp = val/10; + digit[i] = digit_to_ascii[val - temp*10]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + +/* Convert hex digits to ascii */ + +static char hex_digit_to_ascii[16]={'0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f'}; + + +int epicsShareAPI cvtLongToHexString( + epicsInt32 source, + char *pdest) +{ + epicsInt32 val,temp; + char digit[10]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + if(source<0) { + if(source == INT_MIN) { + sprintf(pdest,"-0x%x",source); + return((int)strlen(pdest)); + } + *pdest++ = '-'; + source = -source; + } + *pdest++ = '0'; *pdest++ = 'x'; + val = source; + for(i=0; val!=0; i++) { + temp = val/16; + digit[i] = hex_digit_to_ascii[val - temp*16]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + +int epicsShareAPI cvtLongToOctalString( + epicsInt32 source, + char *pdest) +{ + epicsInt32 val,temp; + char digit[16]; + int i,j; + char *startAddr = pdest; + + if(source==0) { + *pdest++ = '0'; + *pdest = 0; + return((int)(pdest-startAddr)); + } + if(source<0) { + if(source == INT_MIN) { + sprintf(pdest,"-0%o",source); + return((int)strlen(pdest)); + } + *pdest++ = '-'; + source = -source; + } + *pdest++ = '0'; + val = source; + for(i=0; val!=0; i++) { + temp = val/8; + /* reuse digit_to_ascii since octal is a subset of decimal */ + digit[i] = digit_to_ascii[val - temp*8]; + val = temp; + } + for(j=i-1; j>=0; j--) { + *pdest++ = digit[j]; + } + *pdest = 0; + return((int)(pdest-startAddr)); +} + + + + +/* + * + * cvtBitsToUlong() + * + * extract a bit field from the source epicsUInt32 + */ +epicsUInt32 epicsShareAPI cvtBitsToUlong( +epicsUInt32 src, +unsigned bitFieldOffset, +unsigned bitFieldLength) +{ + epicsUInt32 mask; + + src = src >> bitFieldOffset; + + mask = (1< + +#include "epicsTypes.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * each of these functions return the number of characters "transmitted" + * (as in ANSI-C/POSIX.1/XPG3 sprintf() functions) + */ +epicsShareFunc int epicsShareAPI + cvtFloatToString(float value, char *pstring, unsigned short precision); +epicsShareFunc int epicsShareAPI + cvtDoubleToString(double value, char *pstring, unsigned short precision); +epicsShareFunc int epicsShareAPI + cvtFloatToExpString(float value, char *pstring, unsigned short precision); +epicsShareFunc int epicsShareAPI + cvtDoubleToExpString(double value, char *pstring, unsigned short precision); +epicsShareFunc int epicsShareAPI + cvtFloatToCompactString(float value, char *pstring, unsigned short precision); +epicsShareFunc int epicsShareAPI + cvtDoubleToCompactString(double value, char *pstring, unsigned short precision); +epicsShareFunc int epicsShareAPI + cvtCharToString(signed char value, char *pstring); +epicsShareFunc int epicsShareAPI + cvtUcharToString(unsigned char value, char *pstring); +epicsShareFunc int epicsShareAPI + cvtShortToString(short value, char *pstring); +epicsShareFunc int epicsShareAPI + cvtUshortToString(unsigned short value, char *pstring); +epicsShareFunc int epicsShareAPI + cvtLongToString(epicsInt32 value, char *pstring); +epicsShareFunc int epicsShareAPI + cvtUlongToString(epicsUInt32 value, char *pstring); +epicsShareFunc int epicsShareAPI + cvtLongToHexString(epicsInt32 value, char *pstring); +epicsShareFunc int epicsShareAPI + cvtLongToOctalString(epicsInt32 value, char *pstring); +epicsShareFunc epicsUInt32 epicsShareAPI cvtBitsToUlong( + epicsUInt32 src, + unsigned bitFieldOffset, + unsigned bitFieldLength); +epicsShareFunc epicsUInt32 epicsShareAPI cvtUlongToBits( + epicsUInt32 src, + epicsUInt32 dest, + unsigned bitFieldOffset, + unsigned bitFieldLength); + +#ifdef __cplusplus +} +#endif + +#endif /*INCcvtFasth*/ diff --git a/src/libCom/cxxTemplates/README b/src/libCom/cxxTemplates/README new file mode 100644 index 000000000..ba9a29bb8 --- /dev/null +++ b/src/libCom/cxxTemplates/README @@ -0,0 +1,57 @@ + +C++ templates: +tsSLList.h - type safe single linked list template +tsDLList.h - type safe double linked list template +resourceLib.h - hash table template +tsFreeeList.h - free list allocator / deallocator + +the test subdir contains examples + +Since I am using templates the linked lists are type safe +(no casting of pointers ala ellList and dllList). +Also, the node class in embedded in the item on the +list (more efficient use of pool). + +The file resourceLib.h provides a core hashing library +"resTable " where "itemClass" objects +are stored in the hash table and "idClass" is the data type +of the key for the hash table. The identifier class provides +the hash alg. I have provided simple string "stringId" and +unsigned integer "uintId" key types in resourceLib.h. It +is easy to implement a new key class. + +There are examples under cxxTemplate/test. The list/hashing +templates all depend on a particular inheritance hierarchy. +If the inheritance hierarchy is wrong nothing will compile. +For instance, in tsDLList.h the template data type "T" +must derive from tsDLNode. Likewise, in tsSLList.h +"T" must derive from tsSLNode. Likewise, in resourceLib.h +class "T" (the type stored in the hash table) must derive +from class "ID" (the hash table key type) and also derive from +tsSLNode. + +So far, the only confusion I have run into with templates has been: + +1) strange compiler messages - unrelated to cause of course - +when I get the class declaration order wrong (so that the +compiler has trouble instantiating the template). + +2) sun pro/dec/att compilers use a template database and +gnu/msvc++ compilers use explicit template instantiation. +Therefore blocks of code of this sort are required: + +#include "resourceLib.h" // template def +#include "resourceLib.cc" // template functions (that are not inline) +#if defined (EXPL_TEMPL) + // + // From Stroustrups's "The C++ Programming Language" + // Appendix A: r.14.9 + // + // This explicitly instantiates the template class's member + // functions into "templInst.o" + // + template class resTable; + template class resTable; +#endif + + diff --git a/src/libCom/cxxTemplates/epicsGuard.h b/src/libCom/cxxTemplates/epicsGuard.h new file mode 100644 index 000000000..9c4737a3c --- /dev/null +++ b/src/libCom/cxxTemplates/epicsGuard.h @@ -0,0 +1,115 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef epicsGuardh +#define epicsGuardh + +#ifndef assert // allow use of epicsAssert.h +# include +#endif + +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author: Jeffrey O. Hill + * + */ + +template < class T > class epicsGuardRelease; + +// Automatically applies and releases the mutex. +// This class is also useful in situations where +// C++ exceptions are possible. +template < class T > +class epicsGuard { +public: + epicsGuard ( T & ); + void assertIdenticalMutex ( const T & ) const; + ~epicsGuard (); +private: + T * _pTargetMutex; + epicsGuard ( const epicsGuard & ); + epicsGuard & operator = ( const epicsGuard & ); + friend class epicsGuardRelease < T >; +}; + +// Automatically releases and reapplies the mutex. +// This class is also useful in situations where +// C++ exceptions are possible. +template < class T > +class epicsGuardRelease { +public: + epicsGuardRelease ( epicsGuard < T > & ); + ~epicsGuardRelease (); +private: + epicsGuard < T > & _guard; + T * _pTargetMutex; + epicsGuardRelease ( const epicsGuardRelease & ); + epicsGuardRelease & operator = ( const epicsGuardRelease & ); +}; + +// same interface as epicsMutex +class epicsMutexNOOP { +public: + void lock (); + bool tryLock (); + void unlock (); + void show ( unsigned level ) const; +}; + +template < class T > +inline epicsGuard < T > :: epicsGuard ( T & mutexIn ) : + _pTargetMutex ( & mutexIn ) +{ + _pTargetMutex->lock (); +} + +template < class T > +inline epicsGuard < T > :: ~epicsGuard () +{ + _pTargetMutex->unlock (); +} + +template < class T > +inline void epicsGuard < T > :: assertIdenticalMutex ( + const T & mutexToVerify ) const +{ + assert ( _pTargetMutex == & mutexToVerify ); +} + +template < class T > +inline epicsGuardRelease < T > :: + epicsGuardRelease ( epicsGuard & guardIn ) : + _guard ( guardIn ), + _pTargetMutex ( guardIn._pTargetMutex ) +{ + // Setting the guard's _pTargetMutex to nill will + // allow assertIdenticalMutex to catch situations + // where a guard is being used and it has been + // released, and also situations where ~epicsGuard () + // runs and an epicsGuardRelease is still referencing + // the guard will be detected. + _guard._pTargetMutex = 0; + _pTargetMutex->unlock (); +} + +template < class T > +inline epicsGuardRelease < T > :: ~epicsGuardRelease () +{ + _pTargetMutex->lock (); + _guard._pTargetMutex = _pTargetMutex; +} + +inline void epicsMutexNOOP::lock () {} +inline bool epicsMutexNOOP::tryLock () { return true; } +inline void epicsMutexNOOP::unlock () {} +inline void epicsMutexNOOP::show ( unsigned ) const {} + +#endif // epicsGuardh diff --git a/src/libCom/cxxTemplates/epicsOnce.cpp b/src/libCom/cxxTemplates/epicsOnce.cpp new file mode 100644 index 000000000..c3814e1d5 --- /dev/null +++ b/src/libCom/cxxTemplates/epicsOnce.cpp @@ -0,0 +1,113 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeff Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsSingleton.h" +#include "epicsGuard.h" +#include "epicsOnce.h" +#include "tsFreeList.h" + +class epicsOnceImpl : public epicsOnce { +public: + epicsOnceImpl ( epicsOnceNotify & notifyIn ); + void * operator new ( size_t size ); + void operator delete ( void * pCadaver, size_t size ); +private: + epicsSingleton < epicsMutex > :: reference mutexRef; + epicsOnceNotify & notify; + bool onceFlag; + void destroy (); + void once (); + epicsOnceImpl ( epicsOnceImpl & ); // disabled + epicsOnceImpl & operator = ( epicsOnceImpl & ); // disabled + static epicsSingleton < epicsMutex > mutex; + static epicsSingleton < tsFreeList < class epicsOnceImpl, 16 > > freeList; +}; + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class epicsSingleton < epicsMutex >; +template class tsFreeList < class epicsOnceImpl, 16 >; +template class epicsSingleton < tsFreeList < class epicsOnceImpl, 16 > >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +epicsSingleton < epicsMutex > epicsOnceImpl::mutex; +epicsSingleton < tsFreeList < class epicsOnceImpl, 16 > > epicsOnceImpl::freeList; + +inline void * epicsOnceImpl::operator new ( size_t size ) +{ + epicsSingleton < tsFreeList < class epicsOnceImpl, 16 > > :: reference ref = + epicsOnceImpl::freeList.getReference (); + return ref->allocate ( size ); +} + +inline void epicsOnceImpl::operator delete ( void *pCadaver, size_t size ) +{ + epicsSingleton < tsFreeList < class epicsOnceImpl, 16 > > :: reference ref = + epicsOnceImpl::freeList.getReference (); + ref->release ( pCadaver, size ); +} + +inline epicsOnceImpl::epicsOnceImpl ( epicsOnceNotify & notifyIn ) : +mutexRef ( epicsOnceImpl::mutex.getReference() ), notify ( notifyIn ), onceFlag ( false ) +{ +} + +void epicsOnceImpl::once () +{ + epicsGuard < epicsMutex > guard ( *this->mutexRef ); + if ( ! this->onceFlag ) { + this->notify.initialize (); + this->onceFlag = true; + } +} + +void epicsOnceImpl::destroy () +{ + delete this; +} + +epicsOnce & epicsOnce::create ( epicsOnceNotify & notifyIn ) +{ + // free list throws exception in no memory situation + return * new epicsOnceImpl ( notifyIn ); +} + +epicsOnce::~epicsOnce () +{ +} + +epicsOnceNotify::~epicsOnceNotify () +{ +} + diff --git a/src/libCom/cxxTemplates/epicsOnce.h b/src/libCom/cxxTemplates/epicsOnce.h new file mode 100644 index 000000000..6ce49542a --- /dev/null +++ b/src/libCom/cxxTemplates/epicsOnce.h @@ -0,0 +1,46 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeff Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef epicsOnceh +#define epicsOnceh + +#include "shareLib.h" + +class epicsOnceNotify { +public: + epicsShareFunc virtual ~epicsOnceNotify (); + virtual void initialize () = 0; +}; + +class epicsOnce { +public: + epicsShareFunc static epicsOnce & create ( epicsOnceNotify & notifyIn ); + virtual ~epicsOnce (); // use destroy + virtual void once () = 0; // run notifyIn.initialize() once only + virtual void destroy () = 0; // destroy this object +}; + +#endif // epicsOnceh + diff --git a/src/libCom/cxxTemplates/epicsSingleton.h b/src/libCom/cxxTemplates/epicsSingleton.h new file mode 100644 index 000000000..c522b820e --- /dev/null +++ b/src/libCom/cxxTemplates/epicsSingleton.h @@ -0,0 +1,218 @@ +/*************************************************************************\ +* 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: Jeffrey O. Hill + * + */ + +#ifndef epicsSingleton_h +#define epicsSingleton_h + +#include + +#include "shareLib.h" +#include "epicsAssert.h" + +class epicsShareClass SingletonUntyped { +public: + SingletonUntyped (); + ~SingletonUntyped (); + typedef void * ( * PBuild ) (); + void incrRefCount ( PBuild ); + typedef void ( * PDestroy ) ( void * ); + void decrRefCount ( PDestroy ); + void * pInstance () const; +private: + void * _pInstance; + size_t _refCount; + SingletonUntyped ( const SingletonUntyped & ); + SingletonUntyped & operator = ( const SingletonUntyped & ); +}; + +// This class exists for the purpose of avoiding file scope +// object chicken and egg problems. It implements thread safe +// lazy initialization. To avoid locking overhead retain a +// copy of the epicsSingleton :: reference for future use. +template < class TYPE > +class epicsSingleton { +public: + class reference { + public: + reference ( epicsSingleton & ); + reference ( const reference & ); + ~reference (); + // this somewhat convoluted reference of the return + // type ref through the epicsSingleton template is + // required for the archaic Tornado gnu compiler + typename epicsSingleton < TYPE > :: reference & + operator = ( const reference & ); + TYPE * operator -> (); + const TYPE * operator -> () const; + TYPE & operator * (); + const TYPE & operator * () const; + private: + epicsSingleton * _pSingleton; + }; + friend class reference; + epicsSingleton () {} + // mutex lock/unlock pair overhead incured + // when either of these are called + reference getReference (); + const reference getReference () const; +private: + SingletonUntyped _singletonUntyped; + static void * _build (); + static void _destroy ( void * ); + epicsSingleton ( const epicsSingleton & ); + epicsSingleton & operator = ( const epicsSingleton & ); +}; + +template < class TYPE > +inline epicsSingleton < TYPE > :: reference :: + reference ( epicsSingleton & es ): + _pSingleton ( & es ) +{ + es._singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); +} + +template < class TYPE > +inline epicsSingleton < TYPE > :: reference :: + reference ( const reference & ref ) : + _pSingleton ( ref._pSingleton ) +{ + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); +} + +template < class TYPE > +inline epicsSingleton < TYPE > :: reference :: + ~reference () +{ + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + decrRefCount ( & epicsSingleton < TYPE > :: _destroy ); +} + +template < class TYPE > +typename epicsSingleton < TYPE > :: reference & + epicsSingleton < TYPE > :: reference :: + operator = ( const reference & ref ) +{ + if ( _pSingleton != ref._pSingleton ) { + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + decrRefCount ( epicsSingleton < TYPE > :: _destroy ); + _pSingleton = ref._pSingleton; + assert ( _pSingleton ); + _pSingleton->_singletonUntyped. + incrRefCount ( & epicsSingleton < TYPE > :: _build ); + } + return *this; +} + +template < class TYPE > +inline TYPE * + epicsSingleton < TYPE > :: reference :: + operator -> () +{ + assert ( _pSingleton ); + return reinterpret_cast < TYPE * > + ( _pSingleton->_singletonUntyped.pInstance () ); +} + +template < class TYPE > +inline const TYPE * + epicsSingleton < TYPE > :: reference :: + operator -> () const +{ + assert ( _pSingleton ); + return reinterpret_cast < const TYPE * > + ( _pSingleton->_singletonUntyped.pInstance () ); +} + +template < class TYPE > +inline TYPE & + epicsSingleton < TYPE > :: reference :: + operator * () +{ + return * this->operator -> (); +} + +template < class TYPE > +inline const TYPE & + epicsSingleton < TYPE > :: reference :: + operator * () const +{ + return * this->operator -> (); +} + +inline SingletonUntyped :: SingletonUntyped () : + _pInstance ( 0 ), _refCount ( 0 ) +{ +} + +inline void * SingletonUntyped :: pInstance () const +{ + return _pInstance; +} + +inline SingletonUntyped :: ~SingletonUntyped () +{ + // we dont assert fail on non-zero _refCount + // and or non nill _pInstance here because this + // is designed to tolarate situations where + // file scope epicsSingleton objects (which + // theoretically dont have storage lifespan + // issues) are deleted in a non-determanistic + // order +# if 0 + assert ( _refCount == 0 ); + assert ( _pInstance == 0 ); +# endif +} + +template < class TYPE > +void * epicsSingleton < TYPE > :: _build () +{ + return new TYPE (); +} + +template < class TYPE > +void epicsSingleton < TYPE > :: + _destroy ( void * pDestroyTypeless ) +{ + TYPE * pDestroy = + reinterpret_cast < TYPE * > ( pDestroyTypeless ); + delete pDestroy; +} + +template < class TYPE > +inline typename epicsSingleton < TYPE > :: reference + epicsSingleton < TYPE > :: getReference () +{ + return reference ( * this ); +} + +template < class TYPE > +inline const typename epicsSingleton < TYPE > :: reference + epicsSingleton < TYPE > :: getReference () const +{ + epicsSingleton < TYPE > * pConstCastAway = + const_cast < epicsSingleton < TYPE > * > ( this ); + return pConstCastAway->getReference (); +} + +#endif // epicsSingleton_h + diff --git a/src/libCom/cxxTemplates/epicsSingletonBase.cpp b/src/libCom/cxxTemplates/epicsSingletonBase.cpp new file mode 100644 index 000000000..40418937c --- /dev/null +++ b/src/libCom/cxxTemplates/epicsSingletonBase.cpp @@ -0,0 +1,62 @@ +/*************************************************************************\ +* 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: Jeffrey O. Hill + * + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsThread.h" +#include "epicsSingleton.h" +#include "epicsExit.h" + +static epicsThreadOnceId epicsSingletonOnceId = EPICS_THREAD_ONCE_INIT; +static epicsMutex *pSingletonBaseMutexEPICS; + +static void epicsSingletonCleanup (void *) +{ + delete pSingletonBaseMutexEPICS; +} + +static void epicsSingletonOnce ( void * ) +{ + pSingletonBaseMutexEPICS = new epicsMutex; + epicsAtExit ( epicsSingletonCleanup,0 ); +} + +epicsSingletonBase::epicsSingletonBase () : pSingleton ( 0 ) +{ +} + +void * epicsSingletonBase::singletonPointer () const +{ + return this->pSingleton; +} + +epicsSingletonBase::~epicsSingletonBase () +{ +} + +void epicsSingletonBase::lockedFactory () +{ + if ( ! this->pSingleton ) { + epicsThreadOnce ( & epicsSingletonOnceId, epicsSingletonOnce, 0 ); + epicsGuard < epicsMutex > guard ( *pSingletonBaseMutexEPICS ); + if ( ! this->pSingleton ) { + this->pSingleton = this->factory (); + } + } +} diff --git a/src/libCom/cxxTemplates/epicsSingletonMutex.cpp b/src/libCom/cxxTemplates/epicsSingletonMutex.cpp new file mode 100644 index 000000000..6ee2cf7ff --- /dev/null +++ b/src/libCom/cxxTemplates/epicsSingletonMutex.cpp @@ -0,0 +1,63 @@ +/*************************************************************************\ +* 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 O. Hill + * + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsGuard.h" +#include "epicsThread.h" +#include "epicsSingleton.h" + +#ifndef SIZE_MAX +# define SIZE_MAX UINT_MAX +#endif + +static epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT ); +static epicsMutex * pEPICSSigletonMutex = 0; + +extern "C" void SingletonMutexOnce ( void * /* pParm */ ) +{ + // This class exists for the purpose of avoiding file scope + // object chicken and egg problems. Therefore, pEPICSSigletonMutex + // is never destroyed. + pEPICSSigletonMutex = new epicsMutex; +} + +void SingletonUntyped :: incrRefCount ( PBuild pBuild ) +{ + epicsThreadOnce ( & epicsSigletonOnceFlag, SingletonMutexOnce, 0 ); + epicsGuard < epicsMutex > + guard ( *pEPICSSigletonMutex ); + assert ( _refCount < SIZE_MAX ); + if ( _refCount == 0 ) { + _pInstance = ( * pBuild ) (); + } + _refCount++; +} + +void SingletonUntyped :: decrRefCount ( PDestroy pDestroy ) +{ + assert ( _refCount > 0 ); + epicsGuard < epicsMutex > + guard ( *pEPICSSigletonMutex ); + _refCount--; + if ( _refCount == 0 ) { + ( *pDestroy ) ( _pInstance ); + _pInstance = 0; + } +} diff --git a/src/libCom/cxxTemplates/resourceLib.cpp b/src/libCom/cxxTemplates/resourceLib.cpp new file mode 100644 index 000000000..1a01e36da --- /dev/null +++ b/src/libCom/cxxTemplates/resourceLib.cpp @@ -0,0 +1,33 @@ +/*************************************************************************\ +* 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 is 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 + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +#include "resourceLib.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class intId < unsigned, 8u, sizeof(unsigned)*CHAR_BIT >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif diff --git a/src/libCom/cxxTemplates/resourceLib.h b/src/libCom/cxxTemplates/resourceLib.h new file mode 100644 index 000000000..b98a6fd32 --- /dev/null +++ b/src/libCom/cxxTemplates/resourceLib.h @@ -0,0 +1,1172 @@ +/*************************************************************************\ +* 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 is 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 + * + * General hash table templates for fast indexing of resources + * of any base resource type and any resource identifier type. Fast + * indexing is implemented with a hash lookup. The identifier type + * implements the hash algorithm (or derives from one of the supplied + * identifier types which provide a hashing routine). The table expands + * dynamically depending on load, and without introducing non-determanistic + * latency. + * + * Unsigned integer and string identifier classes are supplied here. + * + * Authors Jeffrey O. Hill + * Marty Kraimer (string hash algorithm) + * influenced by papers by Peter K. Pearson and Per-Ake Larson + * + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef INCresourceLibh +#define INCresourceLibh + +#include +#include + +#include +#include +#include +#include +#ifndef assert // allow use of epicsAssert.h +#include +#endif + +#include "tsSLList.h" +#include "epicsString.h" +#include "shareLib.h" +typedef size_t resTableIndex; + +template < class T, class ID > class resTableIter; +template < class T, class ID > class resTableIterConst; + +// +// class resTable +// +// This class stores resource entries of type T which can be efficiently +// located with a hash key of type ID. +// +// NOTES: +// 1) class T must derive from class ID and also from class tsSLNode +// +// 2) If the "resTable::show (unsigned level)" member function is called then +// class T must also implement a "show (unsigned level)" member function which +// dumps increasing diagnostics information with increasing "level" to +// standard out. +// +// 3) Classes of type ID must implement the following member functions: +// +// // equivalence test +// bool operator == (const ID &); +// +// // ID to hash index convert (see examples below) +// resTableIndex hash (unsigned nBitsHashIndex) const; +// +// 4) Storage for identifier of type ID must persist until the item of type +// T is deleted from the resTable +// +template +class resTable { +public: + resTable (); + virtual ~resTable(); + // Call " void T::show (unsigned level)" for each entry + void show ( unsigned level ) const; + void verify () const; + int add ( T & res ); // returns -1 (id exists in table), 0 (success) + T * remove ( const ID &idIn ); // remove entry + void removeAll ( tsSLList & destination ); // remove all entries + T * lookup ( const ID &idIn ) const; // locate entry + // Call (pT->*pCB) () for each entry but expect poor performance + // with sparcely populated tables + void traverse ( void (T::*pCB)() ); + void traverseConst ( void (T::*pCB)() const ) const; + unsigned numEntriesInstalled () const; + void setTableSize ( const unsigned newTableSize ); + // iterate through all entries but expect poor performance + // with sparcely populated tables + typedef resTableIter < T, ID > iterator; + typedef resTableIterConst < T, ID > iteratorConst; + iterator firstIter (); + iteratorConst firstIter () const; +private: + tsSLList < T > * pTable; + unsigned nextSplitIndex; + unsigned hashIxMask; + unsigned hashIxSplitMask; + unsigned nBitsHashIxSplitMask; + unsigned logBaseTwoTableSize; + unsigned nInUse; + resTableIndex hash ( const ID & idIn ) const; + T * find ( tsSLList & list, const ID & idIn ) const; + void splitBucket (); + unsigned tableSize () const; + bool setTableSizePrivate ( unsigned logBaseTwoTableSize ); + resTable ( const resTable & ); + resTable & operator = ( const resTable & ); + static unsigned resTableBitMask ( const unsigned nBits ); + friend class resTableIter < T, ID >; + friend class resTableIterConst < T, ID >; +}; + +// +// class resTableIter +// +// an iterator for the resource table class +// +template < class T, class ID > +class resTableIter { +public: + resTableIter (); + bool valid () const; + bool operator == ( const resTableIter < T,ID > & rhs ) const; + bool operator != ( const resTableIter < T,ID > & rhs ) const; + resTableIter < T, ID > & operator = ( const resTableIter < T, ID > & ); + T & operator * () const; + T * operator -> () const; + resTableIter < T, ID > & operator ++ (); + resTableIter < T, ID > operator ++ ( int ); + T * pointer (); +private: + tsSLIter < T > iter; + unsigned index; + resTable < T,ID > * pResTable; + resTableIter ( resTable < T,ID > & tableIn ); + void findNextEntry (); + friend class resTable < T, ID >; +}; + +// +// class resTableIterConst +// +// an iterator for a const resource table class +// +template < class T, class ID > +class resTableIterConst { +public: + resTableIterConst (); + bool valid () const; + bool operator == ( const resTableIterConst < T,ID > & rhs ) const; + bool operator != ( const resTableIterConst < T,ID > & rhs ) const; + resTableIterConst < T, ID > & operator = ( const resTableIterConst < T, ID > & ); + const T & operator * () const; + const T * operator -> () const; + resTableIterConst < T, ID > & operator ++ (); + resTableIterConst < T, ID > operator ++ ( int ); + const T * pointer () const; +private: + tsSLIterConst < T > iter; + unsigned index; + const resTable < T,ID > * pResTable; + resTableIterConst ( const resTable < T,ID > & tableIn ); + void findNextEntry (); + friend class resTable < T, ID >; +}; + +// +// Some ID classes that work with the above template +// + +// +// class intId +// +// signed or unsigned integer identifier (class T must be +// a signed or unsigned integer type) +// +// this class works as type ID in resTable +// +// 1<. +// Set this parameter to zero if unsure of the correct minimum +// hash table size. +// +// MAX_ID_WIDTH specifies the maximum number of ls bits in an +// integer identifier which might be set at any time. +// +// MIN_INDEX_WIDTH and MAX_ID_WIDTH are specified here at +// compile time so that the hash index can be produced +// efficiently. Hash indexes are produced more efficiently +// when (MAX_ID_WIDTH - MIN_INDEX_WIDTH) is minimized. +// +template +class intId { +public: + intId (const T &idIn); + bool operator == (const intId &idIn) const; + resTableIndex hash () const; + const T getId() const; +protected: + T id; +}; + +// +// class chronIntIdResTable +// +// a specialized resTable which uses unsigned integer keys which are +// allocated in chronological sequence +// +// NOTE: ITEM must public inherit from chronIntIdRes +// +class chronIntId : public intId +{ +public: + chronIntId ( const unsigned &idIn ); +}; + +template +class chronIntIdResTable : public resTable { +public: + chronIntIdResTable (); + virtual ~chronIntIdResTable (); + void idAssignAdd ( ITEM & item ); +private: + unsigned allocId; + chronIntIdResTable ( const chronIntIdResTable & ); + chronIntIdResTable & operator = ( const chronIntIdResTable & ); +}; + +// +// class chronIntIdRes +// +// resource with unsigned chronological identifier +// +template +class chronIntIdRes : public chronIntId, public tsSLNode { +public: + chronIntIdRes (); +private: + void setId (unsigned newId); + chronIntIdRes (const chronIntIdRes & ); + friend class chronIntIdResTable; +}; + +// +// class stringId +// +// character string identifier +// +class epicsShareClass stringId { +public: + enum allocationType {copyString, refString}; + stringId (const char * idIn, allocationType typeIn=copyString); + virtual ~stringId(); + resTableIndex hash () const; + bool operator == (const stringId &idIn) const; + const char * resourceName() const; // return the pointer to the string + void show (unsigned level) const; +private: + stringId & operator = ( const stringId & ); + stringId ( const stringId &); + const char * pStr; + const allocationType allocType; +}; + +///////////////////////////////////////////////// +// resTable member functions +///////////////////////////////////////////////// + +// +// resTable::resTable () +// +template +inline resTable::resTable () : + pTable ( 0 ), nextSplitIndex ( 0 ), hashIxMask ( 0 ), + hashIxSplitMask ( 0 ), nBitsHashIxSplitMask ( 0 ), + logBaseTwoTableSize ( 0 ), nInUse ( 0 ) {} + +template +inline unsigned resTable::resTableBitMask ( const unsigned nBits ) +{ + return ( 1 << nBits ) - 1; +} + +// +// resTable::remove () +// +// remove a res from the resTable +// +template +T * resTable::remove ( const ID & idIn ) // X aCC 361 +{ + if ( this->pTable ) { + // search list for idIn and remove the first match + tsSLList & list = this->pTable [ this->hash(idIn) ]; + tsSLIter pItem = list.firstIter (); + T *pPrev = 0; + while ( pItem.valid () ) { + const ID & idOfItem = *pItem; + if ( idOfItem == idIn ) { + if ( pPrev ) { + list.remove ( *pPrev ); + } + else { + list.get (); + } + this->nInUse--; + break; + } + pPrev = pItem.pointer (); + pItem++; + } + return pItem.pointer (); + } + else { + return 0; + } +} + +template +void resTable::removeAll ( tsSLList & destination ) +{ + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + while ( T * pItem = this->pTable[i].get() ) { + destination.add ( *pItem ); + } + } + this->nInUse = 0; +} + +// +// resTable::lookup () +// +template +inline T * resTable::lookup ( const ID & idIn ) const // X aCC 361 +{ + if ( this->pTable ) { + tsSLList & list = this->pTable [ this->hash ( idIn ) ]; + return this->find ( list, idIn ); + } + else { + return 0; + } +} + +// +// resTable::hash () +// +template +inline resTableIndex resTable::hash ( const ID & idIn ) const +{ + resTableIndex h = idIn.hash (); + resTableIndex h0 = h & this->hashIxMask; + if ( h0 >= this->nextSplitIndex ) { + return h0; + } + return h & this->hashIxSplitMask; +} + +// +// resTable::show +// +template +void resTable::show ( unsigned level ) const +{ + const unsigned N = this->tableSize (); + + printf ( "Hash table with %u buckets and %u items of type %s installed\n", + N, this->nInUse, typeid(T).name() ); + + if ( level >= 1u && N ) { + + if ( level >= 2u ) { + tsSLList * pList = this->pTable; + while ( pList < & this->pTable[N] ) { + tsSLIter pItem = pList->firstIter (); + while ( pItem.valid () ) { + tsSLIter pNext = pItem; + pNext++; + pItem.pointer()->show ( level - 2u ); + pItem = pNext; + } + pList++; + } + } + + double X = 0.0; + double XX = 0.0; + unsigned maxEntries = 0u; + unsigned empty = 0; + for ( unsigned i = 0u; i < N; i++ ) { + tsSLIter pItem = this->pTable[i].firstIter (); + unsigned count = 0; + while ( pItem.valid () ) { + if ( level >= 3u ) { + pItem->show ( level ); + } + count++; + pItem++; + } + if ( count > 0u ) { + X += count; + XX += count * count; + if ( count > maxEntries ) { + maxEntries = count; + } + } else + empty++; + } + + double mean = X / N; + double stdDev = sqrt( XX / N - mean * mean ); + printf ( + "entries per bucket: mean = %f std dev = %f max = %u\n", + mean, stdDev, maxEntries ); + printf("%u empty buckets\n", empty); + if ( X != this->nInUse ) { + printf ("this->nInUse didnt match items counted which was %f????\n", X ); + } + } +} + +// self test +template +void resTable::verify () const +{ + const unsigned N = this->tableSize (); + + if ( this->pTable ) { + assert ( this->nextSplitIndex <= this->hashIxMask + 1 ); + assert ( this->hashIxMask ); + assert ( this->hashIxMask == ( this->hashIxSplitMask >> 1 ) ); + assert ( this->hashIxSplitMask ); + assert ( this->nBitsHashIxSplitMask ); + assert ( resTableBitMask ( this->nBitsHashIxSplitMask ) + == this->hashIxSplitMask ); + assert ( this->logBaseTwoTableSize ); + assert ( this->nBitsHashIxSplitMask <= this->logBaseTwoTableSize ); + } + else { + assert ( this->nextSplitIndex == 0 ); + assert ( this->hashIxMask == 0 ); + assert ( this->hashIxSplitMask == 0 ); + assert ( this->nBitsHashIxSplitMask == 0 ); + assert ( this->logBaseTwoTableSize == 0 ); + } + + unsigned total = 0u; + for ( unsigned i = 0u; i < N; i++ ) { + tsSLIter pItem = this->pTable[i].firstIter (); + unsigned count = 0; + while ( pItem.valid () ) { + resTableIndex index = this->hash ( *pItem ); + assert ( index == i ); + count++; + pItem++; + } + total += count; + } + assert ( total == this->nInUse ); +} + + +// +// resTable::traverse +// +template +void resTable::traverse ( void (T::*pCB)() ) +{ + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + tsSLIter pItem = this->pTable[i].firstIter (); + while ( pItem.valid () ) { + tsSLIter pNext = pItem; + pNext++; + ( pItem.pointer ()->*pCB ) (); + pItem = pNext; + } + } +} + +// +// resTable::traverseConst +// +template +void resTable::traverseConst ( void (T::*pCB)() const ) const +{ + const unsigned N = this->tableSize (); + for ( unsigned i = 0u; i < N; i++ ) { + const tsSLList < T > & table = this->pTable[i]; + tsSLIterConst pItem = table.firstIter (); + while ( pItem.valid () ) { + tsSLIterConst pNext = pItem; + pNext++; + ( pItem.pointer ()->*pCB ) (); + pItem = pNext; + } + } +} + +template +inline unsigned resTable::numEntriesInstalled () const +{ + return this->nInUse; +} + +template +inline unsigned resTable::tableSize () const // X aCC 361 +{ + if ( this->pTable ) { + return ( this->hashIxMask + 1 ) + this->nextSplitIndex; + } + else { + return 0; + } +} + +// it will be more efficent to call this once prior to installing +// the first entry +template +void resTable::setTableSize ( const unsigned newTableSize ) +{ + if ( newTableSize == 0u ) { + return; + } + + // + // count the number of bits in newTableSize and round up + // to the next power of two + // + unsigned newMask = newTableSize - 1; + unsigned nbits; + for ( nbits = 0; nbits < sizeof (newTableSize) * CHAR_BIT; nbits++ ) { + unsigned nBitsMask = resTableBitMask ( nbits ); + if ( ( newMask & ~nBitsMask ) == 0){ + break; + } + } + setTableSizePrivate ( nbits ); +} + +template +bool resTable::setTableSizePrivate ( unsigned logBaseTwoTableSizeIn ) +{ + // dont shrink + if ( this->logBaseTwoTableSize >= logBaseTwoTableSizeIn ) { + return true; + } + + // dont allow ridiculously small tables + if ( logBaseTwoTableSizeIn < 4 ) { + logBaseTwoTableSizeIn = 4; + } + + const unsigned newTableSize = 1 << logBaseTwoTableSizeIn; +# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) + const unsigned oldTableSize = this->pTable ? 1 << this->logBaseTwoTableSize : 0; +# endif + const unsigned oldTableOccupiedSize = this->tableSize (); + + tsSLList * pNewTable; + try { + pNewTable = ( tsSLList * ) + ::operator new ( newTableSize * sizeof ( tsSLList ) ); + } + catch ( ... ){ + if ( ! this->pTable ) { + throw; + } + return false; + } + + // run the constructors using placement new + unsigned i; + for ( i = 0u; i < oldTableOccupiedSize; i++ ) { + new ( &pNewTable[i] ) tsSLList ( this->pTable[i] ); + } + for ( i = oldTableOccupiedSize; i < newTableSize; i++ ) { + new ( &pNewTable[i] ) tsSLList; + } + // Run the destructors explicitly. Currently this destructor is a noop. + // The Tornado II compiler and RedHat 6.2 will not compile ~tsSLList() but + // since its a NOOP we can find an ugly workaround +# if ! defined (__GNUC__) || __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 92 ) + for ( i = 0; i < oldTableSize; i++ ) { + this->pTable[i].~tsSLList(); + } +# endif + + if ( ! this->pTable ) { + this->hashIxSplitMask = resTableBitMask ( logBaseTwoTableSizeIn ); + this->nBitsHashIxSplitMask = logBaseTwoTableSizeIn; + this->hashIxMask = this->hashIxSplitMask >> 1; + this->nextSplitIndex = 0; + } + + operator delete ( this->pTable ); + this->pTable = pNewTable; + this->logBaseTwoTableSize = logBaseTwoTableSizeIn; + + return true; +} + +template +void resTable::splitBucket () +{ + // double the hash table when necessary + // (this results in only a memcpy overhead, but + // no hashing or entry redistribution) + if ( this->nextSplitIndex > this->hashIxMask ) { + bool success = this->setTableSizePrivate ( this->nBitsHashIxSplitMask + 1 ); + if ( ! success ) { + return; + } + this->nBitsHashIxSplitMask += 1; + this->hashIxSplitMask = resTableBitMask ( this->nBitsHashIxSplitMask ); + this->hashIxMask = this->hashIxSplitMask >> 1; + this->nextSplitIndex = 0; + } + + // rehash only the items in the split bucket + tsSLList tmp ( this->pTable[ this->nextSplitIndex ] ); + this->nextSplitIndex++; + T *pItem = tmp.get(); + while ( pItem ) { + resTableIndex index = this->hash ( *pItem ); + this->pTable[index].add ( *pItem ); + pItem = tmp.get(); + } +} + +// +// add a res to the resTable +// +template +int resTable::add ( T &res ) +{ + if ( ! this->pTable ) { + this->setTableSizePrivate ( 10 ); + } + else if ( this->nInUse >= this->tableSize() ) { + this->splitBucket (); + tsSLList &list = this->pTable[this->hash(res)]; + if ( this->find ( list, res ) != 0 ) { + return -1; + } + } + tsSLList &list = this->pTable[this->hash(res)]; + if ( this->find ( list, res ) != 0 ) { + return -1; + } + list.add ( res ); + this->nInUse++; + return 0; +} + +// +// find +// searches from where the iterator points to the +// end of the list for idIn +// +// iterator points to the item found upon return +// (or NULL if nothing matching was found) +// +template +T * resTable::find ( tsSLList &list, const ID &idIn ) const +{ + tsSLIter pItem = list.firstIter (); + while ( pItem.valid () ) { + const ID & idOfItem = *pItem; + if ( idOfItem == idIn ) { + break; + } + pItem++; + } + return pItem.pointer (); +} + +// +// ~resTable::resTable() +// +template +resTable::~resTable() +{ + operator delete ( this->pTable ); +} + +// +// resTable::resTable ( const resTable & ) +// private - not to be used - implemented to eliminate warnings +// +template +inline resTable::resTable ( const resTable & ) +{ +} + +// +// resTable::resTable & operator = ( const resTable & ) +// private - not to be used - implemented to eliminate warnings +// +template +inline resTable & resTable::operator = ( const resTable & ) +{ + return *this; +} + +template +inline resTableIterConst < T, ID > resTable::firstIter () const +{ + return resTableIterConst < T, ID > ( *this ); +} + +template +inline resTableIter < T, ID > resTable::firstIter () +{ + return resTableIter < T, ID > ( *this ); +} + +////////////////////////////////////////////// +// resTableIter member functions +////////////////////////////////////////////// + +template < class T, class ID > +inline resTableIter::resTableIter ( resTable < T,ID > & tableIn ) : + index ( 0 ), pResTable ( & tableIn ) +{ + this->findNextEntry (); +} + +template < class T, class ID > +inline resTableIter::resTableIter () : + iter ( tsSLList::invalidIter() ), + index ( 0 ), pResTable ( 0 ) +{ +} + +template < class T, class ID > +inline void resTableIter::findNextEntry () +{ + if ( this->pResTable ) { + while ( this->index < this->pResTable->tableSize() ) { + this->iter = this->pResTable->pTable[this->index++].firstIter (); + if ( this->iter.valid () ) { + break; + } + } + } +} + +template < class T, class ID > +inline bool resTableIter::valid () const +{ + return this->iter.valid (); +} + +template < class T, class ID > +inline bool resTableIter::operator == + ( const resTableIter < T,ID > & rhs ) const +{ + return ( this->pResTable == rhs.pResTable + && this->index == rhs.index + && this->iter == rhs.iter ); +} + +template < class T, class ID > +inline bool resTableIter::operator != + ( const resTableIter < T,ID > & rhs ) const +{ + return ! this->operator == ( rhs ); +} + +template < class T, class ID > +inline resTableIter < T, ID > & resTableIter::operator = + ( const resTableIter < T, ID > & rhs ) +{ + this->pResTable = rhs.pResTable; + this->index = rhs.index; + this->iter = rhs.iter; + return *this; +} + +template < class T, class ID > +inline T & resTableIter::operator * () const +{ + return this->iter.operator * (); +} + +template < class T, class ID > +inline T * resTableIter::operator -> () const +{ + return this->iter.operator -> (); +} + +template < class T, class ID > +inline resTableIter & resTableIter::operator ++ () +{ + this->iter++; + if ( ! this->iter.valid() ) { + this->findNextEntry (); + } + return *this; +} + +template < class T, class ID > +inline resTableIter resTableIter::operator ++ ( int ) +{ + resTableIter tmp = *this; + this->operator ++ (); + return tmp; +} + +template < class T, class ID > +inline T * resTableIter::pointer () +{ + return this->iter.pointer (); +} + +////////////////////////////////////////////// +// resTableIterConst member functions +////////////////////////////////////////////// + +template < class T, class ID > +inline resTableIterConst::resTableIterConst ( const resTable < T,ID > & tableIn ) : + index ( 0 ), pResTable ( & tableIn ) +{ + this->findNextEntry (); +} + +template < class T, class ID > +inline resTableIterConst::resTableIterConst () : + iter ( tsSLList::invalidIter() ), + index ( 0 ), pResTable ( 0 ) +{ +} + +template < class T, class ID > +inline void resTableIterConst::findNextEntry () +{ + if ( this->pResTable ) { + while ( this->index < this->pResTable->tableSize() ) { + const tsSLList * pList = & this->pResTable->pTable[this->index++]; + this->iter = pList->firstIter (); + if ( this->iter.valid () ) { + break; + } + } + } +} + +template < class T, class ID > +inline bool resTableIterConst::valid () const +{ + return this->iter.valid (); +} + +template < class T, class ID > +inline bool resTableIterConst::operator == + ( const resTableIterConst < T,ID > & rhs ) const +{ + return ( this->pResTable == rhs.pResTable + && this->index == rhs.index + && this->iter == rhs.iter ); +} + +template < class T, class ID > +inline bool resTableIterConst::operator != + ( const resTableIterConst < T,ID > & rhs ) const +{ + return ! this->operator == ( rhs ); +} + +template < class T, class ID > +inline resTableIterConst < T, ID > & resTableIterConst::operator = + ( const resTableIterConst < T, ID > & rhs ) +{ + this->pResTable = rhs.pResTable; + this->index = rhs.index; + this->iter = rhs.iter; + return *this; +} + +template < class T, class ID > +inline const T & resTableIterConst::operator * () const +{ + return this->iter.operator * (); +} + +template < class T, class ID > +inline const T * resTableIterConst::operator -> () const +{ + return this->iter.operator -> (); +} + +template < class T, class ID > +inline resTableIterConst & resTableIterConst::operator ++ () +{ + this->iter++; + if ( ! this->iter.valid() ) { + this->findNextEntry (); + } + return *this; +} + +template < class T, class ID > +inline resTableIterConst resTableIterConst::operator ++ ( int ) +{ + resTableIterConst tmp = *this; + this->operator ++ (); + return tmp; +} + +template < class T, class ID > +inline const T * resTableIterConst::pointer () const +{ + return this->iter.pointer (); +} + +////////////////////////////////////////////// +// chronIntIdResTable member functions +////////////////////////////////////////////// +inline chronIntId::chronIntId ( const unsigned &idIn ) : + intId ( idIn ) {} + +// +// chronIntIdResTable::chronIntIdResTable() +// +template +inline chronIntIdResTable::chronIntIdResTable () : + resTable (), allocId(1u) {} + +template +inline chronIntIdResTable::chronIntIdResTable ( const chronIntIdResTable & ) : + resTable (), allocId(1u) {} + +template +inline chronIntIdResTable & chronIntIdResTable:: + operator = ( const chronIntIdResTable & ) +{ + return *this; +} + +// +// chronIntIdResTable::~chronIntIdResTable() +// (not inline because it is virtual) +// +template +chronIntIdResTable::~chronIntIdResTable() {} + +// +// chronIntIdResTable::add() +// +// NOTE: This detects (and avoids) the case where +// the PV id wraps around and we attempt to have two +// resources with the same id. +// +template +inline void chronIntIdResTable::idAssignAdd (ITEM &item) +{ + int status; + do { + item.chronIntIdRes::setId (allocId++); + status = this->resTable::add (item); + } + while (status); +} + +///////////////////////////////////////////////// +// chronIntIdRes member functions +///////////////////////////////////////////////// + +// +// chronIntIdRes::chronIntIdRes +// +template +inline chronIntIdRes::chronIntIdRes () : chronIntId (UINT_MAX) {} + +// +// id::setId () +// +// workaround for bug in DEC compiler +// +template +inline void chronIntIdRes::setId (unsigned newId) +{ + this->id = newId; +} + +///////////////////////////////////////////////// +// intId member functions +///////////////////////////////////////////////// + +// +// intId::intId +// +// (if this is inline SUN PRO botches the template instantiation) +template +intId::intId (const T &idIn) + : id (idIn) {} + +// +// intId::operator == () +// +template +inline bool intId::operator == // X aCC 361 + (const intId &idIn) const +{ + return this->id == idIn.id; +} + +// +// intId::getId () +// +template +inline const T intId::getId () const // X aCC 361 +{ + return this->id; +} + +// +// integerHash() +// +// converts any integer into a hash table index +// +template < class T > +inline resTableIndex integerHash ( unsigned MIN_INDEX_WIDTH, + unsigned MAX_ID_WIDTH, const T &id ) +{ + resTableIndex hashid = static_cast ( id ); + + // + // the intent here is to gurantee that all components of the + // integer contribute even if the resTableIndex returned might + // index a small table. + // + // On most compilers the optimizer will unroll this loop so this + // is actually a very small inline function + // + // Experiments using the microsoft compiler show that this isnt + // slower than switching on the architecture size and unrolling the + // loop explicitly (that solution has resulted in portability + // problems in the past). + // + unsigned width = MAX_ID_WIDTH; + do { + width >>= 1u; + hashid ^= hashid>>width; + } while (width>MIN_INDEX_WIDTH); + + // + // the result here is always masked to the + // proper size after it is returned to the "resTable" class + // + return hashid; +} + + +// +// intId::hash() +// +template +inline resTableIndex intId::hash () const // X aCC 361 +{ + return integerHash ( MIN_INDEX_WIDTH, MAX_ID_WIDTH, this->id ); +} + +//////////////////////////////////////////////////// +// stringId member functions +//////////////////////////////////////////////////// + +// +// stringId::operator == () +// +inline bool stringId::operator == + (const stringId &idIn) const +{ + if (this->pStr!=NULL && idIn.pStr!=NULL) { + return strcmp(this->pStr,idIn.pStr)==0; + } + return false; // not equal +} + +// +// stringId::resourceName () +// +inline const char * stringId::resourceName () const +{ + return this->pStr; +} + +#ifdef instantiateRecourceLib + +// +// stringId::stringId() +// +stringId::stringId (const char * idIn, allocationType typeIn) : + allocType (typeIn) +{ + if (typeIn==copyString) { + unsigned nChars = strlen (idIn) + 1u; + this->pStr = new char [nChars]; + memcpy ( (void *) this->pStr, idIn, nChars ); + } + else { + this->pStr = idIn; + } +} + +// +// stringId::show () +// +void stringId::show (unsigned level) const +{ + if (level>2u) { + printf ("resource id = %s\n", this->pStr); + } +} + +// +// stringId::~stringId() +// +// +// this needs to be instantiated only once (normally in libCom) +// +stringId::~stringId() +{ + if (this->allocType==copyString) { + if (this->pStr!=NULL) { + // + // the microsoft and solaris compilers will + // not allow a pointer to "const char" + // to be deleted + // + // the HP-UX compiler gives us a warning on + // each cast away of const, but in this case + // it cant be avoided. + // + // The DEC compiler complains that const isnt + // really significant in a cast if it is present. + // + // I hope that deleting a pointer to "char" + // is the same as deleting a pointer to + // "const char" on all compilers + // + delete [] const_cast(this->pStr); + } + } +} + +// +// stringId::hash() +// +resTableIndex stringId::hash() const +{ + if (!this->pStr) { + return 0u; + } + return epicsStrHash(this->pStr, 0); +} + +#endif // if instantiateRecourceLib is defined + +#endif // INCresourceLibh + diff --git a/src/libCom/cxxTemplates/test/Makefile b/src/libCom/cxxTemplates/test/Makefile new file mode 100644 index 000000000..847f1795e --- /dev/null +++ b/src/libCom/cxxTemplates/test/Makefile @@ -0,0 +1,50 @@ +#************************************************************************* +# 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 + +resourceLibTest_SRCS += resourceLibTest.cc +PROD_HOST += resourceLibTest +OBJS_IOC += resourceLibTest + +tsDLListBench_SRCS += tsDLListBench.cc +PROD_HOST += tsDLListBench +OBJS_IOC += tsDLListBench + +tsDLListTest_SRCS += tsDLListTest.cc +PROD_HOST += tsDLListTest +OBJS_IOC += tsDLListTest + +tsSLListBench_SRCS += tsSLListBench.cc +PROD_HOST += tsSLListBench +OBJS_IOC += tsSLListBench + +tsSLListTest_SRCS += tsSLListTest.cc +PROD_HOST += tsSLListTest +OBJS_IOC += tsSLListTest + +minmaxTest_SRCS += minmaxTest.cc +PROD_HOST += minmaxTest +OBJS_IOC += minmaxTest + +tsBTreeTest_SRCS += tsBTreeTest.cc +PROD_HOST += tsBTreeTest +OBJS_IOC += tsBTreeTest + +tsBTreeBench_SRCS += tsBTreeBench.cc +PROD_HOST += tsBTreeBench +OBJS_IOC += tsBTreeBench + +PROD_LIBS = Com + +include $(TOP)/configure/RULES + diff --git a/src/libCom/cxxTemplates/test/minmaxTest.cc b/src/libCom/cxxTemplates/test/minmaxTest.cc new file mode 100644 index 000000000..b5cf5b110 --- /dev/null +++ b/src/libCom/cxxTemplates/test/minmaxTest.cc @@ -0,0 +1,39 @@ +/*************************************************************************\ +* 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 + +#include "tsMinMax.h" + +int main () +{ + float f1 = 3.3f; + float f2 = 3.4f; + float f3; + + f3 = tsMin(f1,f2); + assert(f3==f1); + + f3 = tsMax(f1,f2); + assert(f3==f2); + + int i1 = 3; + int i2 = 4; + int i3; + + i3 = tsMin(i1,i2); + assert(i3==i1); + + i3 = tsMax(i1,i2); + assert(i3==i2); + + return 0; +} + diff --git a/src/libCom/cxxTemplates/test/resourceLibTest.cc b/src/libCom/cxxTemplates/test/resourceLibTest.cc new file mode 100644 index 000000000..1a6a2c1f9 --- /dev/null +++ b/src/libCom/cxxTemplates/test/resourceLibTest.cc @@ -0,0 +1,309 @@ +/*************************************************************************\ +* 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 +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#define instantiateRecourceLib +#include "resourceLib.h" + +#if defined(__GNUC__) && ( __GNUC__<2 || (__GNUC__==2 && __GNUC__<8) ) +typedef intId testIntId; +#else +typedef intId testIntId; +#endif + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +static void empty() +{ +} + +class albert : public testIntId, public tsSLNode { +public: + albert (resTable< albert, testIntId > &atIn, unsigned idIn) : + testIntId(idIn), at(atIn) + { + verify (at.add (*this)==0); + } + void show (unsigned /* level */) + { + } + void destroy() + { + at.remove(*this); + delete this; + } +private: + resTable< albert, testIntId > &at; +}; + +class fred : public testIntId, public tsSLNode { +public: + fred (const char *pNameIn, unsigned idIn) : + testIntId(idIn), pName(pNameIn) {} + void show (unsigned) + { + printf("fred %s\n", pName); + } + void destroy() + { + // always on stack so noop + } +private: + const char * const pName; +}; + +class jane : public stringId, public tsSLNode { +public: + jane (const char *pNameIn) : stringId (pNameIn) {} + + void testTraverse(); + + void destroy() + { + // always on stack so noop + } +}; + +// +// jane::testTraverse() +// +void jane::testTraverse() +{ + printf("Traverse Test\n"); + this->show(10); +} + +int main() +{ + unsigned i; + clock_t start, finish; + double duration; + const unsigned LOOPS = 500000; + resTable < fred, testIntId > intTbl; + resTable < jane, stringId> strTbl; + fred fred0("fred0",0); + fred fred1("fred1",0x1000a432); + fred fred2("fred2",0x0000a432); + fred fred3("fred3",1); + fred fred4("fred4",2); + fred fred5("fred5",3); + fred fred6("fred6",4); + fred fred7("fred7",5); + fred fred8("fred8",6); + fred fred9("fred9",7); + jane jane1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + jane jane2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); + fred *pFred; + jane *pJane; + testIntId intId0 (0); + testIntId intId1 (0x1000a432); + testIntId intId2 (0x0000a432); + testIntId intId3 (1); + testIntId intId4 (2); + testIntId intId5 (3); + testIntId intId6 (4); + testIntId intId7 (5); + testIntId intId8 (6); + testIntId intId9 (7); + + stringId strId1("rrrrrrrrrrrrrrrrrrrrrrrrrr1"); + strTbl.verify (); + stringId strId2("rrrrrrrrrrrrrrrrrrrrrrrrrr2"); + strTbl.verify (); + + intTbl.setTableSize ( 100000 ); + + verify (intTbl.add(fred0)==0); + intTbl.verify (); + verify (intTbl.add(fred1)==0); + intTbl.verify (); + verify (intTbl.add(fred2)==0); + intTbl.verify (); + verify (intTbl.add(fred3)==0); + intTbl.verify (); + verify (intTbl.add(fred4)==0); + intTbl.verify (); + + intTbl.setTableSize ( 200000 ); + + verify (intTbl.add(fred5)==0); + intTbl.verify (); + verify (intTbl.add(fred6)==0); + intTbl.verify (); + verify (intTbl.add(fred7)==0); + intTbl.verify (); + verify (intTbl.add(fred8)==0); + intTbl.verify (); + verify (intTbl.add(fred9)==0); + intTbl.verify (); + + start = clock(); + for (i=0; i alTbl; + + for (i=0; i alTblIter ( alTbl.firstIter() ); + albert *pa; + i=0; + while ( ( pa = alTblIter.pointer() ) ) { + i++; + alTblIter++; + } + verify ( i == elementCount ); + alTbl.verify (); + + for ( i = 0; i < elementCount; i++ ) { + verify ( pAlbert[i] == alTbl.lookup( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + for ( i = 0; i < elementCount; i += 2 ) { + verify ( pAlbert[i] == alTbl.remove ( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + for ( i = 0; i < elementCount; i += 2 ) { + verify ( 0 == alTbl.lookup ( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + for ( i = 1; i < elementCount; i += 2 ) { + verify ( pAlbert[i] == alTbl.lookup ( pAlbert[i]->getId() ) ); + } + alTbl.verify (); + + return 0; +} + + + diff --git a/src/libCom/cxxTemplates/test/tsBTreeBench.cc b/src/libCom/cxxTemplates/test/tsBTreeBench.cc new file mode 100644 index 000000000..b54837001 --- /dev/null +++ b/src/libCom/cxxTemplates/test/tsBTreeBench.cc @@ -0,0 +1,95 @@ +/*************************************************************************\ +* 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 +#include +#include +#include +#include + +#include "tsBTree.h" +#include "tsSLList.h" + +class A : public tsBTreeNode, public tsSLNode { +public: + A() + { + key = (unsigned) rand(); + } + + btCmp tsBTreeCompare(const A &item) const + { + if (this->key<=item.key) { + return btLessOrEqual; + } + else { + return btGreater; + } + } + + void show() + { + printf("A: %u\n", key); + } +private: + unsigned key; +}; + +#define LOOPCOUNT 10000u + +int main () +{ + unsigned i; + tsBTree tree; + tsSLList list; + A *pA; + A a; + clock_t clk; + clock_t diff; + double delay; + + tree.insert (a); + list.add (a); + + for (i=0u; i +#include +#include + +#include "tsBTree.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +class A : public tsBTreeNode { +public: + A(const char *pNameIn) : pName(pNameIn) {} + + btCmp tsBTreeCompare(const A &item) const + { + int cmp = strcmp(this->pName, item.pName); + if (cmp<=0) { + return btLessOrEqual; + } + else { + return btGreater; + } + } + + void show() + { + printf("A: %s\n", pName); + } +private: + const char *pName; +}; + +int main () +{ + tsBTree tree; + A a0 ("fred"); + A a1 ("jane"); + A a2 ("jane0"); + A a3 ("bill"); + A a4 ("jane"); + A a5 ("dan"); + A a6 ("joe"); + + tree.insert (a0); + tree.insert (a1); + tree.insert (a2); + tree.insert (a3); + tree.insert (a4); + tree.insert (a5); + + tree.traverse (&A::show); + + verify (tree.remove(a6)==tsbtrrNotFound); + tree.insert (a6); + verify (tree.remove(a6)==tsbtrrFound); + verify (tree.remove(a5)==tsbtrrFound); + verify (tree.remove(a5)==tsbtrrNotFound); + verify (!tree.verify(a5)); + verify (tree.verify(a4)); + verify (tree.remove(a0)==tsbtrrFound); + verify (!tree.verify(a0)); + verify (tree.remove(a0)==tsbtrrNotFound); + tree.insert (a5); + verify (tree.verify(a5)); + verify (tree.verify(a2)); + verify (tree.remove(a2)==tsbtrrFound); + verify (!tree.verify(a2)); + verify (tree.remove(a2)==tsbtrrNotFound); + verify (tree.verify(a5)); + verify (tree.remove(a5)==tsbtrrFound); + verify (tree.remove(a5)==tsbtrrNotFound); + verify (tree.remove(a0)==tsbtrrNotFound); + verify (tree.remove(a4)==tsbtrrFound); + verify (tree.remove(a3)==tsbtrrFound); + verify (tree.remove(a4)==tsbtrrNotFound); + verify (tree.remove(a1)==tsbtrrFound); + + tree.traverse (&A::show); + + return 0; +} + + diff --git a/src/libCom/cxxTemplates/test/tsDLListBench.cc b/src/libCom/cxxTemplates/test/tsDLListBench.cc new file mode 100644 index 000000000..577a3829d --- /dev/null +++ b/src/libCom/cxxTemplates/test/tsDLListBench.cc @@ -0,0 +1,71 @@ +/*************************************************************************\ +* 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 "tsDLList.h" +#include +#include +#include + +class fred : public tsDLNode { +public: + fred() : count(0) {} + void inc () {count++;} +private: + unsigned count; +}; + +class jane : public fred, public tsDLNode { +public: + jane() {} +private: +}; + +#define LOOPCOUNT 100000 + +int main () +{ + tsDLList list; + tsDLIter iter = list.firstIter(); + fred *pFred; + unsigned i; + clock_t clk; + clock_t diff; + double delay; + + for (i=0; iinc(); + iter++; + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + pFred = new fred(); + clk = clock(); + for (i=0; iinc(); + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + return 0; +} + diff --git a/src/libCom/cxxTemplates/test/tsDLListTest.cc b/src/libCom/cxxTemplates/test/tsDLListTest.cc new file mode 100644 index 000000000..02fe0dcd9 --- /dev/null +++ b/src/libCom/cxxTemplates/test/tsDLListTest.cc @@ -0,0 +1,131 @@ +/*************************************************************************\ +* 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 "tsDLList.h" +#include +#include + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +class fred : public tsDLNode { +public: + fred(const char * const pNameIn) : pName(pNameIn){} + void show () {printf("%s\n", pName);} +private: + const char * const pName; +}; + +class jane : public fred, public tsDLNode { +public: + jane(const char * const pNameIn) : fred(pNameIn){} +private: +}; + +int main () +{ + unsigned i; + tsDLList list; + fred *pFred; + fred *pFredII; + fred *pFredBack; + tsDLList janeList; + tsDLIter janeFwdIter = janeList.firstIter(); + tsDLIter janeBwdIter = janeList.lastIter(); + jane *pJane; + + pFred = new fred ("A"); + pFredII = new fred ("B"); + + list.add (*pFred); + list.add (*pFredII); + tsDLIter iter = list.firstIter(); + verify (iter.pointer() == pFred); + iter++; + verify (iter.pointer() == pFredII); + list.remove(*pFred); + list.add(*pFred); + pFredBack = list.get(); + verify (pFredBack == pFredII); + pFredBack = list.get(); + verify (pFredBack == pFred); + verify (list.count() == 0u); + list.add(*pFred); + list.add(*pFredII); + list.add(* new fred("C")); + list.add(* new fred("D")); + + iter = list.firstIter(); + while ( iter.valid() ) { + iter->show(); + iter++; + } + + pJane = new jane("JA"); + janeList.add(*pJane); + pJane = new jane("JB"); + verify ( janeList.find ( *pJane ) == -1 ); + janeList.add(*pJane); + verify ( janeList.find ( *pJane ) == 1 ); + + while ( janeFwdIter.valid() ) { + janeFwdIter->show(); + janeFwdIter++; + } + + while ( janeBwdIter.valid() ) { + janeBwdIter->show(); + janeBwdIter--; + } + + iter = list.firstIter(); + while ( iter.valid() ) { + iter->show(); + iter++; + } + + tsDLIter < jane > bdIter = janeList.firstIter (); + i = 0; + while ( bdIter.valid () ) { + i++; + bdIter++; + } + verify ( i == janeList.count () ); + + iter = list.firstIter(); + while ( iter.pointer() ) { + list.remove( * iter.pointer() ); + iter++; + } + verify (list.count()==0); + + janeFwdIter = janeList.firstIter(); + while ( janeFwdIter.valid() ) { + janeList.remove( * janeFwdIter.pointer() ); + janeFwdIter++; + } + verify (janeList.count()==0); + + pJane = new jane("JA"); + janeList.add(*pJane); + pJane = new jane("JB"); + janeList.add(*pJane); + janeBwdIter = janeList.lastIter(); + while ( janeBwdIter.valid() ) { + janeList.remove( * janeBwdIter.pointer() ); + janeBwdIter--; + } + verify (janeList.count()==0); + + return 0; +} + diff --git a/src/libCom/cxxTemplates/test/tsSLListBench.cc b/src/libCom/cxxTemplates/test/tsSLListBench.cc new file mode 100644 index 000000000..0b786dfd3 --- /dev/null +++ b/src/libCom/cxxTemplates/test/tsSLListBench.cc @@ -0,0 +1,85 @@ +/*************************************************************************\ +* 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 "tsSLList.h" +#include +#include +#include + +/* + * gnuc does not provide this under sunos4 + */ +#if !defined(CLOCKS_PER_SEC) && defined(SUNOS4) +# define CLOCKS_PER_SEC 1000000 +#endif + +class fred : public tsSLNode { +public: + fred() : count(0) {} + void inc () {count++;} +private: + unsigned count; +}; + +class jane : public fred, public tsSLNode { +public: + jane() {} +private: +}; + +#define LOOPCOUNT 100000 + +int main () +{ + tsSLList list; + fred *pFred; + unsigned i; + clock_t clk; + clock_t diff; + double delay; + + for (i=0; i iter ( list.firstIter () ); + while ( iter.valid () ) { + iter->inc (); + iter++; + } + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + pFred = new fred(); + clk = clock(); + { + tsSLIter iter ( list.firstIter () ); + for ( i=0; iinc(); + } + } + diff = clock() - clk; + delay = diff; + delay = delay/CLOCKS_PER_SEC; + delay = delay/LOOPCOUNT; + printf("delay = %15.10f\n", delay); + + return 0; +} + diff --git a/src/libCom/cxxTemplates/test/tsSLListTest.cc b/src/libCom/cxxTemplates/test/tsSLListTest.cc new file mode 100644 index 000000000..32f96359d --- /dev/null +++ b/src/libCom/cxxTemplates/test/tsSLListTest.cc @@ -0,0 +1,106 @@ +/*************************************************************************\ +* 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 "tsSLList.h" +#include +#include + +class fred : public tsSLNode { +public: + fred(const char * const pNameIn) : pName(pNameIn){} + void show () {printf("%s\n", pName);} +private: + const char * const pName; +}; + +class jane : public fred, public tsSLNode { +public: + jane(const char * const pNameIn) : fred(pNameIn){} +private: +}; + +int main () +{ + tsSLList list; + fred *pFred; + fred *pFredII; + fred *pFredBack; + tsSLList janeList; + jane *pJane; + + pFred = new fred("A"); + pFredII = new fred("B"); + + list.add(*pFred); + list.add(*pFredII); + { + tsSLIter iter1 = list.firstIter (); + tsSLIter iter2 = iter1; + tsSLIter iter3 = iter1; + assert ( iter1 == iter3++ ); + assert ( iter3 == ++iter2 ); + list.remove ( *pFredII ); // removes pFred + } + list.add ( *pFred ); + pFredBack = list.get(); + assert (pFredBack == pFred); + pFredBack = list.get(); + assert (pFredBack == pFredII); + list.add(*pFredII); + list.add(*pFred); + while ( list.get () ); + pFredBack = list.get(); + assert (pFredBack == 0); + list.add(*pFred); + list.add(*pFredII); + list.add(* new fred("C")); + list.add(* new fred("D")); + + { + tsSLIter < fred > iter = list.firstIter(); + while ( iter.valid () ) { + iter->show(); + ++iter; + } + } + + pJane = new jane("JA"); + janeList.add(*pJane); + pJane = new jane("JB"); + janeList.add(*pJane); + + { + tsSLIter < jane > iter = janeList.firstIter (); + while ( iter.valid () ) { + iter->show(); + ++iter; + } + } + + { + tsSLIter < fred > iter = list.firstIter (); + while ( iter.valid () ) { + iter->show (); + iter++; + } + } + + while ( list.get () ); + + { + tsSLIter < fred > iter = list.firstIter (); + assert ( ! iter.valid () ); + } + + return 0; +} + diff --git a/src/libCom/cxxTemplates/tsBTree.h b/src/libCom/cxxTemplates/tsBTree.h new file mode 100644 index 000000000..29b7b9c9d --- /dev/null +++ b/src/libCom/cxxTemplates/tsBTree.h @@ -0,0 +1,279 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef assert // allow use of epicsAssert.h +#include +#endif +#include "locationException.h" + +// +// tsBTreeRMRet +// +enum tsbtRR {tsbtrrNotFound, tsbtrrFound}; +template +class tsBTreeRMRet { +public: + tsBTreeRMRet (tsbtRR foundItIn, T *pNewSegIn) : + foundIt(foundItIn), pNewSeg(pNewSegIn) {} + + operator tsbtRR () + { + return this->foundIt; + } + + const tsbtRR foundIt; + T * const pNewSeg; +}; + +template class tsBTree; + +// +// tsBTreeNode +// +template +class tsBTreeNode +{ +friend class tsBTree; +public: + // + // exceptions + // + class invalid_btCmp {}; + + + // + // when someone copies into a class deriving from this + // do _not_ change the node pointers + // + void operator = (tsBTreeNode &) {} + + enum btCmp {btGreater, btLessOrEqual}; + + // + // class T must supply this member function which + // comapres *this with item + // + // returns: + // btGreater *this is greater than item + // btLessOrEqual *this is less than or equal to item + // + // btCmp tsBTreeCompare(const T &item) const; + // + +private: + T *pLeft; + T *pRight; + + // + // run callback for evey item in the B-Treee in sort order + // + static void traverse (T &item, void (T::*pCallBack)()) + { + tsBTreeNode &node = item; + if (node.pLeft) { + tsBTreeNode::traverse (*node.pLeft, pCallBack); + } + (item.*pCallBack)(); + if (node.pRight) { + tsBTreeNode::traverse (*node.pRight, pCallBack); + } + } + + // + // NOTE: + // no change to item.pLeft and item.pRight here + // so that an segment of a tree can be inserted + // + static void insert(T &self, T &item) + { + tsBTreeNode &node = self; + btCmp result = item.tsBTreeCompare(self); + if (result==btLessOrEqual) { + if (node.pLeft) { + tsBTreeNode::insert (*node.pLeft, item); + } + else { + node.pLeft = &item; + } + } + else if(result==btGreater) { + if (node.pRight) { + tsBTreeNode::insert (*node.pRight, item); + } + else { + node.pRight = &item; + } + } + else { + throwWithLocation ( invalid_btCmp () ); + } + } + + // + // remove() + // returns pointer to modified tree and found/not found + // (NULL if this portion of the tree is empty) + // + static tsBTreeRMRet remove(T &self, T &item) + { + tsBTreeNode &node = self; + if (&self == &item) { + if (node.pLeft) { + if (node.pRight) { + tsBTreeNode *pLeftNode = node.pLeft; + T *pR = pLeftNode->pRight; + if (pR) { + tsBTreeNode::insert (*pR, *node.pRight); + } + else { + pLeftNode->pRight = node.pRight; + } + } + return tsBTreeRMRet(tsbtrrFound, node.pLeft); // found it + } + else { + return tsBTreeRMRet(tsbtrrFound, node.pRight); // found it + } + } + + btCmp result = item.tsBTreeCompare(self); + if (result==btLessOrEqual) { + if (node.pLeft) { + tsBTreeRMRet ret = tsBTreeNode::remove(*node.pLeft, item); + if (ret.foundIt==tsbtrrFound) { + node.pLeft= ret.pNewSeg; + return tsBTreeRMRet (tsbtrrFound, &self); // TRUE - found it + } + } + return tsBTreeRMRet(tsbtrrNotFound, 0u); // not found + } + else if(result==btGreater) { + if (node.pRight) { + tsBTreeRMRet ret = tsBTreeNode::remove(*node.pRight, item); + if (ret.foundIt==tsbtrrFound) { + node.pRight = ret.pNewSeg; + return tsBTreeRMRet(tsbtrrFound,&self); // TRUE - found it + } + } + return tsBTreeRMRet(tsbtrrNotFound, 0u); // not found + } + else { + return tsBTreeRMRet(tsbtrrNotFound, 0u); // not found + } + } + // + // verify + // + static unsigned verify(const T &self, const T &item) + { + const tsBTreeNode &node = self; + + if (&self == &item) { + return 1u; // TRUE -item is present + } + btCmp result = item.tsBTreeCompare(self); + if (result==btLessOrEqual) { + if (node.pLeft) { + return tsBTreeNode::verify (*node.pLeft, item); + } + else { + return 0u; // FALSE - not found + } + } + else if(result==btGreater) { + if (node.pRight) { + return tsBTreeNode::verify (*node.pRight, item); + } + else { + return 0u; // FALSE - not found + } + } + else { + return 0u; // FALSE - not found + } + } +}; + +// +// tsBTree +// +template +class tsBTree +{ +public: + tsBTree() : pRoot(0u) {} + +// ~tsBTree() +// { +// this->traverse(T::~T); +// } + + void insert(T &item) + { + tsBTreeNode &node = item; + node.pLeft = 0; + node.pRight = 0; + if (this->pRoot) { + tsBTreeNode::insert(*this->pRoot, item); + } + else { + this->pRoot = &item; + } + } + // + // remove item from the tree + // + // returns true if item was in the tree + // (otherwise FALSE) + // + unsigned remove(T &item) + { + if (this->pRoot) { + tsBTreeRMRet ret = + tsBTreeNode::remove(*this->pRoot, item); + if (ret.foundIt) { + this->pRoot = ret.pNewSeg; + return 1u; // TRUE - found it + } + } + return 0u; // FALSE - not found + } + // + // verify that item is in the tree + // + // returns true if item is in the tree + // (otherwise FALSE) + // + unsigned verify(T &item) const + { + if (this->pRoot) { + return tsBTreeNode::verify(*this->pRoot, item); + } + else { + return 0u; // FALSE - not found + } + } + // + // Call (pT->*pCB) () for each item in the table + // + // where pT is a pointer to type T and pCB is + // a pointer to a memmber function of T with + // no parameters and returning void + // + void traverse(void (T::*pCB)()) + { + if (this->pRoot) { + tsBTreeNode::traverse(*this->pRoot, pCB); + } + } +private: + T *pRoot; +}; + diff --git a/src/libCom/cxxTemplates/tsDLList.h b/src/libCom/cxxTemplates/tsDLList.h new file mode 100644 index 000000000..b6b4f04c6 --- /dev/null +++ b/src/libCom/cxxTemplates/tsDLList.h @@ -0,0 +1,686 @@ +/*************************************************************************\ +* 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 + * + * type safe doubly linked list templates + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef tsDLListH_include +#define tsDLListH_include + +template class tsDLList; +template class tsDLIterConst; +template class tsDLIter; + +// +// class tsDLNode +// +// a node in a doubly linked list containing entries of type T +// ( class T must publicly derive from class tsDLNode ) +// +template < class T > +class tsDLNode { +public: + tsDLNode (); + tsDLNode ( const tsDLNode & ); + const tsDLNode & operator = ( const tsDLNode & ); +private: + T * pNext; + T * pPrev; + friend class tsDLList; + friend class tsDLIter; + friend class tsDLIterConst; +}; + +// +// class tsDLList +// +// a doubly linked list containing entries of type T +// ( class T must publicly derive from class tsDLNode ) +// +template < class T > +class tsDLList { +public: + tsDLList (); // create empty list + unsigned count () const; // number of items on list + void add ( T & item ); // add item to end of list + void add ( tsDLList & addList ); // add to end of list - addList left empty + void push ( T & item ); // add item to beginning of list + void push ( tsDLList & pushList ); // add to beg of list - pushList left empty + void remove ( T & item ); // remove item from list + void removeAll ( tsDLList & destination ); + T * get (); // removes first item on list + T * pop (); // same as get () + void insertAfter ( T & item, T & itemBefore ); // insert item immediately after itemBefore + void insertBefore ( T & item, T & itemAfter ); // insert item immediately before itemAfter + int find ( const T & item ) const; // returns -1 if not present, node number if present + T * first ( void ) const; // ptr to first item on list + T * last ( void ) const; // ptr to last item on list + tsDLIterConst firstIter () const; + tsDLIter firstIter (); + tsDLIterConst lastIter () const; + tsDLIter lastIter (); + static tsDLIterConst invalidConstIter (); + static tsDLIter invalidIter (); +private: + T * pFirst; + T * pLast; + unsigned itemCount; + void clear (); + tsDLList ( const tsDLList & ); // not allowed + const tsDLList & operator = ( const tsDLList & ); // not allowed +}; + +// +// class tsDLIterConst +// +// bi-directional iterator for a const doubly linked list +// +template +class tsDLIterConst { +public: + tsDLIterConst (); + bool valid () const; + bool operator == ( const tsDLIterConst & rhs ) const; + bool operator != ( const tsDLIterConst & rhs ) const; + tsDLIterConst & operator = ( const tsDLIterConst & ); + const T & operator * () const; + const T * operator -> () const; + tsDLIterConst & operator ++ (); + tsDLIterConst operator ++ (int); + tsDLIterConst & operator -- (); + tsDLIterConst operator -- (int); + const T * pointer () const; +private: + const T * pEntry; + tsDLIterConst ( const T * pInitialEntry ); + friend class tsDLList ; +}; + +// +// class tsDLIter +// +// bi-directional iterator for a doubly linked list +// +template +class tsDLIter { +public: + tsDLIter (); + bool valid () const; + bool operator == ( const tsDLIter & rhs ) const; + bool operator != ( const tsDLIter & rhs ) const; + tsDLIter & operator = ( const tsDLIter & ); + T & operator * () const; + T * operator -> () const; + tsDLIter & operator ++ (); + tsDLIter operator ++ ( int ); + tsDLIter & operator -- (); + tsDLIter operator -- ( int ); + T * pointer () const; +private: + T * pEntry; + tsDLIter ( T *pInitialEntry ); + friend class tsDLList ; +}; + +/////////////////////////////////// +// tsDLNode member functions +/////////////////////////////////// + +// +// tsDLNode::tsDLNode () +// +template +inline tsDLNode::tsDLNode() : pNext(0), pPrev(0) {} + +template +inline tsDLNode::tsDLNode ( const tsDLNode & ) : + pNext (0), pPrev(0) {} + +// +// tsDLNode::operator = () +// +// when someone tries to copy another node into a node +// do _not_ change the node pointers +// +template +inline const tsDLNode & tsDLNode::operator = ( const tsDLNode & ) +{ + return * this; +} + +////////////////////////////////////// +// tsDLList member functions +////////////////////////////////////// + +// +// tsDLList::tsDLList () +// +template +inline tsDLList::tsDLList () +{ + this->clear (); +} + +// +// tsDLList::count () +// +// (returns the number of items on the list) +// +template +inline unsigned tsDLList::count () const +{ + return this->itemCount; +} + +// +// tsDLList::first () +// +template +inline T * tsDLList::first (void) const +{ + return this->pFirst; +} + +// +// tsDLList::last () +// +template +inline T *tsDLList::last (void) const +{ + return this->pLast; +} + +// +// tsDLList::clear () +// +template +inline void tsDLList::clear () +{ + this->pFirst = 0; + this->pLast = 0; + this->itemCount = 0u; +} + +// +// tsDLList::remove () +// +template +inline void tsDLList::remove ( T &item ) +{ + tsDLNode &theNode = item; + + if ( this->pLast == &item ) { + this->pLast = theNode.pPrev; + } + else { + tsDLNode &nextNode = *theNode.pNext; + nextNode.pPrev = theNode.pPrev; + } + + if ( this->pFirst == &item ) { + this->pFirst = theNode.pNext; + } + else { + tsDLNode &prevNode = *theNode.pPrev; + prevNode.pNext = theNode.pNext; + } + + this->itemCount--; +} + +// +// tsDLList::removeAll () +// +template +inline void tsDLList::removeAll ( + tsDLList & destination ) +{ + destination.pFirst = this->pFirst; + destination.pLast = this->pLast; + destination.itemCount = this->itemCount; + this->pFirst = 0; + this->pLast = 0; + this->itemCount = 0; +} + +// +// tsDLList::get () +// +template +inline T * tsDLList::get() +{ + T *pItem = this->pFirst; + + if ( pItem ) { + this->remove ( *pItem ); + } + + return pItem; +} + +// +// tsDLList::pop () +// +// (returns the first item on the list) +template +inline T * tsDLList::pop () +{ + return this->get (); +} + +// +// tsDLList::add () +// +// adds addList to the end of the list +// (and removes all items from addList) +// +template +inline void tsDLList::add ( tsDLList &addList ) +{ + if ( addList.itemCount != 0u ) { + if ( this->itemCount == 0u ) { + this->pFirst = addList.pFirst; + } + else { + tsDLNode *pLastNode = this->pLast; + tsDLNode *pAddListFirstNode = addList.pFirst; + pLastNode->pNext = addList.pFirst; + pAddListFirstNode->pPrev = addList.pLast; + } + this->pLast = addList.pLast; + this->itemCount += addList.itemCount; + addList.clear(); + } +} + +// +// tsDLList::add () +// +// add an item to the end of the list +// +template +inline void tsDLList::add (T &item) +{ + tsDLNode &theNode = item; + + theNode.pNext = 0; + theNode.pPrev = this->pLast; + + if ( this->itemCount ) { + tsDLNode &lastNode = *this->pLast; + lastNode.pNext = &item; + } + else { + this->pFirst = &item; + } + + this->pLast = &item; + + this->itemCount++; +} + +// +// tsDLList::insertAfter () +// +// place item in the list immediately after itemBefore +// +template +inline void tsDLList::insertAfter (T &item, T &itemBefore) +{ + tsDLNode &nodeItem = item; + tsDLNode &nodeBefore = itemBefore; + + nodeItem.pPrev = &itemBefore; + nodeItem.pNext = nodeBefore.pNext; + nodeBefore.pNext = &item; + + if (nodeItem.pNext) { + tsDLNode *pNextNode = nodeItem.pNext; + pNextNode->pPrev = &item; + } + else { + this->pLast = &item; + } + + this->itemCount++; +} + +// +// tsDLList::insertBefore () +// +// place item in the list immediately before itemAfter +// +template +inline void tsDLList::insertBefore (T &item, T &itemAfter) +{ + tsDLNode &node = item; + tsDLNode &nodeAfter = itemAfter; + + node.pNext = &itemAfter; + node.pPrev = nodeAfter.pPrev; + nodeAfter.pPrev = &item; + + if (node.pPrev) { + tsDLNode &prevNode = *node.pPrev; + prevNode.pNext = &item; + } + else { + this->pFirst = &item; + } + + this->itemCount++; +} + +// +// tsDLList::push () +// +// adds pushList to the beginning of the list +// (and removes all items from pushList) +// +template +inline void tsDLList::push ( tsDLList & pushList ) +{ + if ( pushList.itemCount != 0u ) { + if ( this->itemCount ) { + tsDLNode * pFirstNode = this->pFirst; + tsDLNode * pAddListLastNode = pushList.pLast; + pFirstNode->pPrev = pushList.pLast; + pAddListLastNode->pNext = this->pFirst; + } + else { + this->pLast = pushList.pLast; + } + this->pFirst = pushList.pFirst; + this->itemCount += pushList.itemCount; + pushList.clear(); + } +} + +// +// tsDLList::push () +// +// add an item at the beginning of the list +// +template +inline void tsDLList::push (T &item) +{ + tsDLNode &theNode = item; + theNode.pPrev = 0; + theNode.pNext = this->pFirst; + + if ( this->itemCount ) { + tsDLNode *pFirstNode = this->pFirst; + pFirstNode->pPrev = & item; + } + else { + this->pLast = & item; + } + + this->pFirst = &item; + + this->itemCount++; +} + +// +// tsDLList::find () +// returns -1 if the item isnt on the list +// and the node number (beginning with zero if +// it is) +// +template < class T > +int tsDLList < T > :: find ( const T &item ) const +{ + tsDLIterConst < T > thisItem ( &item ); + tsDLIterConst < T > iter ( this->first () ); + int itemNo = 0; + + while ( iter.valid () ) { + if ( iter == thisItem ) { + return itemNo; + } + itemNo++; + iter++; + } + return -1; +} + +template < class T > +inline tsDLIterConst tsDLList < T > :: firstIter () const +{ + return tsDLIterConst < T > ( this->pFirst ); +} + +template < class T > +inline tsDLIter tsDLList < T > :: firstIter () +{ + return tsDLIter < T > ( this->pFirst ); +} + +template < class T > +inline tsDLIterConst tsDLList < T > :: lastIter () const +{ + return tsDLIterConst < T > ( this->pLast ); +} + +template < class T > +inline tsDLIter tsDLList < T > :: lastIter () +{ + return tsDLIter < T > ( this->pLast ); +} + +template < class T > +inline tsDLIterConst tsDLList < T > :: invalidConstIter () +{ + return tsDLIterConst < T > ( 0 ); +} + +template < class T > +inline tsDLIter tsDLList < T > :: invalidIter () +{ + return tsDLIter < T > ( 0 ); +} + +////////////////////////////////////////// +// tsDLIterConst member functions +////////////////////////////////////////// +template +inline tsDLIterConst::tsDLIterConst ( const T *pInitialEntry ) : + pEntry ( pInitialEntry ) {} + +template +inline tsDLIterConst::tsDLIterConst () : + pEntry ( 0 ) {} + +template +inline bool tsDLIterConst::valid () const +{ + return this->pEntry != 0; +} + +template +inline bool tsDLIterConst::operator == (const tsDLIterConst &rhs) const +{ + return this->pEntry == rhs.pEntry; +} + +template +inline bool tsDLIterConst::operator != (const tsDLIterConst &rhs) const +{ + return this->pEntry != rhs.pEntry; +} + +template +inline tsDLIterConst & tsDLIterConst::operator = ( const tsDLIterConst & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template +inline const T & tsDLIterConst::operator * () const +{ + return *this->pEntry; +} + +template +inline const T * tsDLIterConst::operator -> () const +{ + return this->pEntry; +} + +// +// prefix ++ +// +template +inline tsDLIterConst & tsDLIterConst::operator ++ () +{ + const tsDLNode &node = *this->pEntry; + this->pEntry = node.pNext; + return *this; +} + +// +// postfix ++ +// +template +inline tsDLIterConst tsDLIterConst::operator ++ (int) +{ + const tsDLIterConst tmp = *this; + const tsDLNode &node = *this->pEntry; + this->pEntry = node.pNext; + return tmp; +} + +// +// prefix -- +// +template +inline tsDLIterConst & tsDLIterConst::operator -- () +{ + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return *this; +} + +// +// postfix -- +// +template +inline tsDLIterConst tsDLIterConst::operator -- (int) +{ + const tsDLIterConst tmp = *this; + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return tmp; +} + +template +inline const T * tsDLIterConst::pointer () const +{ + return this->pEntry; +} + +////////////////////////////////////////// +// tsDLIter member functions +////////////////////////////////////////// + +template +inline tsDLIter::tsDLIter ( T * pInitialEntry ) : + pEntry ( pInitialEntry ) {} + +template +inline tsDLIter::tsDLIter () : + pEntry ( 0 ) {} + +template +inline bool tsDLIter::valid () const +{ + return this->pEntry != 0; +} + +template +inline bool tsDLIter::operator == (const tsDLIter &rhs) const +{ + return this->pEntry == rhs.pEntry; +} + +template +inline bool tsDLIter::operator != (const tsDLIter &rhs) const +{ + return this->pEntry != rhs.pEntry; +} + +template +inline tsDLIter & tsDLIter::operator = ( const tsDLIter & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template +inline T & tsDLIter::operator * () const +{ + return *this->pEntry; +} + +template +inline T * tsDLIter::operator -> () const +{ + return this->pEntry; +} + +template +inline tsDLIter & tsDLIter::operator ++ () // prefix ++ +{ + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pNext; + return *this; +} + +template +inline tsDLIter tsDLIter::operator ++ (int) // postfix ++ +{ + const tsDLIter tmp = *this; + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pNext; + return tmp; +} + +template +inline tsDLIter & tsDLIter::operator -- () // prefix -- +{ + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return *this; +} + +template +inline tsDLIter tsDLIter::operator -- (int) // postfix -- +{ + const tsDLIter tmp = *this; + const tsDLNode &entryNode = *this->pEntry; + this->pEntry = entryNode.pPrev; + return tmp; +} + +template +inline T * tsDLIter::pointer () const +{ + return this->pEntry; +} + +#endif // tsDLListH_include + diff --git a/src/libCom/cxxTemplates/tsFreeList.h b/src/libCom/cxxTemplates/tsFreeList.h new file mode 100644 index 000000000..bda128a97 --- /dev/null +++ b/src/libCom/cxxTemplates/tsFreeList.h @@ -0,0 +1,198 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef tsFreeList_h +#define tsFreeList_h + +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * Author: Jeff Hill + */ + +// +// TODO: this should allow free list chaining so that a free +// list (in a different lock domain) is used to provide the +// tsFreeListChunk. +// + +// +// To allow your class to be allocated off of a free list +// using the new operator: +// +// 1) add the following static, private free list data members +// to your class +// +// static tsFreeList < class classXYZ > freeList; +// +// 2) add the following member functions to your class +// +// inline void * classXYZ::operator new ( size_t size ) +// { +// return freeList.allocate ( size ); +// } +// +// inline void classXYZ::operator delete ( void *pCadaver, size_t size ) +// { +// freeList.release ( pCadaver, size ); +// } +// +// NOTES: +// +// 1) A NOOP mutex class may be specified if mutual exclusion isnt required +// +// 2) If you wish to force use of the new operator, then declare your class's +// destructor as a protected member function. +// +// 3) Setting N to zero causes the free list to be bypassed +// + +#ifdef EPICS_FREELIST_DEBUG +# define tsFreeListDebugBypass 1 +# define tsFreeListMemSetNew(P,SIZE) memset ( (P), 0xaa, (SIZE) ) +# define tsFreeListMemSetDelete(P,SIZE) memset ( (P), 0xdd, (SIZE) ) +#else +# define tsFreeListDebugBypass 0 +# define tsFreeListMemSetNew(P,SIZE) +# define tsFreeListMemSetDelete(P,SIZE) +#endif + +#include +#include "string.h" + +#include "compilerDependencies.h" +#include "epicsMutex.h" +#include "epicsGuard.h" + +// ms visual studio 6.0 and before incorrectly +// warn about a missing delete operator if only the +// newly preferred delete operator with a size argument +// is present +#if defined ( _MSC_VER ) && _MSC_VER <= 1200 +# pragma warning ( disable : 4291 ) +#endif + +template < class T > union tsFreeListItem; +template < class T, unsigned N> struct tsFreeListChunk; + +template < class T, unsigned N = 0x400, + class MUTEX = epicsMutex > +class tsFreeList { +public: + tsFreeList (); + ~tsFreeList (); + void * allocate ( size_t size ); + void release ( void * p ); + void release ( void * p, size_t size ); +private: + MUTEX mutex; + tsFreeListItem < T > * pFreeList; + tsFreeListChunk < T, N > * pChunkList; + void * allocateFromNewChunk (); +}; + +template < class T > +union tsFreeListItem { +public: + char pad [ sizeof ( T ) ]; + tsFreeListItem < T > * pNext; +}; + +template < class T, unsigned N = 0x400 > +struct tsFreeListChunk { + tsFreeListItem < T > items [N]; + tsFreeListChunk < T, N > * pNext; +}; + +template < class T, unsigned N, class MUTEX > +inline tsFreeList < T, N, MUTEX > :: tsFreeList () : + pFreeList ( 0 ), pChunkList ( 0 ) {} + +template < class T, unsigned N, class MUTEX > +tsFreeList < T, N, MUTEX > :: ~tsFreeList () +{ + while ( tsFreeListChunk < T, N > *pChunk = this->pChunkList ) { + this->pChunkList = this->pChunkList->pNext; + delete pChunk; + } +} + +template < class T, unsigned N, class MUTEX > +void * tsFreeList < T, N, MUTEX >::allocate ( size_t size ) +{ + if ( size != sizeof ( T ) || N == 0u || tsFreeListDebugBypass ) { + void * p = ::operator new ( size ); + tsFreeListMemSetNew ( p, size ); + return p; + } + + epicsGuard < MUTEX > guard ( this->mutex ); + + tsFreeListItem < T > * p = this->pFreeList; + if ( p ) { + this->pFreeList = p->pNext; + return static_cast < void * > ( p ); + } + return this->allocateFromNewChunk (); +} + +template < class T, unsigned N, class MUTEX > +void * tsFreeList < T, N, MUTEX >::allocateFromNewChunk () +{ + tsFreeListChunk < T, N > * pChunk = + new tsFreeListChunk < T, N >; + + for ( unsigned i=1u; i < N-1; i++ ) { + pChunk->items[i].pNext = &pChunk->items[i+1]; + } + pChunk->items[N-1].pNext = 0; + if ( N > 1 ) { + this->pFreeList = &pChunk->items[1u]; + } + pChunk->pNext = this->pChunkList; + this->pChunkList = pChunk; + + return static_cast ( &pChunk->items[0] ); +} + +template < class T, unsigned N, class MUTEX > +void tsFreeList < T, N, MUTEX >::release ( void * pCadaver, size_t size ) +{ + if ( size != sizeof ( T ) ) { + tsFreeListMemSetDelete ( pCadaver, size ); + ::operator delete ( pCadaver ); + } + else { + this->release ( pCadaver ); + } +} + +template < class T, unsigned N, class MUTEX > +void tsFreeList < T, N, MUTEX >::release ( void * pCadaver ) +{ + if ( N == 0u || tsFreeListDebugBypass ) { + tsFreeListMemSetDelete ( pCadaver, sizeof ( T ) ); + ::operator delete ( pCadaver ); + } + else if ( pCadaver ) { + epicsGuard < MUTEX > guard ( this->mutex ); + tsFreeListItem < T > * p = + static_cast < tsFreeListItem < T > * > ( pCadaver ); + p->pNext = this->pFreeList; + this->pFreeList = p; + } +} + +#endif // tsFreeList_h diff --git a/src/libCom/cxxTemplates/tsMinMax.h b/src/libCom/cxxTemplates/tsMinMax.h new file mode 100644 index 000000000..5adac3ef2 --- /dev/null +++ b/src/libCom/cxxTemplates/tsMinMax.h @@ -0,0 +1,31 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +// +// simple type safe inline template functions to replace +// the min() and max() macros +// + +#ifndef tsMinMaxh +#define tsMinMaxh + +template +inline const T & tsMax ( const T & a, const T & b ) +{ + return ( a > b ) ? a : b; +} + +template +inline const T & tsMin ( const T & a, const T & b ) +{ + return ( a < b ) ? a : b; +} + +#endif // tsMinMaxh diff --git a/src/libCom/cxxTemplates/tsSLList.h b/src/libCom/cxxTemplates/tsSLList.h new file mode 100644 index 000000000..96d03d30c --- /dev/null +++ b/src/libCom/cxxTemplates/tsSLList.h @@ -0,0 +1,445 @@ +/*************************************************************************\ +* 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 + * + * type safe singly linked list templates + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef tsSLListh +#define tsSLListh + +#ifndef assert // allow use of epicsAssert.h +#include +#endif + +// +// the hp compiler complains about parameterized friend +// class that has not been declared without this? +// +template < class T > class tsSLList; +template < class T > class tsSLIter; +template < class T > class tsSLIterConst; + +// +// tsSLNode +// NOTE: class T must derive from tsSLNode +// +template +class tsSLNode { +public: + tsSLNode (); + tsSLNode < T > & operator = ( const tsSLNode < T > & ) const; +private: + void removeNextItem (); // removes the item after this node + T *pNext; + tsSLNode ( const tsSLNode < T > & ); + friend class tsSLList < T >; + friend class tsSLIter < T >; + friend class tsSLIterConst < T >; +}; + + +// +// tsSLList +// NOTE: class T must derive from tsSLNode +// +template < class T > +class tsSLList : public tsSLNode < T > { +public: + tsSLList (); // creates an empty list + tsSLList ( tsSLList & ); + void insert ( T &item, tsSLNode < T > &itemBefore ); // insert after item before + void add ( T &item ); // add to the beginning of the list + T * get (); // remove from the beginning of the list + T * pop (); // same as get + void push ( T &item ); // same as add + T * first () const; + void remove ( T &itemBefore ); + tsSLIterConst firstIter () const; + tsSLIter firstIter (); + static tsSLIterConst invalidConstIter (); + static tsSLIter invalidIter (); +private: + const tsSLList < T > & operator = ( const tsSLList < T > & ); +}; + +// +// tsSLIterConst +// +template < class T > +class tsSLIterConst { +public: + tsSLIterConst (); + bool valid () const; + bool operator == (const tsSLIterConst &rhs) const; + bool operator != (const tsSLIterConst &rhs) const; + tsSLIterConst & operator = (const tsSLIterConst &); + const T & operator * () const; + const T * operator -> () const; + tsSLIterConst & operator ++ (); + tsSLIterConst operator ++ (int); + const T * pointer () const; +protected: + const T * pEntry; + tsSLIterConst ( const T *pInitialEntry ); + friend class tsSLList < T >; +}; + +// +// tsSLIter +// +template < class T > +class tsSLIter { +public: + tsSLIter (); + bool valid () const; + bool operator == (const tsSLIter &rhs) const; + bool operator != (const tsSLIter &rhs) const; + tsSLIter & operator = (const tsSLIter &); + T & operator * () const; + T * operator -> () const; + tsSLIter & operator ++ (); + tsSLIter operator ++ (int); + T * pointer () const; +private: + T *pEntry; + tsSLIter ( T *pInitialEntry ); + friend class tsSLList < T >; +}; + +////////////////////////////////////////// +// +// tsSLNode inline member functions +// +////////////////////////////////////////// + +// +// tsSLNode::tsSLNode () +// +template < class T > +inline tsSLNode < T > :: tsSLNode () : pNext ( 0 ) {} + +// +// tsSLNode::tsSLNode ( const tsSLNode < T > & ) +// private - not to be used - implemented to eliminate warnings +// +template < class T > +inline tsSLNode < T > :: tsSLNode ( const tsSLNode < T > & ) +{ +} + +// +// tsSLNode::operator = +// +// when someone copies into a class deriving from this +// do _not_ change the node pointers +// +template < class T > +inline tsSLNode < T > & tsSLNode < T >::operator = + ( const tsSLNode < T > & ) const +{ + return *this; +} + +// +// removeNextItem () +// +// removes the item after this node +// +template +inline void tsSLNode::removeNextItem () +{ + T *pItem = this->pNext; + if ( pItem ) { + tsSLNode < T > *pNode = pItem; + this->pNext = pNode->pNext; + } +} + +////////////////////////////////////////// +// +// tsSLList inline member functions +// +////////////////////////////////////////// + +// +// tsSLList::tsSLList() +// create an empty list +// +template < class T > +inline tsSLList < T > :: tsSLList () +{ +} + +// +// tsSLList::tsSLList( tsSLList & ) +// +template < class T > +inline tsSLList < T > :: tsSLList ( tsSLList &listIn ) +{ + this->pNext = listIn.pNext; + listIn.pNext = 0; +} + +// +// tsSLList::insert() +// (itemBefore might be the list header object and therefore +// will not always be of type T) +// +template < class T > +inline void tsSLList < T > :: insert ( T &item, tsSLNode < T > &itemBefore ) +{ + tsSLNode < T > &node = item; + node.pNext = itemBefore.pNext; + itemBefore.pNext = &item; +} + +// +// tsSLList::add () +// +template < class T > +inline void tsSLList < T > :: add ( T &item ) +{ + this->insert ( item, *this ); +} + +// +// tsSLList::get () +// +template < class T > +inline T * tsSLList < T > :: get () +{ + tsSLNode < T > *pThisNode = this; + T *pItem = pThisNode->pNext; + pThisNode->removeNextItem (); + return pItem; +} + +// +// tsSLList::pop () +// +template < class T > +inline T * tsSLList < T > :: pop () +{ + return this->get (); +} + +// +// tsSLList::push () +// +template +inline void tsSLList < T > :: push ( T &item ) +{ + this->add (item); +} + +template +inline T * tsSLList < T > :: first () const +{ + const tsSLNode < T > *pThisNode = this; + return pThisNode->pNext; +} + +template +inline void tsSLList < T > :: remove ( T &itemBefore ) +{ + tsSLNode < T > *pBeforeNode = &itemBefore; + tsSLNode < T > *pAfterNode = pBeforeNode->pNext; + pBeforeNode->pNext = pAfterNode->pNext; +} + +template +inline tsSLIterConst tsSLList < T > :: firstIter () const +{ + const tsSLNode < T > *pThisNode = this; + return tsSLIterConst ( pThisNode->pNext ); +} + +template +inline tsSLIter tsSLList < T > :: firstIter () +{ + tsSLNode < T > *pThisNode = this; + return tsSLIter ( pThisNode->pNext ); +} + +template +inline tsSLIterConst tsSLList < T > :: invalidConstIter () +{ + return tsSLIterConst ( 0 ); +} + +template +inline tsSLIter tsSLList < T > :: invalidIter () +{ + return tsSLIter ( 0 ); +} + +////////////////////////////////////////// +// +// tsSLIterConst inline member functions +// +////////////////////////////////////////// + +template < class T > +inline tsSLIterConst::tsSLIterConst ( const T *pInitialEntry ) : + pEntry ( pInitialEntry ) +{ +} + +template < class T > +inline tsSLIterConst::tsSLIterConst () : + pEntry ( 0 ) +{ +} + +template < class T > +inline bool tsSLIterConst::valid () const +{ + return this->pEntry != 0; +} + +template < class T > +inline bool tsSLIterConst::operator == ( const tsSLIterConst &rhs ) const +{ + return this->pEntry == rhs.pConstEntry; +} + +template < class T > +inline bool tsSLIterConst::operator != (const tsSLIterConst &rhs) const +{ + return this->pEntry != rhs.pConstEntry; +} + +template < class T > +inline tsSLIterConst & tsSLIterConst::operator = ( const tsSLIterConst & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template < class T > +inline const T & tsSLIterConst::operator * () const +{ + return *this->pEntry; +} + +template < class T > +inline const T * tsSLIterConst::operator -> () const +{ + return this->pEntry; +} + +template < class T > +inline tsSLIterConst & tsSLIterConst::operator ++ () // prefix ++ +{ + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return *this; +} + +template < class T > +inline tsSLIterConst tsSLIterConst::operator ++ ( int ) // postfix ++ +{ + const tsSLIterConst tmp = *this; + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return tmp; +} + +template +inline const T * tsSLIterConst < T > :: pointer () const +{ + return this->pEntry; +} + +////////////////////////////////////////// +// +// tsSLIter inline member functions +// +////////////////////////////////////////// + +template < class T > +inline tsSLIter::tsSLIter ( T *pInitialEntry ) : + pEntry ( pInitialEntry ) +{ +} + +template < class T > +inline tsSLIter::tsSLIter () : + pEntry ( 0 ) +{ +} + +template < class T > +inline bool tsSLIter::valid () const +{ + return this->pEntry != 0; +} + +template < class T > +inline bool tsSLIter::operator == ( const tsSLIter &rhs ) const +{ + return this->pEntry == rhs.pEntry; +} + +template < class T > +inline bool tsSLIter::operator != ( const tsSLIter &rhs ) const +{ + return this->pEntry != rhs.pEntry; +} + +template < class T > +inline tsSLIter & tsSLIter::operator = ( const tsSLIter & rhs ) +{ + this->pEntry = rhs.pEntry; + return *this; +} + +template < class T > +inline T & tsSLIter::operator * () const +{ + return *this->pEntry; +} + +template < class T > +inline T * tsSLIter::operator -> () const +{ + return this->pEntry; +} + +template < class T > +inline tsSLIter & tsSLIter::operator ++ () // prefix ++ +{ + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return *this; +} + +template < class T > +inline tsSLIter tsSLIter::operator ++ ( int ) // postfix ++ +{ + const tsSLIter tmp = *this; + const tsSLNode < T > *pCurNode = this->pEntry; + this->pEntry = pCurNode->pNext; + return tmp; +} + +template +inline T * tsSLIter < T > :: pointer () const +{ + return this->pEntry; +} + +#endif // tsSLListh diff --git a/src/libCom/dbmf/dbmf.c b/src/libCom/dbmf/dbmf.c new file mode 100644 index 000000000..4b47d8c02 --- /dev/null +++ b/src/libCom/dbmf/dbmf.c @@ -0,0 +1,238 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jim Kowalkowski and Marty Kraimer + * Date: 4/97 + * + * Intended for applications that create and free requently + * + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "ellLib.h" +#include "dbmf.h" + +/*Default values for dblfInit */ +#define DBMF_SIZE 64 +#define DBMF_INITIAL_ITEMS 10 + +typedef struct chunkNode {/*control block for each set of chunkItems*/ + ELLNODE node; + void *pchunk; + int nNotFree; +}chunkNode; + +typedef struct itemHeader{ + void *pnextFree; + chunkNode *pchunkNode; +}itemHeader; + +typedef struct dbmfPrivate { + ELLLIST chunkList; + epicsMutexId lock; + size_t size; + size_t allocSize; + int chunkItems; + size_t chunkSize; + int nAlloc; + int nFree; + int nGtSize; + void *freeList; +} dbmfPrivate; +dbmfPrivate dbmfPvt; +static dbmfPrivate *pdbmfPvt = NULL; +int dbmfDebug=0; + +int epicsShareAPI dbmfInit(size_t size, int chunkItems) +{ + if(pdbmfPvt) { + printf("dbmfInit: Already initialized\n"); + return(-1); + } + pdbmfPvt = &dbmfPvt; + ellInit(&pdbmfPvt->chunkList); + pdbmfPvt->lock = epicsMutexMustCreate(); + /*allign to at least a double*/ + pdbmfPvt->size = size + size%sizeof(double); + pdbmfPvt->allocSize = pdbmfPvt->size + sizeof(itemHeader); + pdbmfPvt->chunkItems = chunkItems; + pdbmfPvt->chunkSize = pdbmfPvt->allocSize * pdbmfPvt->chunkItems; + pdbmfPvt->nAlloc = 0; + pdbmfPvt->nFree = 0; + pdbmfPvt->nGtSize = 0; + pdbmfPvt->freeList = NULL; + return(0); +} + + +void* epicsShareAPI dbmfMalloc(size_t size) +{ + void **pnextFree; + void **pfreeList; + char *pmem = NULL; + chunkNode *pchunkNode; + itemHeader *pitemHeader; + + if(!pdbmfPvt) dbmfInit(DBMF_SIZE,DBMF_INITIAL_ITEMS); + epicsMutexMustLock(pdbmfPvt->lock); + pfreeList = &pdbmfPvt->freeList; + if(*pfreeList == NULL) { + int i; + size_t nbytesTotal; + + if(dbmfDebug) printf("dbmfMalloc allocating new storage\n"); + nbytesTotal = pdbmfPvt->chunkSize + sizeof(chunkNode); + pmem = (char *)malloc(nbytesTotal); + if(!pmem) { + epicsMutexUnlock(pdbmfPvt->lock); + printf("dbmfMalloc malloc failed\n"); + return(NULL); + } + pchunkNode = (chunkNode *)(pmem + pdbmfPvt->chunkSize); + pchunkNode->pchunk = pmem; + pchunkNode->nNotFree=0; + ellAdd(&pdbmfPvt->chunkList,&pchunkNode->node); + for(i=0; ichunkItems; i++) { + pitemHeader = (itemHeader *)pmem; + pitemHeader->pchunkNode = pchunkNode; + pnextFree = &pitemHeader->pnextFree; + *pnextFree = *pfreeList; *pfreeList = (void *)pmem; + pdbmfPvt->nFree++; + pmem += pdbmfPvt->allocSize; + } + } + if(size<=pdbmfPvt->size) { + pnextFree = *pfreeList; *pfreeList = *pnextFree; + pmem = (void *)pnextFree; + pdbmfPvt->nAlloc++; pdbmfPvt->nFree--; + pitemHeader = (itemHeader *)pnextFree; + pitemHeader->pchunkNode->nNotFree += 1; + } else { + pmem = malloc(sizeof(itemHeader) + size); + if(!pmem) { + epicsMutexUnlock(pdbmfPvt->lock); + printf("dbmfMalloc malloc failed\n"); + return(NULL); + } + pdbmfPvt->nAlloc++; + pdbmfPvt->nGtSize++; + pitemHeader = (itemHeader *)pmem; + pitemHeader->pchunkNode = NULL; + if(dbmfDebug) printf("dbmfMalloc: size %lu mem %p\n", + (unsigned long)size,pmem); + } + epicsMutexUnlock(pdbmfPvt->lock); + return((void *)(pmem + sizeof(itemHeader))); +} + +char * epicsShareAPI dbmfStrdup(unsigned char *str) +{ + size_t len = strlen((char *) str); + char *buf = dbmfMalloc(len + 1); + strcpy(buf, (char *) str); + return buf; +} + +void epicsShareAPI dbmfFree(void* mem) +{ + char *pmem = (char *)mem; + chunkNode *pchunkNode; + itemHeader *pitemHeader; + + if(!mem) return; + if(!pdbmfPvt) { + printf("dbmfFree called but dbmfInit never called\n"); + return; + } + pmem -= sizeof(itemHeader); + epicsMutexMustLock(pdbmfPvt->lock); + pitemHeader = (itemHeader *)pmem; + if(!pitemHeader->pchunkNode) { + if(dbmfDebug) printf("dbmfGree: mem %p\n",pmem); + free((void *)pmem); pdbmfPvt->nAlloc--; + }else { + void **pfreeList = &pdbmfPvt->freeList; + void **pnextFree = &pitemHeader->pnextFree; + + pchunkNode = pitemHeader->pchunkNode; + pchunkNode->nNotFree--; + *pnextFree = *pfreeList; *pfreeList = pnextFree; + pdbmfPvt->nAlloc--; pdbmfPvt->nFree++; + } + epicsMutexUnlock(pdbmfPvt->lock); +} + +int epicsShareAPI dbmfShow(int level) +{ + if(pdbmfPvt==NULL) { + printf("Never initialized\n"); + return(0); + } + printf("size %lu allocSize %lu chunkItems %d ", + (unsigned long)pdbmfPvt->size, + (unsigned long)pdbmfPvt->allocSize,pdbmfPvt->chunkItems); + printf("nAlloc %d nFree %d nChunks %d nGtSize %d\n", + pdbmfPvt->nAlloc,pdbmfPvt->nFree, + ellCount(&pdbmfPvt->chunkList),pdbmfPvt->nGtSize); + if(level>0) { + chunkNode *pchunkNode; + + pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList); + while(pchunkNode) { + printf("pchunkNode %p nNotFree %d\n", + (void*)pchunkNode,pchunkNode->nNotFree); + pchunkNode = (chunkNode *)ellNext(&pchunkNode->node); + } + } + if(level>1) { + void **pnextFree;; + + epicsMutexMustLock(pdbmfPvt->lock); + pnextFree = (void**)pdbmfPvt->freeList; + while(pnextFree) { + printf("%p\n",*pnextFree); + pnextFree = (void**)*pnextFree; + } + epicsMutexUnlock(pdbmfPvt->lock); + } + return(0); +} + +void epicsShareAPI dbmfFreeChunks(void) +{ + chunkNode *pchunkNode; + chunkNode *pnext;; + + if(!pdbmfPvt) { + printf("dbmfFreeChunks called but dbmfInit never called\n"); + return; + } + epicsMutexMustLock(pdbmfPvt->lock); + if(pdbmfPvt->nFree + != (pdbmfPvt->chunkItems * ellCount(&pdbmfPvt->chunkList))) { + printf("dbmfFinish: not all free\n"); + epicsMutexUnlock(pdbmfPvt->lock); + return; + } + pchunkNode = (chunkNode *)ellFirst(&pdbmfPvt->chunkList); + while(pchunkNode) { + pnext = (chunkNode *)ellNext(&pchunkNode->node); + ellDelete(&pdbmfPvt->chunkList,&pchunkNode->node); + free(pchunkNode->pchunk); + pchunkNode = pnext; + } + pdbmfPvt->nFree = 0; pdbmfPvt->freeList = NULL; + epicsMutexUnlock(pdbmfPvt->lock); +} diff --git a/src/libCom/dbmf/dbmf.h b/src/libCom/dbmf/dbmf.h new file mode 100644 index 000000000..44395ade3 --- /dev/null +++ b/src/libCom/dbmf/dbmf.h @@ -0,0 +1,45 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Author: Jim Kowalkowski and Marty Kraimer + * Date: 4/97 + * + * A library to manage storage that is allocated and then freed. + */ +#ifndef DBMF_H +#define DBMF_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsShareAPI dbmfInit(size_t size, int chunkItems); +epicsShareFunc void * epicsShareAPI dbmfMalloc(size_t bytes); +epicsShareFunc char * epicsShareAPI dbmfStrdup(unsigned char *str); +epicsShareFunc void epicsShareAPI dbmfFree(void* bytes); +epicsShareFunc void epicsShareAPI dbmfFreeChunks(void); +epicsShareFunc int epicsShareAPI dbmfShow(int level); + +/* Rules: + * 1) Size is always made a multiple of 8. + * 2) if dbmfInit is not called before one of the other routines then it + * is automatically called with size=64 and chunkItems=10 + * 3) These routines should only be used to allocate storage that will + * shortly thereafter be freed. + * 4) dbmfFreeChunks can only free chunks that contain only free items +*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/ellLib/ellLib.c b/src/libCom/ellLib/ellLib.c new file mode 100644 index 000000000..c570f59d5 --- /dev/null +++ b/src/libCom/ellLib/ellLib.c @@ -0,0 +1,317 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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: John Winans (ANL) + * Date: 07-02-92 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "ellLib.h" + + /**************************************************************************** + * + * This function adds a node to the end of a linked list. + * + *****************************************************************************/ +void ellAdd (ELLLIST *pList, ELLNODE *pNode) +{ + pNode->next = NULL; + pNode->previous = pList->node.previous; + + if (pList->count) + pList->node.previous->next = pNode; + else + pList->node.next = pNode; + + pList->node.previous = pNode; + pList->count++; + + return; +} + /**************************************************************************** + * + * This function concatinates the second linked list to the end of the first + * list. The second list is left empty. Either list (or both) lists may + * be empty at the begining of the operation. + * + *****************************************************************************/ +void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList) +{ + if (pAddList->count == 0) + return; /* Add list is empty, nothing to add. */ + + if (pDstList->count == 0) { + /* Destination list is empty... just transfer the add list over. */ + pDstList->node.next = pAddList->node.next; + pDstList->node.previous = pAddList->node.previous; + pDstList->count = pAddList->count; + } else { + /* Destination list not empty... append the add list. */ + pDstList->node.previous->next = pAddList->node.next; + pAddList->node.next->previous = pDstList->node.previous; + pDstList->node.previous = pAddList->node.previous; + pDstList->count += pAddList->count; + } + + pAddList->count = 0; + pAddList->node.next = NULL; + pAddList->node.previous = NULL; + + return; +} + /**************************************************************************** + * + * This function deletes a specific node from a specified list; + * + *****************************************************************************/ +void ellDelete (ELLLIST *pList, ELLNODE *pNode) +{ + if (pList->node.previous == pNode) + pList->node.previous = pNode->previous; + else + pNode->next->previous = pNode->previous; + + if (pList->node.next == pNode) + pList->node.next = pNode->next; + else + pNode->previous->next = pNode->next; + + pList->count--; + + return; +} + /**************************************************************************** + * + * This function extracts a sublist that starts with pStartNode and ends with + * pEndNode from pSrcList and places it in pDstList. + * + * WRS is unclear as to what happens when pDstList is non-empty at the start + * of the operation. We will place the extracted list at the END of pDstList + * when it is non-empty. + * + *****************************************************************************/ +void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList) +{ + ELLNODE *pnode; + int count; + + /* Cut the list out of the source list (update count later) */ + if (pStartNode->previous != NULL) + pStartNode->previous->next = pEndNode->next; + else + pSrcList->node.next = pEndNode->next; + + if (pEndNode->next != NULL) { + pEndNode->next->previous = pStartNode->previous; + pEndNode->next = NULL; + } + else + pSrcList->node.previous = pStartNode->previous; + + /* Place the sublist into the destination list */ + pStartNode->previous = pDstList->node.previous; + if (pDstList->count) + pDstList->node.previous->next = pStartNode; + else + pDstList->node.next = pStartNode; + + pDstList->node.previous = pEndNode; + + /* Adjust the counts */ + pnode = pStartNode; + count = 1; + while(pnode != pEndNode) + { + pnode = pnode->next; + count++; + } + pSrcList->count -= count; + pDstList->count += count; + + return; +} + /**************************************************************************** + * + * This function returns the first node in the specified list. The node is + * removed from the list. If the list is empty, NULL will be returned. + * + *****************************************************************************/ +ELLNODE * ellGet (ELLLIST *pList) +{ + ELLNODE *pnode = pList->node.next; + + if (pnode != NULL) + ellDelete(pList, pnode); + + return pnode; +} + /**************************************************************************** + * + * This function inserts the specified node pNode after pPrev in the list + * plist. If pPrev is NULL, then pNode will be inserted at the head of the + * list. + * + *****************************************************************************/ +void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode) +{ + if (pPrev != NULL) { + pNode->previous = pPrev; + pNode->next = pPrev->next; + pPrev->next = pNode; + } else { + pNode->previous = NULL; + pNode->next = plist->node.next; + plist->node.next = pNode; + } + + if (pNode->next == NULL) + plist->node.previous = pNode; + else + pNode->next->previous = pNode; + + plist->count++; + + return; +} + /**************************************************************************** + * + * This function returns the nodeNum'th element in pList. If there is no + * nodeNum'th node in the list, NULL will be returned. + * + *****************************************************************************/ +ELLNODE * ellNth (ELLLIST *pList, int nodeNum) +{ + ELLNODE *pnode; + + if ((nodeNum < 1) || (pList->count == 0)) + return NULL; + + if (nodeNum > pList->count/2) { + if (nodeNum > pList->count) + return NULL; + + pnode = pList->node.previous; + nodeNum = pList->count - nodeNum; + while(nodeNum) { + pnode = pnode->previous; + nodeNum--; + } + return pnode; + } + + pnode = pList->node.next; + while (--nodeNum > 0) + pnode = pnode->next; + + return pnode; +} + /**************************************************************************** + * + * This function returns the node, nStep nodes forward from pNode. If there is + * no node that many steps from pNode, NULL will be returned. + * + *****************************************************************************/ +ELLNODE * ellNStep (ELLNODE *pNode, int nStep) +{ + if (nStep > 0) { + while ((pNode != NULL) && nStep) { + pNode = pNode->next; + nStep--; + } + } else { + while ((pNode != NULL) && nStep) { + pNode = pNode->previous; + nStep++; + } + } + return pNode; +} + /**************************************************************************** + * + * This function returns the node number of pNode within pList. If the node is + * not in pList, -1 is returned. Note that the first node is 1. + * + *****************************************************************************/ +int ellFind (ELLLIST *pList, ELLNODE *pNode) +{ + ELLNODE *got = pList->node.next; + int count = 1; + + while ((got != pNode) && (got != NULL)) { + got = got->next; + count++; + } + if (got == NULL) + return -1; + + return count; +} + /**************************************************************************** + * + * This function frees the nodes in a list. It makes the list into an empty + * list, and calls freeFunc() for all the nodes that are (were) in that list. + * + * NOTE: the nodes in the list are free()'d on the assumption that the node + * structures were malloc()'d one-at-a-time and that the ELLNODE structure is + * the first member of the parent structure. + * + *****************************************************************************/ +void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc) +{ + ELLNODE *nnode = pList->node.next; + ELLNODE *pnode; + + while (nnode != NULL) + { + pnode = nnode; + nnode = nnode->next; + freeFunc(pnode); + } + pList->node.next = NULL; + pList->node.previous = NULL; + pList->count = 0; +} + + /**************************************************************************** + * + * This function verifies that the list is consistent. + * joh 12-12-97 + * + *****************************************************************************/ +void ellVerify (ELLLIST *pList) +{ + ELLNODE *pNode; + ELLNODE *pNext; + int count = 0; + + assert (pList); + + pNode = ellFirst(pList); + if (pNode) { + assert (ellPrevious(pNode) == NULL); + while (1) { + count++; + pNext = ellNext(pNode); + if (pNext) { + assert (ellPrevious(pNext) == pNode); + } else { + break; + } + pNode = pNext; + } + assert (ellNext(pNode) == NULL); + } + + assert (pNode == ellLast(pList)); + assert (count == ellCount(pList)); +} diff --git a/src/libCom/ellLib/ellLib.h b/src/libCom/ellLib/ellLib.h new file mode 100644 index 000000000..2432b773b --- /dev/null +++ b/src/libCom/ellLib/ellLib.h @@ -0,0 +1,66 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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: John Winans (ANL) + * Date: 07-02-92 + */ +#ifndef INC_ellLib_H +#define INC_ellLib_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ELLNODE { + struct ELLNODE *next; + struct ELLNODE *previous; +} ELLNODE; + +#define ELLNODE_INIT {NULL, NULL} + +typedef struct ELLLIST { + ELLNODE node; + int count; +} ELLLIST; + +#define ELLLIST_INIT {ELLNODE_INIT, 0} + +typedef void (*FREEFUNC)(void *); + +#define ellInit(PLIST) {\ + (PLIST)->node.next = (PLIST)->node.previous = NULL;\ + (PLIST)->count = 0;\ +} +#define ellCount(PLIST) ((PLIST)->count) +#define ellFirst(PLIST) ((PLIST)->node.next) +#define ellLast(PLIST) ((PLIST)->node.previous) +#define ellNext(PNODE) ((PNODE)->next) +#define ellPrevious(PNODE) ((PNODE)->previous) +#define ellFree(PLIST) ellFree2(PLIST, free) + +epicsShareFunc void ellAdd (ELLLIST *pList, ELLNODE *pNode); +epicsShareFunc void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList); +epicsShareFunc void ellDelete (ELLLIST *pList, ELLNODE *pNode); +epicsShareFunc void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList); +epicsShareFunc ELLNODE * ellGet (ELLLIST *pList); +epicsShareFunc void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode); +epicsShareFunc ELLNODE * ellNth (ELLLIST *pList, int nodeNum); +epicsShareFunc ELLNODE * ellNStep (ELLNODE *pNode, int nStep); +epicsShareFunc int ellFind (ELLLIST *pList, ELLNODE *pNode); +epicsShareFunc void ellFree2 (ELLLIST *pList, FREEFUNC freeFunc); +epicsShareFunc void ellVerify (ELLLIST *pList); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_ellLib_H */ diff --git a/src/libCom/env/bldEnvData.pl b/src/libCom/env/bldEnvData.pl new file mode 100644 index 000000000..d8a44ad3f --- /dev/null +++ b/src/libCom/env/bldEnvData.pl @@ -0,0 +1,125 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# Author: Kay-Uwe Kasemir +# based on bldEnvData shell scripts, Andrew Johnson (RGO) +# Date: 1-30-97 +# +# Experimental Physics and Industrial Control System (EPICS) +# +# tool to build envData.c from envDefs.h and config/CONFIG*ENV + +use Cwd 'abs_path'; + +# We need exactly one argument: +$usage="Usage:\tbldEnvData "; +die $usage unless $#ARGV==0; + +$config_dir = abs_path($ARGV[0]); +$config_env = "${config_dir}/CONFIG_ENV"; +$config_site_env = "${config_dir}/CONFIG_SITE_ENV"; + +$env_dir = abs_path("../env"); +$env_defs = "${env_dir}/envDefs.h"; + +$out_name = "envData.c"; + +# $tool = basename of this script +$tool=$0; +$tool=~ s'.*/''; + + +# Start by extracting the ENV_PARAM declarations from $env_defs +# i.e. gather the names of params we are interested in: +# +open SRC, "<$env_defs" or die "Cannot open $env_defs"; +while () { + if (m/epicsShareExtern\s+const\s+ENV_PARAM\s+([A-Za-z_]\w*)\s*;/) { + $need_var{$1} = 1; + } +} +close SRC; + + +# Read the default values from the config file into shell variables +sub GetVars { + my ($filename) = @_; + open IN, "<$filename" or die "Cannot read $filename"; + while () { + # Discard comments, carriage returns and trailing whitespace + next if m/^ \s* \#/x; + chomp; + if (m/^ \s* ([A-Za-z_]\w*) \s* = \s* ( \S* | ".*" ) \s* $/x) { + my ($var, $val) = ($1, $2); + next unless $need_var{$var}; + $val =~ s/^"(.*)"$/$1/; + $value{$var} = $val; + } + } + close IN; +} + +GetVars ($config_env); +GetVars ($config_site_env); + +# Generate header file +# + +print "Generating $out_name\n"; + +open OUT, ">$out_name" or die "cannot create $out_name"; + +# Write header +print OUT "/* $out_name\n", + " *\n", + " * Created " . localtime() . "\n", + " * by $tool from files:\n", + " *\t$env_defs\n", + " *\t$config_env\n", + " *\t$config_site_env\n", + " */\n", + "\n", + "#define epicsExportSharedSymbols\n", + "#include \"envDefs.h\"\n", + "\n"; + + +# Print variables +# +@vars = sort keys %need_var; +foreach $var (@vars) { + $default = exists $value{$var} ? $value{$var} : ""; + print "Warning: No default value found for $var\n" + unless exists $value{$var}; + + print OUT "epicsShareDef const ENV_PARAM $var =\n", + "\t{\"$var\", \"$default\"};\n"; +} + +# Now create an array pointing to all parameters + +print OUT "\n", + "epicsShareDef const ENV_PARAM* env_param_list[] = {\n"; + +# Contents are the addresses of each parameter +foreach $var (@vars) { + print OUT "\t&$var,\n"; +} + +# Finally finish list with 0 +print OUT "\t0\n", + "};\n", + "\n", + "/*\tEOF $out_name */\n"; + +close OUT; + +# EOF bldEnvData.pl diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h new file mode 100644 index 000000000..ebda6e834 --- /dev/null +++ b/src/libCom/env/envDefs.h @@ -0,0 +1,100 @@ +/*************************************************************************\ +* 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 is 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: Roger A. Cole + * Date: 07-20-91 + * + */ +/**************************************************************************** +* TITLE envDefs.h - definitions for environment get/set routines +* +* DESCRIPTION +* This file defines the environment parameters for EPICS. These +* ENV_PARAM's are created in envData.c by bldEnvData for +* use by EPICS programs running under UNIX and VxWorks. +* +* User programs can define their own environment parameters for their +* own use--the only caveat is that such parameters aren't automatically +* setup by EPICS. +* +*****************************************************************************/ + +#ifndef envDefsH +#define envDefsH + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +typedef struct envParam { + char *name; /* text name of the parameter */ + char *pdflt; +} ENV_PARAM; + +/* + * bldEnvData looks for "epicsShareExtern const ENV_PARAM" + */ +epicsShareExtern const ENV_PARAM EPICS_CA_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CA_CONN_TMO; +epicsShareExtern const ENV_PARAM EPICS_CA_AUTO_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CA_REPEATER_PORT; +epicsShareExtern const ENV_PARAM EPICS_CA_SERVER_PORT; +epicsShareExtern const ENV_PARAM EPICS_CA_MAX_ARRAY_BYTES; +epicsShareExtern const ENV_PARAM EPICS_CA_MAX_SEARCH_PERIOD; +epicsShareExtern const ENV_PARAM EPICS_CA_NAME_SERVERS; +epicsShareExtern const ENV_PARAM EPICS_CAS_INTF_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_IGNORE_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_AUTO_BEACON_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_ADDR_LIST; +epicsShareExtern const ENV_PARAM EPICS_CAS_SERVER_PORT; +epicsShareExtern const ENV_PARAM EPICS_CA_BEACON_PERIOD; /* deprecated */ +epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PERIOD; +epicsShareExtern const ENV_PARAM EPICS_CAS_BEACON_PORT; +epicsShareExtern const ENV_PARAM EPICS_TIMEZONE; +epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_LIMIT; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_NAME; +epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_COMMAND; +epicsShareExtern const ENV_PARAM EPICS_CMD_PROTO_PORT; +epicsShareExtern const ENV_PARAM EPICS_AR_PORT; +epicsShareExtern const ENV_PARAM IOCSH_PS1; +epicsShareExtern const ENV_PARAM IOCSH_HISTSIZE; + +epicsShareExtern const ENV_PARAM *env_param_list[]; + +struct in_addr; + +epicsShareFunc char * epicsShareAPI + envGetConfigParam(const ENV_PARAM *pParam, int bufDim, char *pBuf); +epicsShareFunc const char * epicsShareAPI + envGetConfigParamPtr(const ENV_PARAM *pParam); +epicsShareFunc long epicsShareAPI + envPrtConfigParam(const ENV_PARAM *pParam); +epicsShareFunc long epicsShareAPI + envGetInetAddrConfigParam(const ENV_PARAM *pParam, struct in_addr *pAddr); +epicsShareFunc long epicsShareAPI + envGetDoubleConfigParam(const ENV_PARAM *pParam, double *pDouble); +epicsShareFunc long epicsShareAPI + envGetLongConfigParam(const ENV_PARAM *pParam, long *pLong); +epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam + (const ENV_PARAM *pEnv, unsigned short defaultPort); +epicsShareFunc long epicsShareAPI epicsPrtEnvParams(void); +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value); +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /*envDefsH*/ + diff --git a/src/libCom/env/envSubr.c b/src/libCom/env/envSubr.c new file mode 100644 index 000000000..c6664f738 --- /dev/null +++ b/src/libCom/env/envSubr.c @@ -0,0 +1,413 @@ +/*************************************************************************\ +* 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: Roger A. Cole + * Date: 07-20-91 + */ +/*+/mod*********************************************************************** +* TITLE envSubr.c - routines to get and set EPICS environment parameters +* +* DESCRIPTION +* These routines are oriented for use with EPICS environment +* parameters under UNIX and VxWorks. They may be used for other +* purposes, as well. +* +* Many EPICS environment parameters are predefined in envDefs.h. +* +* QUICK REFERENCE +* #include "envDefs.h" +* ENV_PARAM param; +* char *envGetConfigParamPtr( pParam ) +* char *envGetConfigParam( pParam, bufDim, pBuf ) +* long envGetLongConfigParam( pParam, pLong ) +* long envGetDoubleConfigParam( pParam, pDouble ) +* long envGetInetAddrConfigParam( pParam, pAddr ) +* long envPrtConfigParam( pParam ) +* +* SEE ALSO +* $epics/share/bin/envSetupParams, envDefs.h +* +*-***************************************************************************/ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdlib.h" +#include "epicsStdioRedirect.h" +#include "errMdef.h" +#include "errlog.h" +#include "envDefs.h" +#include "epicsAssert.h" +#include "osiSock.h" + + +/*+/subr********************************************************************** +* NAME envGetConfigParamPtr - returns a pointer to the configuration +* parameter value string +* +* DESCRIPTION +* Returns a pointer to a configuration parameter value. +* If the configuration parameter isn't found in the environment, +* then a pointer to the default value for the parameter is copied. +* If no parameter is found and there is no default, then +* NULL is returned. +* +* RETURNS +* pointer to the environment variable value string, or +* NULL if no parameter value and no default value was found +* +* EXAMPLES +* 1. Get the value for the EPICS-defined environment parameter +* EPICS_TS_NTP_INET. +* +* #include "envDefs.h" +* const char *pStr; +* +* pStr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); +* if (pStr) { +* printf("NTP time server is: %s\n", pStr); +* } +* +*-*/ +const char * epicsShareAPI envGetConfigParamPtr( +const ENV_PARAM *pParam /* I pointer to config param structure */ +) +{ + const char *pEnv; /* pointer to environment string */ + + pEnv = getenv(pParam->name); + + if (pEnv == NULL) { + pEnv = pParam->pdflt; + } + + if (pEnv) { + if (pEnv[0u] == '\0') { + pEnv = NULL; + } + } + + return pEnv; +} + + +/*+/subr********************************************************************** +* NAME envGetConfigParam - get value of a configuration parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it +* into the caller's buffer. If the configuration parameter +* isn't found in the environment, then the default value for +* the parameter is copied. If no parameter is found and there +* is no default, then '\0' is copied and NULL is returned. +* +* RETURNS +* pointer to callers buffer, or +* NULL if no parameter value or default value was found +* +* EXAMPLES +* 1. Get the value for the EPICS-defined environment parameter +* EPICS_TS_NTP_INET. +* +* #include "envDefs.h" +* char temp[80]; +* +* printf("NTP time server is: %s\n", +* envGetConfigParam(&EPICS_TS_NTP_INET, sizeof(temp), temp)); +* +* 2. Get the value for the DISPLAY environment parameter under UNIX. +* +* #include "envDefs.h" +* char temp[80]; +* ENV_PARAM display={"DISPLAY",""} +* +* if (envGetConfigParam(&display, sizeof(temp), temp) == NULL) +* printf("DISPLAY isn't defined\n"); +* else +* printf("DISPLAY is %s\n", temp); +* +*-*/ +char * epicsShareAPI envGetConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +int bufDim, /* I dimension of parameter buffer */ +char *pBuf /* I pointer to parameter buffer */ +) +{ + const char *pEnv; /* pointer to environment string */ + + pEnv = envGetConfigParamPtr(pParam); + if (!pEnv) { + return NULL; + } + + strncpy(pBuf, pEnv, bufDim-1); + pBuf[bufDim-1] = '\0'; + + return pBuf; +} + +/*+/subr********************************************************************** +* NAME envGetDoubleConfigParam - get value of a double configuration parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it into the +* caller's real (double) buffer. If the configuration parameter isn't +* found in the environment, then the default value for the parameter +* is copied. +* +* If no parameter is found and there is no default, then -1 is +* returned and the caller's buffer is unmodified. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* EXAMPLE +* 1. Get the value for the real environment parameter EPICS_THRESHOLD. +* +* #include "envDefs.h" +* double threshold; +* long status; +* +* status = envGetDoubleConfigParam(&EPICS_THRESHOLD, &threshold); +* if (status == 0) { +* printf("the threshold is: %lf\n", threshold); +* } +* else { +* printf("%s could not be found or was not a real number\n", +* EPICS_THRESHOLD.name); +* } +* +*-*/ +long epicsShareAPI envGetDoubleConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +double *pDouble /* O pointer to place to store value */ +) +{ + char text[128]; + char *ptext; + int count; + + ptext = envGetConfigParam(pParam, sizeof text, text); + if (ptext != NULL) { + count = epicsScanDouble(text, pDouble); + if (count == 1) { + return 0; + } + (void)fprintf(stderr,"Unable to find a real number in %s=%s\n", + pParam->name, text); + } + + return -1; +} + +/*+/subr********************************************************************** +* NAME envGetInetAddrConfigParam - get value of an inet addr config parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it into +* the caller's (struct in_addr) buffer. If the configuration parameter +* isn't found in the environment, then the default value for +* the parameter is copied. +* +* If no parameter is found and there is no default, then -1 is +* returned and the callers buffer is unmodified. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* EXAMPLE +* 1. Get the value for the inet address environment parameter EPICS_INET. +* +* #include "envDefs.h" +* struct in_addr addr; +* long status; +* +* status = envGetInetAddrConfigParam(&EPICS_INET, &addr); +* if (status == 0) { +* printf("the s_addr is: %x\n", addr.s_addr); +* } +* else { +* printf("%s could not be found or was not an inet address\n", +* EPICS_INET.name); +* } +* +*-*/ +long epicsShareAPI envGetInetAddrConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +struct in_addr *pAddr /* O pointer to struct to receive inet addr */ +) +{ + char text[128]; + char *ptext; + long status; + struct sockaddr_in sin; + + ptext = envGetConfigParam(pParam, sizeof text, text); + if (ptext) { + status = aToIPAddr (text, 0u, &sin); + if (status == 0) { + *pAddr = sin.sin_addr; + return 0; + } + (void)fprintf(stderr,"Unable to find an IP address or valid host name in %s=%s\n", + pParam->name, text); + } + return -1; +} + +/*+/subr********************************************************************** +* NAME envGetLongConfigParam - get value of an integer config parameter +* +* DESCRIPTION +* Gets the value of a configuration parameter and copies it +* into the caller's integer (long) buffer. If the configuration +* parameter isn't found in the environment, then the default value for +* the parameter is copied. +* +* If no parameter is found and there is no default, then -1 is +* returned and the callers buffer is unmodified. +* +* RETURNS +* 0, or +* -1 if an error is encountered +* +* EXAMPLE +* 1. Get the value as a long for the integer environment parameter +* EPICS_NUMBER_OF_ITEMS. +* +* #include "envDefs.h" +* long count; +* long status; +* +* status = envGetLongConfigParam(&EPICS_NUMBER_OF_ITEMS, &count); +* if (status == 0) { +* printf("and the count is: %d\n", count); +* } +* else { +* printf("%s could not be found or was not an integer\n", +* EPICS_NUMBER_OF_ITEMS.name); +* } +* +*-*/ +long epicsShareAPI envGetLongConfigParam( +const ENV_PARAM *pParam,/* I pointer to config param structure */ +long *pLong /* O pointer to place to store value */ +) +{ + char text[128]; + char *ptext; + int count; + + ptext = envGetConfigParam(pParam, sizeof text, text); + if (ptext) { + count = sscanf(text, "%ld", pLong); + if (count == 1) + return 0; + (void)fprintf(stderr,"Unable to find an integer in %s=%s\n", + pParam->name, text); + } + return -1; +} + +/*+/subr********************************************************************** +* NAME envPrtConfigParam - print value of a configuration parameter +* +* DESCRIPTION +* Prints the value of a configuration parameter. +* +* RETURNS +* 0 +* +* EXAMPLE +* 1. Print the value for the EPICS-defined environment parameter +* EPICS_TS_NTP_INET. +* +* #include "envDefs.h" +* +* envPrtConfigParam(&EPICS_TS_NTP_INET); +* +*-*/ +long epicsShareAPI envPrtConfigParam( +const ENV_PARAM *pParam) /* pointer to config param structure */ +{ + const char *pVal; + + pVal = envGetConfigParamPtr(pParam); + if (pVal == NULL) + fprintf(stdout, "%s is undefined\n", pParam->name); + else + fprintf(stdout,"%s: %s\n", pParam->name, pVal); + return 0; +} + +/*+/subr********************************************************************** +* NAME epicsPrtEnvParams - print value of all configuration parameters +* +* DESCRIPTION +* Prints all configuration parameters and their current value. +* +* RETURNS +* 0 +* +* EXAMPLE +* 1. Print the value for all EPICS-defined environment parameters. +* +* #include "envDefs.h" +* +* epicsPrtEnvParams(); +* +*-*/ +long epicsShareAPI +epicsPrtEnvParams(void) +{ + const ENV_PARAM **ppParam = env_param_list; + + while (*ppParam != NULL) + envPrtConfigParam(*(ppParam++)); + + return 0; +} + +/* + * envGetInetPortConfigParam () + */ +epicsShareFunc unsigned short epicsShareAPI envGetInetPortConfigParam + (const ENV_PARAM *pEnv, unsigned short defaultPort) +{ + long longStatus; + long epicsParam; + + longStatus = envGetLongConfigParam (pEnv, &epicsParam); + if (longStatus!=0) { + epicsParam = (long) defaultPort; + errlogPrintf ("EPICS Environment \"%s\" integer fetch failed\n", pEnv->name); + errlogPrintf ("setting \"%s\" = %ld\n", pEnv->name, epicsParam); + } + + if (epicsParam<=IPPORT_USERRESERVED || epicsParam>USHRT_MAX) { + errlogPrintf ("EPICS Environment \"%s\" out of range\n", pEnv->name); + /* + * Quit if the port is wrong due coding error + */ + assert (epicsParam != (long) defaultPort); + epicsParam = (long) defaultPort; + errlogPrintf ("Setting \"%s\" = %ld\n", pEnv->name, epicsParam); + } + + /* + * ok to clip to unsigned short here because we checked the range + */ + return (unsigned short) epicsParam; +} + diff --git a/src/libCom/error/epicsPrint.h b/src/libCom/error/epicsPrint.h new file mode 100644 index 000000000..db889794a --- /dev/null +++ b/src/libCom/error/epicsPrint.h @@ -0,0 +1,18 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*epicsPrint.h */ + +/*This is now obsolete. Replaced by errlog.h */ +#ifndef INCepicsPrintH +#define INCepicsPrintH + +#include "errlog.h" + +#endif /*INCepicsPrintH*/ diff --git a/src/libCom/error/errMdef.h b/src/libCom/error/errMdef.h new file mode 100644 index 000000000..d6137b6f3 --- /dev/null +++ b/src/libCom/error/errMdef.h @@ -0,0 +1,64 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Error Handling definitions */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INCerrMdefh +#define INCerrMdefh + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTN_SUCCESS(STATUS) ((STATUS)==0) + +#define M_dbAccess (501 <<16) /*Database Access Routines */ +#define M_drvSup (503 <<16) /*Driver Support*/ +#define M_devSup (504 <<16) /*Device Support*/ +#define M_recSup (505 <<16) /*Record Support*/ +#define M_recType (506 <<16) /*Record Type*/ +#define M_record (507 <<16) /*Database Records*/ +#define M_ar (508 <<16) /*Archiver; see arDefs.h*/ +#define M_ts (509 <<16) /*Time Stamp Routines; see tsDefs.h*/ +#define M_arAcc (510 <<16) /*Archive Access Library Routines*/ +#define M_bf (511 <<16) /*Block File Routines; see bfDefs.h*/ +#define M_syd (512 <<16) /*Sync Data Routines; see sydDefs.h*/ +#define M_ppr (513 <<16) /*Portable Plot Routines; see pprPlotDefs.h*/ +#define M_env (514 <<16) /*Environment Routines; see envDefs.h*/ +#define M_gen (515 <<16) /*General Purpose Routines; see genDefs.h*/ +#define M_gpib (516 <<16) /*Gpib driver & device support; see drvGpibInterface.h*/ +#define M_bitbus (517 <<16) /*Bitbus driver & device support; see drvBitBusInterface.h*/ +#define M_dbLib (519 <<16) /*Static Database Access */ +#define M_epvxi (520 <<16) /*VXI Driver*/ +#define M_devLib (521 <<16) /*Device Resource Registration*/ +#define M_asLib (522 <<16) /*Access Security */ +#define M_cas (523 <<16) /*CA server*/ +#define M_casApp (524 <<16) /*CA server application*/ +#define M_bucket (525 <<16) /*Bucket Hash*/ +#define M_gddFuncTbl (526 <<16) /*gdd jump table*/ + +epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength); +epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum); +epicsShareFunc void epicsShareAPI errSymTestPrint(long errNum); +epicsShareFunc int epicsShareAPI errSymBld(void); +epicsShareFunc int epicsShareAPI errSymbolAdd (long errNum,char *name); +epicsShareFunc void epicsShareAPI errSymDump(void); +epicsShareFunc void epicsShareAPI tstErrSymFind(void); + +#ifdef __cplusplus +} +#endif + +#endif /*INCerrMdefh*/ diff --git a/src/libCom/error/errSymLib.c b/src/libCom/error/errSymLib.c new file mode 100644 index 000000000..76b00d16a --- /dev/null +++ b/src/libCom/error/errSymLib.c @@ -0,0 +1,320 @@ +/*************************************************************************\ +* 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 + * errSymLib.c + * Author: Marty Kraimer + * Date: 6-1-90 + * errMessage.c - Handle error messages + *************************************************************************** + * This must ultimately be replaced by a facility that allows remote + * nodes access to the error messages. A message handling communication + * task should be written that allows multiple remote nodes to request + * notification of all error messages. + * For now lets just print messages and last errno via logMsg or printf + *************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsAssert.h" +#include "dbDefs.h" +#include "errMdef.h" +#include "errSymTbl.h" +#include "ellLib.h" +#include "errlog.h" + + +static unsigned short errhash(long errNum); + +typedef struct errnumnode { + ELLNODE node; + long errNum; + struct errnumnode *hashnode; + char *message; + long pad; +} ERRNUMNODE; +#define NHASH 256 + + +static ELLLIST errnumlist = ELLLIST_INIT; +static ERRNUMNODE **hashtable; +static int initialized = FALSE; +extern ERRSYMTAB_ID errSymTbl; + +/**************************************************************** + * ERRSYMBLD + * + * Create the normal ell LIST of sorted error messages nodes + * Followed by linked hash lists - that link together those + * ell nodes that have a common hash number. + * + ***************************************************************/ +int epicsShareAPI errSymBld(void) +{ + ERRSYMBOL *errArray = errSymTbl->symbols; + ERRNUMNODE *perrNumNode = NULL; + ERRNUMNODE *pNextNode = NULL; + ERRNUMNODE **phashnode = NULL; + int i; + int modnum; + unsigned short hashInd; + + if(initialized) return(0); + hashtable = (ERRNUMNODE**)callocMustSucceed + (NHASH, sizeof(ERRNUMNODE*),"errSymBld"); + for (i = 0; i < errSymTbl->nsymbols; i++, errArray++) { + modnum = errArray->errNum >> 16; + if (modnum < 501) { + fprintf(stderr, "errSymBld: ERROR - Module number in errSymTbl < 501 was Module=%lx Name=%s\n", + errArray->errNum, errArray->name); + continue; + } + if ((errSymbolAdd(errArray->errNum, errArray->name)) <0 ) { + fprintf(stderr, "errSymBld: ERROR - errSymbolAdd() failed \n"); + continue; + } + } + perrNumNode = (ERRNUMNODE *) ellFirst(&errnumlist); + while (perrNumNode) { + /* hash each perrNumNode->errNum */ + hashInd = errhash(perrNumNode->errNum); + phashnode = (ERRNUMNODE**)&hashtable[hashInd]; + pNextNode = (ERRNUMNODE*) *phashnode; + /* search for last node (NULL) of hashnode linked list */ + while (pNextNode) { + phashnode = &pNextNode->hashnode; + pNextNode = *phashnode; + } + *phashnode = perrNumNode; + perrNumNode = (ERRNUMNODE *) ellNext((ELLNODE *) perrNumNode); + } + initialized = TRUE; + return(0); +} + + + +/**************************************************************** + * HASH + * returns the hash index of errNum +****************************************************************/ +static unsigned short errhash(long errNum) +{ +unsigned short modnum; +unsigned short errnum; + + modnum = (unsigned short) (errNum >> 16); + errnum = (unsigned short) (errNum & 0xffff); + return((unsigned short)(((modnum - 500) * 20) + errnum) % NHASH); +} + +/**************************************************************** + * ERRSYMBOLADD + * adds symbols to the master errnumlist as compiled from errSymTbl.c + ***************************************************************/ +int epicsShareAPI errSymbolAdd (long errNum,char *name) +{ + ERRNUMNODE *pNew; + + pNew = (ERRNUMNODE*)callocMustSucceed(1,sizeof(ERRNUMNODE),"errSymbolAdd"); + pNew->errNum = errNum; + pNew->message = name; + ellAdd(&errnumlist,(ELLNODE*)pNew); + return(0); +} + +/**************************************************************** + * errRawCopy + ***************************************************************/ +static void errRawCopy ( long statusToDecode, char *pBuf, unsigned bufLength ) +{ + unsigned modnum, errnum; + unsigned nChar; + int status; + + modnum = (unsigned) statusToDecode; + modnum >>= 16; + modnum &= 0xffff; + errnum = (unsigned) statusToDecode; + errnum &= 0xffff; + + if ( bufLength ) { + if ( modnum == 0 ) { + if ( bufLength > 11 ) { + status = sprintf ( pBuf, "err = %d", errnum ); + } + else if ( bufLength > 5 ) { + status = sprintf ( pBuf, "%d", errnum ); + } + else { + strncpy ( pBuf,"", bufLength ); + pBuf[bufLength-1] = '\0'; + status = 0; + } + } + else { + if ( bufLength > 50 ) { + status = sprintf ( pBuf, + "status = (%d,%d) not in symbol table", modnum, errnum ); + } + else if ( bufLength > 25 ) { + status = sprintf ( pBuf, + "status = (%d,%d)", modnum, errnum ); + } + else if ( bufLength > 15 ) { + status = sprintf ( pBuf, + "(%d,%d)", modnum, errnum ); + } + else { + strncpy ( pBuf, + "", bufLength); + pBuf[bufLength-1] = '\0'; + status = 0; + } + } + assert (status >= 0 ); + nChar = (unsigned) status; + assert ( nChar < bufLength ); + } +} + +/**************************************************************** + * errSymLookup + ***************************************************************/ +void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength) +{ + unsigned modNum; + unsigned hashInd; + ERRNUMNODE *pNextNode; + ERRNUMNODE **phashnode = NULL; + + if(!initialized) errSymBld(); + + modNum = (unsigned) status; + modNum >>= 16; + modNum &= 0xffff; + if ( modNum <= 500 ) { + const char * pStr = strerror ((int) status); + if ( pStr ) { + strncpy(pBuf, pStr,bufLength); + pBuf[bufLength-1] = '\0'; + return; + } + } + else { + hashInd = errhash(status); + phashnode = (ERRNUMNODE**)&hashtable[hashInd]; + pNextNode = *phashnode; + while(pNextNode) { + if(pNextNode->errNum==status){ + strncpy(pBuf, pNextNode->message, bufLength); + pBuf[bufLength-1] = '\0'; + return; + } + phashnode = &pNextNode->hashnode; + pNextNode = *phashnode; + } + } + errRawCopy(status, pBuf, bufLength); +} + +/**************************************************************** + * errSymDump + ***************************************************************/ +void epicsShareAPI errSymDump(void) +{ +ERRNUMNODE **phashnode = NULL; +ERRNUMNODE *pNextNode; +int i; +int modnum; +int errnum; +int msgcount; +int firstTime; + + if (!initialized) errSymBld(); + + msgcount = 0; + printf("errSymDump: number of hash slots=%d\n", NHASH); + for ( i=0; i < NHASH; i++) { + phashnode = &hashtable[i]; + pNextNode = *phashnode; + firstTime=1; + while (pNextNode) { + if (firstTime) { + printf("HASHNODE=%d\n", i); + firstTime=0; + } + modnum = pNextNode->errNum >> 16; + errnum = pNextNode->errNum & 0xffff; + printf("\tmod %d num %d \"%s\"\n" + , modnum , errnum , pNextNode->message); + msgcount++; + phashnode = &pNextNode->hashnode; + pNextNode = *phashnode; + } + } + printf("\nerrSymDump: total number of error messages=%d\n", msgcount); +} + + +/**************************************************************** + * errSymTestPrint + ***************************************************************/ +void epicsShareAPI errSymTestPrint(long errNum) +{ + char message[256]; + unsigned short modnum; + unsigned short errnum; + + if (!initialized) errSymBld(); + + message[0] = '\0'; + modnum = (unsigned short) (errNum >> 16); + errnum = (unsigned short) (errNum & 0xffff); + if (modnum < 501) { + fprintf(stderr, "Usage: errSymTestPrint(long errNum) \n"); + fprintf(stderr, "errSymTestPrint: module number < 501 \n"); + return; + } + errSymLookup(errNum, message, sizeof(message)); + if ( message[0] == '\0' ) return; + printf("module %hu number %hu message=\"%s\"\n", + modnum, errnum, message); + return; +} + +/**************************************************************** + * ERRSYMTEST +****************************************************************/ +void epicsShareAPI errSymTest(unsigned short modnum, + unsigned short begErrNum, unsigned short endErrNum) +{ + long errNum; + unsigned short errnum; + + if(!initialized) errSymBld(); + if (modnum < 501) + return; + + /* print range of error messages */ + for (errnum = begErrNum; errnum <= endErrNum; errnum++) { + errNum = modnum << 16; + errNum |= (errnum & 0xffff); + errSymTestPrint(errNum); + } +} diff --git a/src/libCom/error/errSymTbl.h b/src/libCom/error/errSymTbl.h new file mode 100644 index 000000000..2b1712621 --- /dev/null +++ b/src/libCom/error/errSymTbl.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef INCerrSymTblh +#define INCerrSymTblh 1 + +#ifndef NELEMENTS +#define NELEMENTS(array) /* number of elements in an array */ \ + (sizeof (array) / sizeof ((array) [0])) +#endif +#define LOCAL static + +typedef struct /* ERRSYMBOL - entry in symbol table */ + { + char *name; /* pointer to symbol name */ + long errNum; /* errMessage symbol number */ + } ERRSYMBOL; +typedef struct /* ERRSYMTAB - symbol table */ + { + int nsymbols; /* current number of symbols in table */ + ERRSYMBOL *symbols; /* ptr to array of symbol entries */ + } ERRSYMTAB; +typedef ERRSYMTAB *ERRSYMTAB_ID; + + +#endif /* INCerrSymTblh */ diff --git a/src/libCom/error/errlog.c b/src/libCom/error/errlog.c new file mode 100644 index 000000000..49e743c6d --- /dev/null +++ b/src/libCom/error/errlog.c @@ -0,0 +1,617 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Original Author: Marty Kraimer + * Date: 07JAN1998 + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#define ERRLOG_INIT +#include "adjustment.h" +#include "dbDefs.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsInterrupt.h" +#include "errMdef.h" +#include "error.h" +#include "ellLib.h" +#include "errlog.h" +#include "epicsStdio.h" +#include "epicsExit.h" + + +#define BUFFER_SIZE 1280 +#define MAX_MESSAGE_SIZE 256 + +/*Declare storage for errVerbose */ +epicsShareDef int errVerbose = 0; + +static void errlogCleanup(void); +static void exitHandler(void *); +static void errlogThread(void); + +static char *msgbufGetFree(int noConsoleMessage); +static void msgbufSetSize(int size); /* Send 'size' chars plus trailing '\0' */ +static char *msgbufGetSend(int *noConsoleMessage); +static void msgbufFreeSend(void); + +typedef struct listenerNode{ + ELLNODE node; + errlogListener listener; + void *pPrivate; +} listenerNode; + +/*each message consists of a msgNode immediately followed by the message */ +typedef struct msgNode { + ELLNODE node; + char *message; + int length; + int noConsoleMessage; +} msgNode; + +static struct { + epicsEventId waitForWork; /*errlogThread waits for this*/ + epicsMutexId msgQueueLock; + epicsMutexId listenerLock; + epicsEventId waitForFlush; /*errlogFlush waits for this*/ + epicsEventId flush; /*errlogFlush sets errlogThread does a Try*/ + epicsMutexId flushLock; + epicsEventId waitForExit; /*exitHandler waits for this*/ + int atExit; /*TRUE when exitHandler is active*/ + ELLLIST listenerList; + ELLLIST msgQueue; + msgNode *pnextSend; + int errlogInitFailed; + int buffersize; + int maxMsgSize; + int msgNeeded; + int sevToLog; + int toConsole; + int missedMessages; + char *pbuffer; +} pvtData; + + +/* + * vsnprintf with truncation message + */ +static int tvsnPrint(char *str, size_t size, const char *format, va_list ap) +{ + static const char tmsg[] = "<>\n"; + int nchar = epicsVsnprintf(str, size, format ? format : "", ap); + + if (nchar >= size) { + if (size > sizeof tmsg) + strcpy(str + size - sizeof tmsg, tmsg); + nchar = size - 1; + } + return nchar; +} + +epicsShareFunc int errlogPrintf(const char *pFormat, ...) +{ + va_list pvar; + char *pbuffer; + int nchar; + int isOkToBlock; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogPrintf called from interrupt level\n"); + return 0; + } + isOkToBlock = epicsThreadIsOkToBlock(); + errlogInit(0); + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + va_start(pvar, pFormat); + vfprintf(stderr, pFormat, pvar); + va_end (pvar); + fflush(stderr); + } + pbuffer = msgbufGetFree(isOkToBlock); + if (!pbuffer) return 0; + va_start(pvar, pFormat); + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); + va_end(pvar); + msgbufSetSize(nchar); + return nchar; +} + +epicsShareFunc int errlogVprintf( + const char *pFormat,va_list pvar) +{ + int nchar; + char *pbuffer; + int isOkToBlock; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogVprintf called from interrupt level\n"); + return 0; + } + errlogInit(0); + if (pvtData.atExit) return 0; + isOkToBlock = epicsThreadIsOkToBlock(); + pbuffer = msgbufGetFree(isOkToBlock); + if (!pbuffer) { + vfprintf(stderr, pFormat, pvar); + fflush(stderr); + return 0; + } + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + fprintf(stderr, "%s", pbuffer); + fflush(stderr); + } + msgbufSetSize(nchar); + return nchar; +} + +epicsShareFunc int epicsShareAPI errlogMessage(const char *message) +{ + errlogPrintf("%s", message); + return 0; +} + +epicsShareFunc int errlogPrintfNoConsole( const char *pFormat, ...) +{ + va_list pvar; + int nchar; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogPrintfNoConsole called from interrupt level\n"); + return 0; + } + errlogInit(0); + va_start(pvar, pFormat); + nchar = errlogVprintfNoConsole(pFormat, pvar); + va_end(pvar); + return nchar; +} + +epicsShareFunc int errlogVprintfNoConsole( + const char *pFormat,va_list pvar) +{ + int nchar; + char *pbuffer; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogVprintfNoConsole called from interrupt level\n"); + return 0; + } + errlogInit(0); + if (pvtData.atExit) return 0; + pbuffer = msgbufGetFree(1); + if (!pbuffer) return 0; + nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar); + msgbufSetSize(nchar); + return nchar; +} + +epicsShareFunc int errlogSevPrintf( + const errlogSevEnum severity,const char *pFormat, ...) +{ + va_list pvar; + int nchar; + int isOkToBlock; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogSevPrintf called from interrupt level\n"); + return 0; + } + errlogInit(0); + if (pvtData.sevToLog>severity) return 0; + isOkToBlock = epicsThreadIsOkToBlock(); + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + fprintf(stderr, "sevr=%s ", errlogGetSevEnumString(severity)); + va_start(pvar, pFormat); + vfprintf(stderr, pFormat, pvar); + va_end(pvar); + fflush(stderr); + } + va_start(pvar, pFormat); + nchar = errlogSevVprintf(severity, pFormat, pvar); + va_end(pvar); + return nchar; +} + +epicsShareFunc int errlogSevVprintf( + const errlogSevEnum severity,const char *pFormat,va_list pvar) +{ + char *pnext; + int nchar; + int totalChar = 0; + int isOkToBlock; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage + ("errlogSevVprintf called from interrupt level\n"); + return 0; + } + errlogInit(0); + if (pvtData.atExit) return 0; + isOkToBlock = epicsThreadIsOkToBlock(); + pnext = msgbufGetFree(isOkToBlock); + if (!pnext) return 0; + nchar = sprintf(pnext, "sevr=%s ", errlogGetSevEnumString(severity)); + pnext += nchar; totalChar += nchar; + nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pFormat, pvar); + pnext += nchar; totalChar += nchar; + if (pnext[-1] != '\n') { + strcpy(pnext,"\n"); + totalChar++; + } + msgbufSetSize(totalChar); + return nchar; +} + +epicsShareFunc char * epicsShareAPI errlogGetSevEnumString( + const errlogSevEnum severity) +{ + static char unknown[] = "unknown"; + + errlogInit(0); + if (severity < 0 || severity > 3) return unknown; + return errlogSevEnumString[severity]; +} + +epicsShareFunc void epicsShareAPI errlogSetSevToLog( + const errlogSevEnum severity) +{ + errlogInit(0); + pvtData.sevToLog = severity; +} + +epicsShareFunc errlogSevEnum epicsShareAPI errlogGetSevToLog(void) +{ + errlogInit(0); + return pvtData.sevToLog; +} + +epicsShareFunc void epicsShareAPI errlogAddListener( + errlogListener listener, void *pPrivate) +{ + listenerNode *plistenerNode; + + errlogInit(0); + if (pvtData.atExit) return; + plistenerNode = callocMustSucceed(1,sizeof(listenerNode), + "errlogAddListener"); + epicsMutexMustLock(pvtData.listenerLock); + plistenerNode->listener = listener; + plistenerNode->pPrivate = pPrivate; + ellAdd(&pvtData.listenerList,&plistenerNode->node); + epicsMutexUnlock(pvtData.listenerLock); +} + +epicsShareFunc void epicsShareAPI errlogRemoveListener( + errlogListener listener) +{ + listenerNode *plistenerNode; + + errlogInit(0); + if (!pvtData.atExit) epicsMutexMustLock(pvtData.listenerLock); + plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); + while (plistenerNode) { + if (plistenerNode->listener==listener) { + ellDelete(&pvtData.listenerList, &plistenerNode->node); + free((void *)plistenerNode); + break; + } + plistenerNode = (listenerNode *)ellNext(&plistenerNode->node); + } + if (!pvtData.atExit) epicsMutexUnlock(pvtData.listenerLock); + if (!plistenerNode) { + fprintf(stderr, "errlogRemoveListener did not find listener\n"); + } +} + +epicsShareFunc int epicsShareAPI eltc(int yesno) +{ + errlogInit(0); + pvtData.toConsole = yesno; + return 0; +} + +epicsShareFunc void errPrintf(long status, const char *pFileName, + int lineno, const char *pformat, ...) +{ + va_list pvar; + char *pnext; + int nchar; + int totalChar=0; + int isOkToBlock; + char name[256]; + + if (epicsInterruptIsInterruptContext()) { + epicsInterruptContextMessage("errPrintf called from interrupt level\n"); + return; + } + errlogInit(0); + isOkToBlock = epicsThreadIsOkToBlock(); + if (status == 0) status = errno; + if (status > 0) { + errSymLookup(status, name, sizeof(name)); + } + if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) { + if (pFileName) fprintf(stderr, "filename=\"%s\" line number=%d\n", + pFileName, lineno); + if (status>0) fprintf(stderr, "%s ", name); + va_start(pvar, pformat); + vfprintf(stderr, pformat, pvar); + va_end(pvar); + fputc('\n', stderr); + fflush(stderr); + } + if (pvtData.atExit) return; + pnext = msgbufGetFree(isOkToBlock); + if (!pnext) return; + if (pFileName) { + nchar = sprintf(pnext,"filename=\"%s\" line number=%d\n", + pFileName, lineno); + pnext += nchar; totalChar += nchar; + } + if (status > 0) { + nchar = sprintf(pnext,"%s ",name); + pnext += nchar; totalChar += nchar; + } + va_start(pvar, pformat); + nchar = tvsnPrint(pnext, pvtData.maxMsgSize - totalChar - 1, pformat, pvar); + va_end(pvar); + if (nchar>0) { + pnext += nchar; + totalChar += nchar; + } + strcpy(pnext, "\n"); + totalChar++ ; /*include the \n */ + msgbufSetSize(totalChar); +} + +static void exitHandler(void *pvt) +{ + pvtData.atExit = 1; + epicsEventSignal(pvtData.waitForWork); + epicsEventMustWait(pvtData.waitForExit); + epicsEventDestroy(pvtData.waitForExit); + return; +} + +struct initArgs { + int bufsize; + int maxMsgSize; +}; + +static void errlogInitPvt(void *arg) +{ + struct initArgs *pconfig = (struct initArgs *) arg; + epicsThreadId tid; + + pvtData.errlogInitFailed = TRUE; + pvtData.buffersize = pconfig->bufsize; + pvtData.maxMsgSize = pconfig->maxMsgSize; + pvtData.msgNeeded = adjustToWorstCaseAlignment(pvtData.maxMsgSize + + sizeof(msgNode)); + ellInit(&pvtData.listenerList); + ellInit(&pvtData.msgQueue); + pvtData.toConsole = TRUE; + pvtData.waitForWork = epicsEventMustCreate(epicsEventEmpty); + pvtData.listenerLock = epicsMutexMustCreate(); + pvtData.msgQueueLock = epicsMutexMustCreate(); + pvtData.waitForFlush = epicsEventMustCreate(epicsEventEmpty); + pvtData.flush = epicsEventMustCreate(epicsEventEmpty); + pvtData.flushLock = epicsMutexMustCreate(); + pvtData.waitForExit = epicsEventMustCreate(epicsEventEmpty); + pvtData.pbuffer = callocMustSucceed(1, pvtData.buffersize, + "errlogInitPvt"); + + errSymBld(); /* Better not to do this lazily... */ + + tid = epicsThreadCreate("errlog", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + (EPICSTHREADFUNC)errlogThread, 0); + if (tid) { + pvtData.errlogInitFailed = FALSE; + } +} + +static void errlogCleanup(void) +{ + free(pvtData.pbuffer); + epicsMutexDestroy(pvtData.flushLock); + epicsEventDestroy(pvtData.flush); + epicsEventDestroy(pvtData.waitForFlush); + epicsMutexDestroy(pvtData.listenerLock); + epicsMutexDestroy(pvtData.msgQueueLock); + epicsEventDestroy(pvtData.waitForWork); + /*Note that exitHandler must destroy waitForExit*/ +} + +epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize) +{ + static epicsThreadOnceId errlogOnceFlag = EPICS_THREAD_ONCE_INIT; + struct initArgs config; + + if (pvtData.atExit) + return 0; + + if (bufsize < BUFFER_SIZE) bufsize = BUFFER_SIZE; + config.bufsize = bufsize; + if (maxMsgSize < MAX_MESSAGE_SIZE) maxMsgSize = MAX_MESSAGE_SIZE; + config.maxMsgSize = maxMsgSize; + epicsThreadOnce(&errlogOnceFlag, errlogInitPvt, (void *)&config); + if (pvtData.errlogInitFailed) { + fprintf(stderr,"errlogInit failed\n"); + exit(1); + } + return 0; +} +epicsShareFunc int epicsShareAPI errlogInit(int bufsize) +{ + return errlogInit2(bufsize, MAX_MESSAGE_SIZE); +} +epicsShareFunc void epicsShareAPI errlogFlush(void) +{ + int count; + errlogInit(0); + if (pvtData.atExit) return; + /*If nothing in queue dont wake up errlogThread*/ + epicsMutexMustLock(pvtData.msgQueueLock); + count = ellCount(&pvtData.msgQueue); + epicsMutexUnlock(pvtData.msgQueueLock); + if (count <= 0) return; + /*must let errlogThread empty queue*/ + epicsMutexMustLock(pvtData.flushLock); + epicsEventSignal(pvtData.flush); + epicsEventSignal(pvtData.waitForWork); + epicsEventMustWait(pvtData.waitForFlush); + epicsMutexUnlock(pvtData.flushLock); +} + +static void errlogThread(void) +{ + listenerNode *plistenerNode; + int noConsoleMessage; + char *pmessage; + + epicsAtExit(exitHandler,0); + while (TRUE) { + epicsEventMustWait(pvtData.waitForWork); + if (pvtData.atExit) break; + while ((pmessage = msgbufGetSend(&noConsoleMessage))) { + if (pvtData.atExit) break; + epicsMutexMustLock(pvtData.listenerLock); + if (pvtData.toConsole && !noConsoleMessage) { + fprintf(stderr,"%s",pmessage); + fflush(stderr); + } + plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList); + while (plistenerNode) { + (*plistenerNode->listener)(plistenerNode->pPrivate, pmessage); + plistenerNode = (listenerNode *)ellNext(&plistenerNode->node); + } + epicsMutexUnlock(pvtData.listenerLock); + msgbufFreeSend(); + } + if (pvtData.atExit) break; + if (epicsEventTryWait(pvtData.flush) != epicsEventWaitOK) continue; + epicsThreadSleep(.2); /*just wait an extra .2 seconds*/ + epicsEventSignal(pvtData.waitForFlush); + } + errlogCleanup(); + epicsEventSignal(pvtData.waitForExit); +} + +static msgNode *msgbufGetNode(void) +{ + char *pbuffer = pvtData.pbuffer; + char *pnextFree; + msgNode *pnextSend; + + if (ellCount(&pvtData.msgQueue) == 0 ) { + pnextFree = pbuffer; /* Reset if empty */ + } else { + msgNode *pfirst = (msgNode *)ellFirst(&pvtData.msgQueue); + msgNode *plast = (msgNode *)ellLast(&pvtData.msgQueue); + char *plimit = pbuffer + pvtData.buffersize; + + pnextFree = plast->message + adjustToWorstCaseAlignment(plast->length); + if (pfirst <= plast && + pnextFree + pvtData.msgNeeded > plimit) { + pnextFree = pbuffer; /* Hit end, wrap to start */ + plimit = (char *)pfirst; + } + if (pnextFree + pvtData.msgNeeded > plimit) { + return 0; /* No room */ + } + } + pnextSend = (msgNode *)pnextFree; + pnextSend->message = pnextFree + sizeof(msgNode); + pnextSend->length = 0; + return pnextSend; +} + +static char *msgbufGetFree(int noConsoleMessage) +{ + msgNode *pnextSend; + + if (epicsMutexLock(pvtData.msgQueueLock) != epicsMutexLockOK) + return 0; + + if ((ellCount(&pvtData.msgQueue) == 0) && pvtData.missedMessages) { + int nchar; + + pnextSend = msgbufGetNode(); + nchar = sprintf(pnextSend->message, + "errlog: %d messages were discarded\n", pvtData.missedMessages); + pnextSend->length = nchar + 1; + pvtData.missedMessages = 0; + ellAdd(&pvtData.msgQueue, &pnextSend->node); + } + pvtData.pnextSend = pnextSend = msgbufGetNode(); + if (pnextSend) { + pnextSend->noConsoleMessage = noConsoleMessage; + pnextSend->length = 0; + return pnextSend->message; /* NB: msgQueueLock is still locked */ + } + ++pvtData.missedMessages; + epicsMutexUnlock(pvtData.msgQueueLock); + return 0; +} + +static void msgbufSetSize(int size) +{ + msgNode *pnextSend = pvtData.pnextSend; + + pnextSend->length = size+1; + ellAdd(&pvtData.msgQueue, &pnextSend->node); + epicsMutexUnlock(pvtData.msgQueueLock); + epicsEventSignal(pvtData.waitForWork); +} + +/*errlogThread is the only task that calls msgbufGetSend and msgbufFreeSend*/ +/*Thus errlogThread is the ONLY task that removes messages from msgQueue */ +/*This is why each can lock and unlock msgQueue */ +/*This is necessary to prevent other tasks from waiting for errlogThread */ +static char * msgbufGetSend(int *noConsoleMessage) +{ + msgNode *pnextSend; + + epicsMutexMustLock(pvtData.msgQueueLock); + pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); + epicsMutexUnlock(pvtData.msgQueueLock); + if (!pnextSend) return(0); + *noConsoleMessage = pnextSend->noConsoleMessage; + return(pnextSend->message); +} + +static void msgbufFreeSend(void) +{ + msgNode *pnextSend; + + epicsMutexMustLock(pvtData.msgQueueLock); + pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue); + if (!pnextSend) { + fprintf(stderr, "errlog: msgbufFreeSend logic error\n"); + epicsThreadSuspendSelf(); + } + ellDelete(&pvtData.msgQueue, &pnextSend->node); + epicsMutexUnlock(pvtData.msgQueueLock); +} diff --git a/src/libCom/error/errlog.h b/src/libCom/error/errlog.h new file mode 100644 index 000000000..f08d4a936 --- /dev/null +++ b/src/libCom/error/errlog.h @@ -0,0 +1,85 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INCerrlogh +#define INCerrlogh + +#include + +#include "shareLib.h" +#include "compilerDependencies.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* define errMessage with a macro so we can print the file and line number*/ +#define errMessage(S, PM) \ + errPrintf(S, __FILE__, __LINE__, "%s", PM) +/* epicsPrintf and epicsVprintf old versions of errlog routines*/ +#define epicsPrintf errlogPrintf +#define epicsVprintf errlogVprintf + +typedef void (*errlogListener)(void *pPrivate, const char *message); + +typedef enum {errlogInfo, errlogMinor, errlogMajor, errlogFatal} errlogSevEnum; + +#ifdef ERRLOG_INIT +epicsShareDef char * errlogSevEnumString[] = {"info","minor","major","fatal"}; +#else +epicsShareExtern char * errlogSevEnumString[]; +#endif + +epicsShareFunc int errlogPrintf( + const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); +epicsShareFunc int errlogVprintf( + const char *pformat,va_list pvar); +epicsShareFunc int errlogSevPrintf( + const errlogSevEnum severity,const char *pformat, ...) EPICS_PRINTF_STYLE(2,3); +epicsShareFunc int errlogSevVprintf( + const errlogSevEnum severity,const char *pformat,va_list pvar); +epicsShareFunc int epicsShareAPI errlogMessage( + const char *message); + +epicsShareFunc char * epicsShareAPI errlogGetSevEnumString( + const errlogSevEnum severity); +epicsShareFunc void epicsShareAPI errlogSetSevToLog( + const errlogSevEnum severity ); +epicsShareFunc errlogSevEnum epicsShareAPI errlogGetSevToLog(void); + +epicsShareFunc void epicsShareAPI errlogAddListener( + errlogListener listener, void *pPrivate); +epicsShareFunc void epicsShareAPI errlogRemoveListener( + errlogListener listener); + +epicsShareFunc int epicsShareAPI eltc(int yesno); +epicsShareFunc int epicsShareAPI errlogInit(int bufsize); +epicsShareFunc int epicsShareAPI errlogInit2(int bufsize, int maxMsgSize); +epicsShareFunc void epicsShareAPI errlogFlush(void); + +/*other routines that write to log file*/ +epicsShareFunc void errPrintf(long status, const char *pFileName, + int lineno, const char *pformat, ...) EPICS_PRINTF_STYLE(4,5); + +epicsShareExtern int errVerbose; + +/* The following are added so that logMsg on vxWorks does not cause + * the message to appear twice on the console + */ +epicsShareFunc int errlogPrintfNoConsole( + const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); +epicsShareFunc int errlogVprintfNoConsole( + const char *pformat,va_list pvar); + +#ifdef __cplusplus +} +#endif + +#endif /*INCerrlogh*/ diff --git a/src/libCom/error/error.h b/src/libCom/error/error.h new file mode 100644 index 000000000..4a89ec63f --- /dev/null +++ b/src/libCom/error/error.h @@ -0,0 +1,57 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* error.h - errMessage symbol table header */ +/* share/epicsH Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * Author: Marty Kraimer + * Date: 6-1-90 + */ + + +#ifndef INCerrorh +#define INCerrorh 1 + +#define NELEMENTS(array) /* number of elements in an array */ \ + (sizeof (array) / sizeof ((array) [0])) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct /* ERRSYMBOL - entry in symbol table */ + { + char *name; /* pointer to symbol name */ + long errNum; /* errMessage symbol number */ + } ERRSYMBOL; +typedef struct /* ERRSYMTAB - symbol table */ + { + short nsymbols; /* current number of symbols in table */ + ERRSYMBOL *symbols; /* ptr to array of symbol entries */ + } ERRSYMTAB; +typedef ERRSYMTAB *ERRSYMTAB_ID; + +/*************************************************************/ +struct errSet { /* This defines one module error set */ + long number; /* dimension of err strings */ + char **papName; /* ptr to arr of ptr to error string */ +}; +struct errDes { /* An array of error sets for modules */ + long number; /* number of err modules */ + struct errSet **papErrSet; /* ptr to arr of ptr to errSet */ +}; +extern struct errDes *dbErrDes; +/*************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /*INCerrorh*/ diff --git a/src/libCom/error/makeStatTbl.pl b/src/libCom/error/makeStatTbl.pl new file mode 100644 index 000000000..8bc63e23c --- /dev/null +++ b/src/libCom/error/makeStatTbl.pl @@ -0,0 +1,127 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# makeStatTbl.pl - Create Error Symbol Table +# +# Kay-Uwe Kasemir, 1-31-97, +# based on makeStatTbl shell script. +# +# SYNOPSIS +# perl makeStatTbl.pl hdir [...] +# +# DESCRIPTION +# This tool creates a symbol table (ERRSYMTAB) structure which contains the +# names and values of all the status codes defined in the .h files in the +# specified directory(s). The status codes must be prefixed with "S_" +# in order to be included in this table. +# A "err.h" file must exist in each hdir which defines the module +# numbers, eg. "M_". The table is created on standard output. +# +# This tool's primary use is for creating an error status table used +# by errPrint, and errSymLookup. +# +# FILES +# errMdef.h module number file for each h directory +# +# SEE ALSO: errnoLib(1), symLib(1) +#*/ + +use Cwd; + +die "No args (files to parse) given" if ($#ARGV < 0); + +# parse all lines of all files given: +while (<>) +{ + if (m'^#define[ /t]*S_') + { + chomp; + push @err_sym_line, $_; + } + if (m'^#[ \t]*define[ /t]+M_') + { + chomp; + push @err_facility_line, $_; + } +} + +$out_name = "errSymTbl.c"; +$dir = cwd(); + +open OUT, ">$out_name" or die "Cannot open $out_name"; + +print OUT "/*\n"; +print OUT " * status code symbol table\n"; +print OUT " *\n"; +print OUT " * CREATED BY makeStatTbl.pl\n"; +print OUT " * FROM $dir\n"; +print OUT " * ON " . localtime() . "\n"; +print OUT " */\n"; +print OUT "\n"; +print OUT "#include \"errMdef.h\"\n"; +print OUT "#include \"errSymTbl.h\"\n"; +print OUT "\n"; + + +foreach $line ( @err_facility_line ) +{ + if ($line =~ m'^#[ \t]*define[ \t]+(M_[A-Za-z0-9_]+)[ \t]+(.*)') + { + printf OUT "#ifndef %s\n", $1; + printf OUT "#define %s %s\n", $1, $2; + printf OUT "#endif /* ifdef %s */\n", $1; + } +} + +$count = 0; +foreach $line ( @err_sym_line ) +{ + print OUT "$line\n"; + # define S_symbol /* comment */ + if ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+).*\/\*(.+)\*\/') + { + $symbol[$count] = $1; + $comment[$count]= $2; + ++$count; + } + else + { + # Some status values for '0' (=OK) have no comment: + unless ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+)') + { + die "cannot decode this line:\n$line\n"; + } + } +} + + +print OUT "\n"; +print OUT "static ERRSYMBOL symbols[] =\n"; +print OUT "{\n"; + +for ($i=0; $i<$count; ++$i) +{ + printf OUT "\t{ \"%s\", (long) %s },\n", + $comment[$i], $symbol[$i]; +} + +print OUT "};\n"; +print OUT "\n"; +print OUT "static ERRSYMTAB symTbl =\n"; +print OUT "{\n"; +print OUT "\tNELEMENTS(symbols), /* current number of symbols in table */\n"; +print OUT "\tsymbols, /* ptr to symbol array */\n"; +print OUT "};\n"; +print OUT "\n"; +print OUT "ERRSYMTAB_ID errSymTbl = &symTbl;\n"; +print OUT "\n"; +print OUT "/*\tEOF $out_name */\n"; + diff --git a/src/libCom/fdmgr/fdManager.cpp b/src/libCom/fdmgr/fdManager.cpp new file mode 100644 index 000000000..83984709c --- /dev/null +++ b/src/libCom/fdmgr/fdManager.cpp @@ -0,0 +1,364 @@ +/*************************************************************************\ +* 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 +// +// File descriptor management C++ class library +// (for multiplexing IO in a single threaded environment) +// +// Author Jeffrey O. Hill +// johill@lanl.gov +// 505 665 1831 +// +// NOTES: +// 1) This library is not thread safe +// + +// +// ANSI C +// +#include +#include + +#define instantiateRecourceLib +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "epicsThread.h" +#include "tsMinMax.h" +#include "fdManager.h" +#include "locationException.h" + +epicsShareDef fdManager fileDescriptorManager; + +const unsigned mSecPerSec = 1000u; +const unsigned uSecPerSec = 1000u * mSecPerSec; + +// +// fdManager::fdManager() +// +// hopefully its a reasonable guess that select() and epicsThreadSleep() +// will have the same sleep quantum +// +epicsShareFunc fdManager::fdManager () : + sleepQuantum ( epicsThreadSleepQuantum () ), + fdSetsPtr ( new fd_set [fdrNEnums] ), + pTimerQueue ( 0 ), maxFD ( 0 ), processInProg ( false ), + pCBReg ( 0 ) +{ + int status = osiSockAttach (); + assert (status); + + for ( size_t i = 0u; i < fdrNEnums; i++ ) { + FD_ZERO ( &fdSetsPtr[i] ); + } +} + +// +// fdManager::~fdManager() +// +epicsShareFunc fdManager::~fdManager() +{ + fdReg *pReg; + + while ( (pReg = this->regList.get()) ) { + pReg->state = fdReg::limbo; + pReg->destroy(); + } + while ( (pReg = this->activeList.get()) ) { + pReg->state = fdReg::limbo; + pReg->destroy(); + } + delete this->pTimerQueue; + delete [] this->fdSetsPtr; + osiSockRelease(); +} + +// +// fdManager::process() +// +epicsShareFunc void fdManager::process (double delay) +{ + this->lazyInitTimerQueue (); + + // + // no recursion + // + if (this->processInProg) { + return; + } + this->processInProg = true; + + // + // One shot at expired timers prior to going into + // select. This allows zero delay timers to arm + // fd writes. We will never process the timer queue + // more than once here so that fd activity get serviced + // in a reasonable length of time. + // + double minDelay = this->pTimerQueue->process(epicsTime::getCurrent()); + + if ( minDelay >= delay ) { + minDelay = delay; + } + + bool ioPending = false; + tsDLIter < fdReg > iter = this->regList.firstIter (); + while ( iter.valid () ) { + FD_SET(iter->getFD(), &this->fdSetsPtr[iter->getType()]); + ioPending = true; + ++iter; + } + + if ( ioPending ) { + struct timeval tv; + tv.tv_sec = static_cast ( minDelay ); + tv.tv_usec = static_cast ( (minDelay-tv.tv_sec) * uSecPerSec ); + + fd_set * pReadSet = & this->fdSetsPtr[fdrRead]; + fd_set * pWriteSet = & this->fdSetsPtr[fdrWrite]; + fd_set * pExceptSet = & this->fdSetsPtr[fdrException]; + int status = select (this->maxFD, pReadSet, pWriteSet, pExceptSet, &tv); + + this->pTimerQueue->process(epicsTime::getCurrent()); + + if ( status > 0 ) { + + // + // Look for activity + // + iter=this->regList.firstIter (); + while ( iter.valid () && status > 0 ) { + tsDLIter < fdReg > tmp = iter; + tmp++; + if (FD_ISSET(iter->getFD(), &this->fdSetsPtr[iter->getType()])) { + FD_CLR(iter->getFD(), &this->fdSetsPtr[iter->getType()]); + this->regList.remove(*iter); + this->activeList.add(*iter); + iter->state = fdReg::active; + status--; + } + iter = tmp; + } + + // + // I am careful to prevent problems if they access the + // above list while in a "callBack()" routine + // + fdReg * pReg; + while ( (pReg = this->activeList.get()) ) { + pReg->state = fdReg::limbo; + + // + // Tag current fdReg so that we + // can detect if it was deleted + // during the call back + // + this->pCBReg = pReg; + pReg->callBack(); + if (this->pCBReg != NULL) { + // + // check only after we see that it is non-null so + // that we dont trigger bounds-checker dangling pointer + // error + // + assert (this->pCBReg==pReg); + this->pCBReg = 0; + if (pReg->onceOnly) { + pReg->destroy(); + } + else { + this->regList.add(*pReg); + pReg->state = fdReg::pending; + } + } + } + } + else if ( status < 0 ) { + int errnoCpy = SOCKERRNO; + + // dont depend on flags being properly set if + // an error is retuned from select + for ( size_t i = 0u; i < fdrNEnums; i++ ) { + FD_ZERO ( &fdSetsPtr[i] ); + } + + // + // print a message if its an unexpected error + // + if ( errnoCpy != SOCK_EINTR ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, + "fdManager: select failed because \"%s\"\n", + sockErrBuf ); + } + } + } + else { + /* + * recover from subtle differences between + * windows sockets and UNIX sockets implementation + * of select() + */ + epicsThreadSleep(minDelay); + this->pTimerQueue->process(epicsTime::getCurrent()); + } + this->processInProg = false; + return; +} + +// +// fdReg::destroy() +// (default destroy method) +// +epicsShareFunc void fdReg::destroy() +{ + delete this; +} + +// +// fdReg::~fdReg() +// +epicsShareFunc fdReg::~fdReg() +{ + this->manager.removeReg(*this); +} + +// +// fdReg::show() +// +epicsShareFunc void fdReg::show(unsigned level) const +{ + printf ("fdReg at %p\n", (void *) this); + if (level>1u) { + printf ("\tstate = %d, onceOnly = %d\n", + this->state, this->onceOnly); + } + this->fdRegId::show(level); +} + +// +// fdRegId::show() +// +void fdRegId::show ( unsigned level ) const +{ + printf ( "fdRegId at %p\n", + static_cast ( this ) ); + if ( level > 1u ) { + printf ( "\tfd = %d, type = %d\n", + this->fd, this->type ); + } +} + +// +// fdManager::installReg () +// +epicsShareFunc void fdManager::installReg (fdReg ®) +{ + this->maxFD = tsMax ( this->maxFD, reg.getFD()+1 ); + // Most applications will find that its important to push here to + // the front of the list so that transient writes get executed + // first allowing incoming read protocol to find that outgoing + // buffer space is newly available. + this->regList.push ( reg ); + reg.state = fdReg::pending; + + int status = this->fdTbl.add ( reg ); + if ( status != 0 ) { + throwWithLocation ( fdInterestSubscriptionAlreadyExits () ); + } +} + +// +// fdManager::removeReg () +// +void fdManager::removeReg (fdReg ®In) +{ + fdReg *pItemFound; + + pItemFound = this->fdTbl.remove (regIn); + if (pItemFound!=®In) { + fprintf(stderr, + "fdManager::removeReg() bad fd registration object\n"); + return; + } + + // + // signal fdManager that the fdReg was deleted + // during the call back + // + if (this->pCBReg == ®In) { + this->pCBReg = 0; + } + + switch (regIn.state) { + case fdReg::active: + this->activeList.remove (regIn); + break; + case fdReg::pending: + this->regList.remove (regIn); + break; + case fdReg::limbo: + break; + default: + // + // here if memory corrupted + // + assert(0); + } + regIn.state = fdReg::limbo; + + FD_CLR(regIn.getFD(), &this->fdSetsPtr[regIn.getType()]); +} + +// +// fdManager::reschedule () +// NOOP - this only runs single threaded, and therefore they can only +// add a new timer from places that will always end up in a reschedule +// +void fdManager::reschedule () +{ +} + +double fdManager::quantum () +{ + return this->sleepQuantum; +} + +// +// lookUpFD() +// +epicsShareFunc fdReg *fdManager::lookUpFD (const SOCKET fd, const fdRegType type) +{ + if (fd<0) { + return NULL; + } + fdRegId id (fd,type); + return this->fdTbl.lookup(id); +} + +// +// fdReg::fdReg() +// +fdReg::fdReg (const SOCKET fdIn, const fdRegType typIn, + const bool onceOnlyIn, fdManager &managerIn) : + fdRegId (fdIn,typIn), state (limbo), + onceOnly (onceOnlyIn), manager (managerIn) +{ + if (!FD_IN_FDSET(fdIn)) { + fprintf (stderr, "%s: fd > FD_SETSIZE ignored\n", + __FILE__); + return; + } + this->manager.installReg (*this); +} + +template class resTable; diff --git a/src/libCom/fdmgr/fdManager.h b/src/libCom/fdmgr/fdManager.h new file mode 100644 index 000000000..bfe94a107 --- /dev/null +++ b/src/libCom/fdmgr/fdManager.h @@ -0,0 +1,205 @@ +/*************************************************************************\ +* 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 + * + * File descriptor management C++ class library + * (for multiplexing IO in a single threaded environment) + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef fdManagerH_included +#define fdManagerH_included + +#include "shareLib.h" // reset share lib defines +#include "tsDLList.h" +#include "resourceLib.h" +#include "epicsTime.h" +#include "osiSock.h" +#include "epicsTimer.h" + +enum fdRegType {fdrRead, fdrWrite, fdrException, fdrNEnums}; + +// +// fdRegId +// +// file descriptor interest id +// +class epicsShareClass fdRegId +{ +public: + + fdRegId (const SOCKET fdIn, const fdRegType typeIn) : + fd(fdIn), type(typeIn) {} + + SOCKET getFD () const + { + return this->fd; + } + + fdRegType getType () const + { + return this->type; + } + + bool operator == (const fdRegId &idIn) const + { + return this->fd == idIn.fd && this->type==idIn.type; + } + + resTableIndex hash () const; + + virtual void show (unsigned level) const; + + virtual ~fdRegId() {} +private: + SOCKET fd; + fdRegType type; +}; + +// +// fdManager +// +// file descriptor manager +// +class fdManager : public epicsTimerQueueNotify { +public: + // + // exceptions + // + class fdInterestSubscriptionAlreadyExits {}; + + epicsShareFunc fdManager (); + epicsShareFunc virtual ~fdManager (); + epicsShareFunc void process ( double delay ); // delay parameter is in seconds + + // returns NULL if the fd is unknown + epicsShareFunc class fdReg *lookUpFD (const SOCKET fd, const fdRegType type); + + epicsTimer & createTimer (); + +private: + tsDLList < fdReg > regList; + tsDLList < fdReg > activeList; + resTable < fdReg, fdRegId > fdTbl; + const double sleepQuantum; + fd_set * fdSetsPtr; + epicsTimerQueuePassive * pTimerQueue; + SOCKET maxFD; + bool processInProg; + // + // Set to fdreg when in call back + // and nill otherwise + // + fdReg * pCBReg; + void reschedule (); + double quantum (); + epicsShareFunc void installReg (fdReg ®); + epicsShareFunc void removeReg (fdReg ®); + void lazyInitTimerQueue (); + fdManager ( const fdManager & ); + fdManager & operator = ( const fdManager & ); + friend class fdReg; +}; + +// +// default file descriptor manager +// +epicsShareExtern fdManager fileDescriptorManager; + +// +// fdReg +// +// file descriptor registration +// +class fdReg : public fdRegId, public tsDLNode, public tsSLNode { + friend class fdManager; + +public: + + epicsShareFunc fdReg (const SOCKET fdIn, const fdRegType type, + const bool onceOnly=false, fdManager &manager = fileDescriptorManager); + epicsShareFunc virtual ~fdReg (); + + epicsShareFunc virtual void show (unsigned level) const; + + // + // Called by the file descriptor manager: + // 1) If the fdManager is deleted and there are still + // fdReg objects attached + // 2) Immediately after calling "callBack()" if + // the constructor specified "onceOnly" + // + // fdReg::destroy() does a "delete this" + // + epicsShareFunc virtual void destroy (); + +private: + enum state {active, pending, limbo}; + + // + // called when there is activity on the fd + // NOTES + // 1) the fdManager will call this only once during the + // lifetime of a fdReg object if the constructor + // specified "onceOnly" + // + epicsShareFunc virtual void callBack ()=0; + + unsigned char state; // state enums go here + unsigned char onceOnly; + fdManager &manager; + + fdReg ( const fdReg & ); + fdReg & operator = ( const fdReg & ); +}; + +// +// fdRegId::hash() +// +inline resTableIndex fdRegId::hash () const +{ + const unsigned fdManagerHashTableMinIndexBits = 8; + const unsigned fdManagerHashTableMaxIndexBits = sizeof(SOCKET)*CHAR_BIT; + resTableIndex hashid; + + hashid = integerHash ( fdManagerHashTableMinIndexBits, + fdManagerHashTableMaxIndexBits, this->fd ); + + // + // also evenly distribute based on the type of fdRegType + // + hashid ^= this->type; + + // + // the result here is always masked to the + // proper size after it is returned to the resource class + // + return hashid; +} + +inline void fdManager::lazyInitTimerQueue () +{ + if ( ! this->pTimerQueue ) { + this->pTimerQueue = & epicsTimerQueuePassive::create ( *this ); + } +} + +inline epicsTimer & fdManager::createTimer () +{ + this->lazyInitTimerQueue (); + return this->pTimerQueue->createTimer (); +} + +#endif // fdManagerH_included + diff --git a/src/libCom/fdmgr/fdmgr.cpp b/src/libCom/fdmgr/fdmgr.cpp new file mode 100644 index 000000000..2d9245bd3 --- /dev/null +++ b/src/libCom/fdmgr/fdmgr.cpp @@ -0,0 +1,337 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// +// File descriptor management C++ class library +// (for multiplexing IO in a single threaded environment) +// +// Author Jeffrey O. Hill +// johill@lanl.gov +// 505 665 1831 +// +// NOTES: +// 1) the routines in this file provide backward compatibility with the original +// "C" based file descriptor manager API +// 2) This library is _not_ thread safe +// + +#include +#define epicsExportSharedSymbols +#include "locationException.h" +#include "epicsAssert.h" +#include "fdManager.h" +#include "fdmgr.h" + +static const fdRegType fdiToFdRegType[] = {fdrRead, fdrWrite, fdrException}; +static const unsigned fdiToFdRegTypeNElements = sizeof (fdiToFdRegType) / sizeof (fdiToFdRegType[0]); +const unsigned mSecPerSec = 1000u; +const unsigned uSecPerSec = 1000u * mSecPerSec; + +class fdRegForOldFdmgr : public fdReg { +public: + // + // exceptions + // + class noFunctionSpecified {}; + class doubleDelete {}; + + epicsShareFunc fdRegForOldFdmgr (const SOCKET fdIn, const fdRegType type, + const bool onceOnly, fdManager &manager, pCallBackFDMgr pFunc, void *pParam); + epicsShareFunc ~fdRegForOldFdmgr (); + +private: + pCallBackFDMgr pFunc; + void *pParam; + epicsShareFunc virtual void callBack (); + fdRegForOldFdmgr ( const fdRegForOldFdmgr & ); + fdRegForOldFdmgr & operator = ( const fdRegForOldFdmgr & ); +}; + +class oldFdmgr; + +// +// timerForOldFdmgr +// +class timerForOldFdmgr : public epicsTimerNotify, public chronIntIdRes { +public: + epicsShareFunc timerForOldFdmgr (oldFdmgr &fdmgr, double delay, pCallBackFDMgr pFunc, void *pParam); + epicsShareFunc virtual ~timerForOldFdmgr (); + + // + // exceptions + // + class noFunctionSpecified {}; + class doubleDelete {}; +private: + epicsTimer &timer; + oldFdmgr &fdmgr; + pCallBackFDMgr pFunc; + void *pParam; + unsigned id; + epicsShareFunc expireStatus expire ( const epicsTime & currentTime ); + timerForOldFdmgr ( const timerForOldFdmgr & ); + timerForOldFdmgr & operator = ( const timerForOldFdmgr & ); +}; + +class oldFdmgr : public fdManager { + friend class timerForOldFdmgr; + friend epicsShareFunc int epicsShareAPI fdmgr_clear_timeout (fdctx *pfdctx, fdmgrAlarmId id); + +public: + oldFdmgr (); + +private: + chronIntIdResTable resTbl; + oldFdmgr ( const oldFdmgr & ); + oldFdmgr & operator = ( const oldFdmgr & ); +}; + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class chronIntIdResTable ; +template class resTable; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +epicsShareFunc fdRegForOldFdmgr::fdRegForOldFdmgr + (const SOCKET fdIn, const fdRegType typeIn, + const bool onceOnlyIn, fdManager &managerIn, + pCallBackFDMgr pFuncIn, void *pParamIn) : + fdReg (fdIn, typeIn, onceOnlyIn, managerIn), + pFunc (pFuncIn), pParam (pParamIn) +{ + if (pFuncIn==NULL) { + throwWithLocation ( noFunctionSpecified () ); + } +} + +epicsShareFunc fdRegForOldFdmgr::~fdRegForOldFdmgr () +{ + if (this->pFunc==NULL) { + throwWithLocation ( doubleDelete () ); + } +} + +epicsShareFunc void fdRegForOldFdmgr::callBack () +{ + (*this->pFunc) (this->pParam); +} + +timerForOldFdmgr::timerForOldFdmgr ( oldFdmgr &fdmgrIn, + double delayIn, pCallBackFDMgr pFuncIn, void * pParamIn ) : + timer ( fdmgrIn.createTimer() ), + fdmgr ( fdmgrIn ), pFunc ( pFuncIn ), pParam( pParamIn ) +{ + if ( pFuncIn == NULL ) { + throwWithLocation ( noFunctionSpecified () ); + } + this->fdmgr.resTbl.idAssignAdd (*this); + this->timer.start ( *this, delayIn ); +} + +timerForOldFdmgr::~timerForOldFdmgr () +{ + this->fdmgr.resTbl.remove ( this->getId() ); + this->timer.destroy (); +} + +epicsTimerNotify::expireStatus timerForOldFdmgr::expire ( const epicsTime & ) +{ + (*this->pFunc) (this->pParam); + return noRestart; +} + +oldFdmgr::oldFdmgr () {} + +extern "C" epicsShareFunc fdctx * epicsShareAPI fdmgr_init (void) +{ + oldFdmgr *pfdm; + + try { + pfdm = new oldFdmgr(); + } + catch (...) + { + pfdm = NULL; + } + + return (fdctx *) pfdm; +} + +extern "C" epicsShareFunc fdmgrAlarmId epicsShareAPI fdmgr_add_timeout ( + fdctx *pfdctx, struct timeval *ptimeout, pCallBackFDMgr pFunc, void *pParam) +{ + double delay = ptimeout->tv_sec + ptimeout->tv_usec / static_cast (uSecPerSec); + oldFdmgr *pfdm = static_cast (pfdctx); + timerForOldFdmgr *pTimer; + unsigned id = fdmgrNoAlarm; + + if (!pfdm) { + return fdmgrNoAlarm; + } + + while (true) { + try { + pTimer = new timerForOldFdmgr + (*pfdm, delay, pFunc, pParam); + } + catch (...) + { + pTimer = NULL; + } + if (pTimer) { + id = pTimer->getId (); + if (id!=fdmgrNoAlarm) { + break; + } + else { + delete pTimer; + } + } + else { + break; + } + } + + return id; +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_timeout (fdctx *pfdctx, fdmgrAlarmId id) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + timerForOldFdmgr *pTimer; + + try { + pTimer = pfdm->resTbl.remove (id); + } + catch (...) + { + pTimer = NULL; + } + + if (pTimer==NULL) { + return -1; + } + delete pTimer; + return 0; +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_add_callback ( // X aCC 361 + fdctx *pfdctx, SOCKET fd, enum fdi_type fdi, pCallBackFDMgr pFunc, void *pParam) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + fdRegForOldFdmgr *pfdrbc; + bool onceOnly = (fdi==fdi_write); + unsigned fdiType; + + if (pfdm==NULL) { + return -1; + } + + if (pFunc==NULL) { + return -1; + } + + if (fdi<0) { + return -1; + } + + fdiType = (unsigned) fdi; + + if (fdiType>=fdiToFdRegTypeNElements) { + return -1; + } + + try { + pfdrbc = new fdRegForOldFdmgr (fd, fdiToFdRegType[fdiType], onceOnly, *pfdm, pFunc, pParam); + } + catch (...) + { + pfdrbc = NULL; + } + + if (pfdrbc==NULL) { + return -1; + } + else { + return 0; + } +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_callback ( // X aCC 361 + fdctx *pfdctx, SOCKET fd, enum fdi_type fdi) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + fdReg *pFDR; + + if (pfdm==NULL) { + return -1; + } + + try { + pFDR = pfdm->lookUpFD (fd, fdiToFdRegType[fdi]); + } + catch (...) + { + pFDR = NULL; + } + + if (pFDR==NULL) { + return -1; + } + else { + delete pFDR; + return 0; + } +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_pend_event (fdctx *pfdctx, struct timeval *ptimeout) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + double delay = ptimeout->tv_sec + ptimeout->tv_usec / static_cast (uSecPerSec); + + try { + pfdm->process (delay); + } + catch (...) { + return -1; + } + + return 0; +} + +extern "C" epicsShareFunc int epicsShareAPI fdmgr_delete (fdctx *pfdctx) +{ + oldFdmgr *pfdm = static_cast (pfdctx); + delete pfdm; + return 0; +} + +/* + * depricated interface + */ +extern "C" epicsShareFunc int epicsShareAPI fdmgr_clear_fd (fdctx *pfdctx, SOCKET fd) +{ + return fdmgr_clear_callback(pfdctx, fd, fdi_read); +} + +/* + * depricated interface + */ +extern "C" epicsShareFunc int epicsShareAPI fdmgr_add_fd ( + fdctx *pfdctx, SOCKET fd, void (*pfunc)(void *pParam), void *param) +{ + return fdmgr_add_callback (pfdctx, fd, fdi_read, pfunc, param); +} diff --git a/src/libCom/fdmgr/fdmgr.h b/src/libCom/fdmgr/fdmgr.h new file mode 100644 index 000000000..63803075b --- /dev/null +++ b/src/libCom/fdmgr/fdmgr.h @@ -0,0 +1,171 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* fdmgr.h + * + * share/epicsH/Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Header file associated with a file descriptor manager + * for use with the UNIX system call select + * + * Author Jeffrey O. Hill + * hill@atdiv.lanl.gov + * 505 665 1831 + */ + +#ifndef includeFdmgrH +#define includeFdmgrH + +#include "ellLib.h" +#include "bucketLib.h" +#include "osiSock.h" +#include "epicsThread.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum fdi_type {fdi_read, fdi_write, fdi_excp}; +enum alarm_list_type {alt_invalid, alt_alarm, alt_expired, alt_free}; + +typedef void fdctx; +typedef void (*pCallBackFDMgr)(void *); + +/* + * C "typedef" name "alarm" was changed to "fdmgrAlarm" to avoid collisions + * with other libraries. Next the identifier was changed again to + * an unsigned integer type "fdmgrAlarmId". + * + * This "#define" is for codes that used to use a pointer to the old typedef + * "alarm" or "fdmgrAlarm" types to identify an alarm. + * + * ie the following code will allow compilation against + * all versions: + * + * #if defined (NEW_FDMGR_ALARMID) + * fdmgrAlarmId XXXX + * #elif defined (NEW_FDMGR_ALARM) + * fdmgrAlarm *XXXX; + * #else + * alarm *XXXX; + * #endif + * + * XXXX = fdmgrAlarmId fdmgr_add_timeout() + */ +typedef unsigned fdmgrAlarmId; +#define NEW_FDMGR_ALARMID + +/* + * + * Initialize a file descriptor manager session + * + */ +epicsShareFunc fdctx * epicsShareAPI fdmgr_init(void); + +/* + * Specify a function to be called with a specified parameter + * after a specified delay relative to the current time + * + * Returns fdmgrNoAlarm (zero) if alarm cant be created + */ +#define fdmgrNoAlarm 0 +epicsShareFunc fdmgrAlarmId epicsShareAPI fdmgr_add_timeout( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +struct timeval *ptimeout, /* relative delay from current time */ +pCallBackFDMgr pfunc, /* function (handler) to call */ +void *param /* first parameter passed to the func */ +); + +/* + * Clear a timeout which has not executed its function (handler) + * yet. + */ +epicsShareFunc int epicsShareAPI fdmgr_clear_timeout( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +fdmgrAlarmId id /* alarm to delete */ +); + +/* + * + * Specify a function (handler) to be called with a specified parameter + * when a file descriptor becomes active. The parameter fdi (file + * descriptor interest) specifies the type of activity (IO) we wish + * to be informed of: read, write, or exception. For more + * info on this see the man pages for the UNIX system call select(). + * + * read and exception callbacks are permanent( ie the application's + * function (handler) continues to be called each time the + * file descriptor becomes active until fdmgr_add_callback() + * is called). + * + * write callbacks are called only once after each call to + * fdmgr_add_callback() + * + */ +epicsShareFunc int epicsShareAPI fdmgr_add_callback( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd, /* file descriptor */ +enum fdi_type fdi, /* file descriptor interest type */ +pCallBackFDMgr pfunc, /* function (handler) to call */ +void *param /* first parameter passed to the func */ +); + +/* + * + * Clear nterest in a type of file descriptor activity (IO). + * + */ +epicsShareFunc int epicsShareAPI fdmgr_clear_callback( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd, /* file descriptor */ +enum fdi_type fdi /* file descriptor interest type */ +); + +/* + * + * Wait a specified delay relative from the current time for file + * descriptor activity (IO) or timeouts (timer expiration). Application + * specified functions (handlers) will not be called unless the + * application waits in this function or polls it frequently + * enough. + * + */ +epicsShareFunc int epicsShareAPI fdmgr_pend_event( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +struct timeval *ptimeout +); + + +/* + * obsolete interface + */ +epicsShareFunc int epicsShareAPI fdmgr_clear_fd( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd +); + +/* + * obsolete interface + */ +epicsShareFunc int epicsShareAPI fdmgr_add_fd( +fdctx *pfdctx, /* fd mgr ctx from fdmgr_init() */ +SOCKET fd, +pCallBackFDMgr pfunc, /* function (handler) to call */ +void *param +); + +epicsShareFunc int epicsShareAPI fdmgr_delete(fdctx *pfdctx); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef includeFdmgrH (last line in this file) */ + diff --git a/src/libCom/freeList/freeList.h b/src/libCom/freeList/freeList.h new file mode 100644 index 000000000..dc047010b --- /dev/null +++ b/src/libCom/freeList/freeList.h @@ -0,0 +1,35 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* share/epicsH/freeList.h */ +/* share/epicsH Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* Author: Marty Kraimer Date: 04-19-94 */ + +#ifndef INCfreeListh +#define INCfreeListh + +#include +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI freeListInitPvt(void **ppvt,int size,int nmalloc); +epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt); +epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt); +epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem); +epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt); +epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt); + +#ifdef __cplusplus +} +#endif + +#endif /*INCfreeListh*/ diff --git a/src/libCom/freeList/freeList.html b/src/libCom/freeList/freeList.html new file mode 100644 index 000000000..853cf9d35 --- /dev/null +++ b/src/libCom/freeList/freeList.html @@ -0,0 +1,85 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +
    +
    +
    +
    +

    VERSION $Id: freeList.3,v 1.2 1997/04/10 19:47:35

    +
    +
    +

    NAME

    +     freeList.c - General Purpose memory free list library
    +
    +
    +
    +

    SYNOPSIS

    +     freeListInitPvt     - Initialize a free list
    +     freeListCalloc - Allocate and initialize to zero a new element
    +     freeListMalloc - Allocate a new element
    +     freeListFree   - Free an element,i.e. put on free list
    +
    +
    +     void freeListInitPvt(void **ppvt,int size,int nmalloc);
    +     void *freeListCalloc(void *pvt);
    +     void *freeListMalloc(void *pvt);
    +     size_t freeListItemsAvail(void *pvt);
    +     void freeListFree(void *pvt,void*pmem);
    +
    +     where :
    +
    +     pvt  - For private use by library. Caller must provide a "void *pvt"
    +     size - Size in butes of each element. Note that all elements must be same size
    +     nmalloc   - Number of elements top allocate when regular malloc must be called.
    +
    +
    +
    +
    +

    DESCRIPTION

    +     This library can be used to allocate  and  free  fixed  size
    +     memory  elements.   Free  elements  are maintained on a free
    +     list rather then being returned to the  heap  via  calls  to
    +     free.  When  it  is  necessary to call malloc, memory can be
    +     allocated in multiples of the element size.
    +
    +
    +
    +

    RETURNS

    +     freeListCalloc and freeListMalloc return address of element allocated
    +     or NULL if no more memory could be obtained via call to malloc
    +
    +
    +
    +

    INCLUDES

    +     freeLib.h
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +Man(1) output converted with +man2html +
    + + diff --git a/src/libCom/freeList/freeListLib.c b/src/libCom/freeList/freeListLib.c new file mode 100644 index 000000000..0ed4e8e22 --- /dev/null +++ b/src/libCom/freeList/freeListLib.c @@ -0,0 +1,153 @@ +/*************************************************************************\ +* 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: Marty Kraimer Date: 04-19-94 */ + + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsMutex.h" +#include "freeList.h" +#include "adjustment.h" + +typedef struct allocMem { + struct allocMem *next; + void *memory; +}allocMem; +typedef struct { + int size; + int nmalloc; + void *head; + allocMem *mallochead; + size_t nBlocksAvailable; + epicsMutexId lock; +}FREELISTPVT; + +epicsShareFunc void epicsShareAPI + freeListInitPvt(void **ppvt,int size,int nmalloc) +{ + FREELISTPVT *pfl; + + pfl = callocMustSucceed(1,sizeof(FREELISTPVT), "freeListInitPvt"); + pfl->size = adjustToWorstCaseAlignment(size); + pfl->nmalloc = nmalloc; + pfl->head = NULL; + pfl->mallochead = NULL; + pfl->nBlocksAvailable = 0u; + pfl->lock = epicsMutexMustCreate(); + *ppvt = (void *)pfl; + return; +} + +epicsShareFunc void * epicsShareAPI freeListCalloc(void *pvt) +{ + FREELISTPVT *pfl = pvt; +# ifdef EPICS_FREELIST_DEBUG + return callocMustSucceed(1,pfl->size,"freeList Debug Calloc"); +# else + void *ptemp; + + ptemp = freeListMalloc(pvt); + if(ptemp) memset((char *)ptemp,0,pfl->size); + return(ptemp); +# endif +} + +epicsShareFunc void * epicsShareAPI freeListMalloc(void *pvt) +{ + FREELISTPVT *pfl = pvt; +# ifdef EPICS_FREELIST_DEBUG + return callocMustSucceed(1,pfl->size,"freeList Debug Malloc"); +# else + void *ptemp; + void **ppnext; + allocMem *pallocmem; + int i; + + epicsMutexMustLock(pfl->lock); + ptemp = pfl->head; + if(ptemp==0) { + ptemp = (void *)malloc(pfl->nmalloc*pfl->size); + if(ptemp==0) { + epicsMutexUnlock(pfl->lock); + return(0); + } + pallocmem = (allocMem *)calloc(1,sizeof(allocMem)); + if(pallocmem==0) { + epicsMutexUnlock(pfl->lock); + free(ptemp); + return(0); + } + pallocmem->memory = ptemp; + if(pfl->mallochead) + pallocmem->next = pfl->mallochead; + pfl->mallochead = pallocmem; + for(i=0; inmalloc; i++) { + ppnext = ptemp; + *ppnext = pfl->head; + pfl->head = ptemp; + ptemp = ((char *)ptemp) + pfl->size; + } + ptemp = pfl->head; + pfl->nBlocksAvailable += pfl->nmalloc; + } + ppnext = pfl->head; + pfl->head = *ppnext; + pfl->nBlocksAvailable--; + epicsMutexUnlock(pfl->lock); + return(ptemp); +# endif +} + +epicsShareFunc void epicsShareAPI freeListFree(void *pvt,void*pmem) +{ + FREELISTPVT *pfl = pvt; +# ifdef EPICS_FREELIST_DEBUG + memset ( pmem, 0xdd, pfl->size ); + free(pmem); +# else + void **ppnext; + + epicsMutexMustLock(pfl->lock); + ppnext = pmem; + *ppnext = pfl->head; + pfl->head = pmem; + pfl->nBlocksAvailable++; + epicsMutexUnlock(pfl->lock); +# endif +} + +epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt) +{ + FREELISTPVT *pfl = pvt; + allocMem *phead; + allocMem *pnext; + + phead = pfl->mallochead; + while(phead) { + pnext = phead->next; + free(phead->memory); + free(phead); + phead = pnext; + } + epicsMutexDestroy(pfl->lock); + free(pvt); +} + +epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt) +{ + FREELISTPVT *pfl = pvt; + return pfl->nBlocksAvailable; +} + diff --git a/src/libCom/gpHash/gpHash.h b/src/libCom/gpHash/gpHash.h new file mode 100644 index 000000000..f8e8f41f1 --- /dev/null +++ b/src/libCom/gpHash/gpHash.h @@ -0,0 +1,52 @@ +/*************************************************************************\ +* 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: Marty Kraimer Date: 04-07-94 */ + +/* gph provides a general purpose directory accessed via a hash table*/ + +#ifndef INC_gpHash_H +#define INC_gpHash_H + +#include "shareLib.h" + +#include "ellLib.h" + +typedef struct{ + ELLNODE node; + const char *name; /*address of name placed in directory*/ + void *pvtid; /*private name for subsystem user*/ + void *userPvt; /*private for user*/ +} GPHENTRY; + +struct gphPvt; + +#ifdef __cplusplus +extern "C" { +#endif + +/*tableSize must be power of 2 in range 256 to 65536*/ +epicsShareFunc void epicsShareAPI + gphInitPvt(struct gphPvt **ppvt, int tableSize); +epicsShareFunc GPHENTRY * epicsShareAPI + gphFind(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc GPHENTRY * epicsShareAPI + gphAdd(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc void epicsShareAPI + gphDelete(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc void epicsShareAPI gphFreeMem(struct gphPvt *pvt); +epicsShareFunc void epicsShareAPI gphDump(struct gphPvt *pvt); +epicsShareFunc void epicsShareAPI gphDumpFP(FILE *fp, struct gphPvt *pvt); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_gpHash_H */ diff --git a/src/libCom/gpHash/gpHash.html b/src/libCom/gpHash/gpHash.html new file mode 100644 index 000000000..658e47f47 --- /dev/null +++ b/src/libCom/gpHash/gpHash.html @@ -0,0 +1,139 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +
    +
    +
    +
    +

    VERSION $Id: gpHash.3,v 1.2 1999/09/13 19:18:04

    +
    +
    +

    NAME

    +     gpHash.c - General Purpose Hash Library
    +
    +
    +
    +

    SYNOPSIS

    +     gphInitPvt     - Initialize
    +     gphFind        - Find an element taht has been hashed
    +     gphAdd         - Add a new entry
    +     gphDelete - Delete an entry
    +     gphFreeMem     - Free all memory allocated by gpHash
    +     gphDump        - Dump current members
    +
    +
    +     typedef struct{
    +         ELLNODE     node;
    +         char        *name;          /*address of name placed in directory*/
    +         void        *pvtid;         /*private name for subsystem user*/
    +         void        *userPvt;       /*private for user*/
    +     } GPHENTRY;
    +
    +     void    gphInitPvt(void **ppvt);
    +     GPHENTRY *gphFind(void *pvt,const char *name,void *pvtid);
    +     GPHENTRY *gphAdd(void *pvt,const char *name,void *pvtid);
    +     void gphDelete(void *pvt,const char *name,void *pvtid);
    +     void gphFreeMem(void *pvt);
    +     void gphDump(void *pvt);
    +
    +
    +     where :
    +
    +     pvt  - For private use by library. Caller must provide a "void *pvt"
    +     name - The character string that will be hashed and added to table
    +     pvtid     - The name plus value of this pointer constitute unique entry
    +
    +
    +
    +
    +

    DESCRIPTION

    +     This library provides a general purpose directory  of  names
    +     that  is  accessed via a hash table. The hash table contains
    +     256 entries. Each entry is a list of members  that  hash  to
    +     the  same  value. The user can maintain seperate directories
    +     via the same table by having  a  different  pvtid  for  each
    +     directory.
    +
    +
    +
    +

    RETURNS

    +     gphFind returns the address of the GPHENTRY describing the entry or NULL if name was not found.
    +     gphAdd returns the address of the new GPHENTRY describing the entry or NULL if name was already
    +     present.
    +
    +
    +
    +

    INCLUDES

    +     gpHash.h
    +
    +
    +
    +
    +

    REFERENCE

    +     Fast Hashing of Variable Length Text Strings, Peter K. Pear-
    +     son, Communications of the ACM, June 1990
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +Man(1) output converted with +man2html +
    + + diff --git a/src/libCom/gpHash/gpHashLib.c b/src/libCom/gpHash/gpHashLib.c new file mode 100644 index 000000000..44384fe48 --- /dev/null +++ b/src/libCom/gpHash/gpHashLib.c @@ -0,0 +1,231 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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: Marty Kraimer Date: 04-07-94 */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsMutex.h" +#include "epicsString.h" +#include "dbDefs.h" +#include "ellLib.h" +#include "epicsPrint.h" +#include "gpHash.h" + +typedef struct gphPvt { + int size; + unsigned int mask; + ELLLIST **paplist; /*pointer to array of pointers to ELLLIST */ + epicsMutexId lock; +} gphPvt; + +#define MIN_SIZE 256 +#define DEFAULT_SIZE 512 +#define MAX_SIZE 65536 + +void epicsShareAPI gphInitPvt(gphPvt **ppvt, int size) +{ + gphPvt *pgphPvt; + + if (size & (size - 1)) { + printf("gphInitPvt: %d is not a power of 2\n", size); + size = DEFAULT_SIZE; + } + + if (size < MIN_SIZE) + size = MIN_SIZE; + + if (size > MAX_SIZE) + size = MAX_SIZE; + + pgphPvt = callocMustSucceed(1, sizeof(gphPvt), "gphInitPvt"); + pgphPvt->size = size; + pgphPvt->mask = size - 1; + pgphPvt->paplist = callocMustSucceed(size, sizeof(ELLLIST *), "gphInitPvt"); + pgphPvt->lock = epicsMutexMustCreate(); + *ppvt = pgphPvt; + return; +} + +GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + ELLLIST **paplist; + ELLLIST *gphlist; + GPHENTRY *pgphNode; + int hash; + + if (pgphPvt == NULL) return NULL; + paplist = pgphPvt->paplist; + hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); + hash = epicsStrHash(name, hash) & pgphPvt->mask; + + epicsMutexMustLock(pgphPvt->lock); + gphlist = paplist[hash]; + if (gphlist == NULL) { + pgphNode = NULL; + } else { + pgphNode = (GPHENTRY *) ellFirst(gphlist); + } + + while (pgphNode) { + if (pvtid == pgphNode->pvtid && + strcmp(name, pgphNode->name) == 0) break; + pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); + } + + epicsMutexUnlock(pgphPvt->lock); + return pgphNode; +} + +GPHENTRY * epicsShareAPI gphAdd(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + ELLLIST **paplist; + ELLLIST *plist; + GPHENTRY *pgphNode; + int hash; + + if (pgphPvt == NULL) return NULL; + paplist = pgphPvt->paplist; + hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); + hash = epicsStrHash(name, hash) & pgphPvt->mask; + + epicsMutexMustLock(pgphPvt->lock); + plist = paplist[hash]; + if (plist == NULL) { + plist = callocMustSucceed(1, sizeof(ELLLIST), "gphAdd"); + ellInit(plist); + paplist[hash] = plist; + } + + pgphNode = (GPHENTRY *) ellFirst(plist); + while (pgphNode) { + if (pvtid == pgphNode->pvtid && + strcmp(name, pgphNode->name) == 0) { + epicsMutexUnlock(pgphPvt->lock); + return NULL; + } + pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); + } + + pgphNode = callocMustSucceed(1, sizeof(GPHENTRY), "gphAdd"); + pgphNode->name = name; + pgphNode->pvtid = pvtid; + ellAdd(plist, (ELLNODE *)pgphNode); + + epicsMutexUnlock(pgphPvt->lock); + return (pgphNode); +} + +void epicsShareAPI gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + ELLLIST **paplist; + ELLLIST *plist = NULL; + GPHENTRY *pgphNode; + int hash; + + if (pgphPvt == NULL) return; + paplist = pgphPvt->paplist; + hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); + hash = epicsStrHash(name, hash) & pgphPvt->mask; + + epicsMutexMustLock(pgphPvt->lock); + if (paplist[hash] == NULL) { + pgphNode = NULL; + } else { + plist = paplist[hash]; + pgphNode = (GPHENTRY *) ellFirst(plist); + } + + while(pgphNode) { + if (pvtid == pgphNode->pvtid && + strcmp(name, pgphNode->name) == 0) { + ellDelete(plist, (ELLNODE*)pgphNode); + free((void *)pgphNode); + break; + } + pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); + } + + epicsMutexUnlock(pgphPvt->lock); + return; +} + +void epicsShareAPI gphFreeMem(gphPvt *pgphPvt) +{ + ELLLIST **paplist; + int h; + + /* Caller must ensure that no other thread is using *pvt */ + if (pgphPvt == NULL) return; + + paplist = pgphPvt->paplist; + for (h = 0; h < pgphPvt->size; h++) { + ELLLIST *plist = paplist[h]; + GPHENTRY *pgphNode; + GPHENTRY *next; + + if (plist == NULL) continue; + pgphNode = (GPHENTRY *) ellFirst(plist); + + while (pgphNode) { + next = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); + ellDelete(plist, (ELLNODE*)pgphNode); + free(pgphNode); + pgphNode = next; + } + free(paplist[h]); + } + epicsMutexDestroy(pgphPvt->lock); + free(paplist); + free(pgphPvt); +} + +void epicsShareAPI gphDump(gphPvt *pgphPvt) +{ + gphDumpFP(stdout, pgphPvt); +} + +void epicsShareAPI gphDumpFP(FILE *fp, gphPvt *pgphPvt) +{ + unsigned int empty = 0; + ELLLIST **paplist; + int h; + + if (pgphPvt == NULL) return; + + printf("Hash table has %d buckets", pgphPvt->size); + + paplist = pgphPvt->paplist; + for (h = 0; h < pgphPvt->size; h++) { + ELLLIST *plist = paplist[h]; + GPHENTRY *pgphNode; + int i = 0; + + if (plist == NULL) { + empty++; + continue; + } + pgphNode = (GPHENTRY *) ellFirst(plist); + + fprintf(fp, "\n [%3d] %3d ", h, ellCount(plist)); + while (pgphNode) { + if (!(++i % 3)) + fprintf(fp, "\n "); + fprintf(fp, " %s %p", pgphNode->name, pgphNode->pvtid); + pgphNode = (GPHENTRY *) ellNext((ELLNODE*)pgphNode); + } + } + fprintf(fp, "\n%u buckets empty.\n", empty); +} diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp new file mode 100644 index 000000000..ce15ed9a1 --- /dev/null +++ b/src/libCom/iocsh/iocsh.cpp @@ -0,0 +1,926 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* iocsh.cpp */ +/* Author: Marty Kraimer Date: 27APR2000 */ +/* Heavily modified by Eric Norum Date: 03MAY2000 */ +/* Adapted to C++ by Eric Norum Date: 18DEC2000 */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "errlog.h" +#include "macLib.h" +#include "epicsStdio.h" +#include "epicsString.h" +#include "epicsStdlib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "envDefs.h" +#include "registry.h" +#include "epicsReadline.h" +#include "cantProceed.h" +#include "iocsh.h" + +extern "C" { + +/* + * Global link to pdbbase + */ +epicsShareDef struct dbBase **iocshPpdbbase; + +/* + * File-local information + */ +struct iocshCommand { + iocshFuncDef const *pFuncDef; + iocshCallFunc func; + struct iocshCommand *next; +}; +static struct iocshCommand *iocshCommandHead; +static char iocshCmdID[] = "iocshCmd"; +struct iocshVariable { + iocshVarDef const *pVarDef; + struct iocshVariable *next; +}; +static struct iocshVariable *iocshVariableHead; +static char iocshVarID[] = "iocshVar"; +extern "C" { static void varCallFunc(const iocshArgBuf *); } +static epicsMutexId iocshTableMutex; +static epicsThreadOnceId iocshTableOnceId = EPICS_THREAD_ONCE_INIT; + +/* + * I/O redirection + */ +#define NREDIRECTS 5 +struct iocshRedirect { + const char *name; + const char *mode; + FILE *fp; + FILE *oldFp; +}; + +/* + * Set up command table mutex + */ +static void iocshTableOnce (void *) +{ + iocshTableMutex = epicsMutexMustCreate (); +} + +/* + * Lock the table mutex + */ +static void +iocshTableLock (void) +{ + epicsThreadOnce (&iocshTableOnceId, iocshTableOnce, NULL); + epicsMutexMustLock (iocshTableMutex); +} + +/* + * Unlock the table mutex + */ +static void +iocshTableUnlock (void) +{ + epicsThreadOnce (&iocshTableOnceId, iocshTableOnce, NULL); + epicsMutexUnlock (iocshTableMutex); +} + +/* + * Register a command + */ +void epicsShareAPI iocshRegister (const iocshFuncDef *piocshFuncDef, iocshCallFunc func) +{ + struct iocshCommand *l, *p, *n; + int i; + + iocshTableLock (); + for (l = NULL, p = iocshCommandHead ; p != NULL ; l = p, p = p->next) { + i = strcmp (piocshFuncDef->name, p->pFuncDef->name); + if (i == 0) { + p->pFuncDef = piocshFuncDef; + p->func = func; + iocshTableUnlock (); + return; + } + if (i < 0) + break; + } + n = (struct iocshCommand *)callocMustSucceed (1, sizeof *n, "iocshRegister"); + if (!registryAdd(iocshCmdID, piocshFuncDef->name, (void *)n)) { + free (n); + iocshTableUnlock (); + errlogPrintf ("iocshRegister failed to add %s\n", piocshFuncDef->name); + return; + } + if (l == NULL) { + n->next = iocshCommandHead; + iocshCommandHead = n; + } + else { + n->next = l->next; + l->next = n; + } + n->pFuncDef = piocshFuncDef; + n->func = func; + iocshTableUnlock (); +} + +/* + * Register the "var" command and any variable(s) + */ +static const iocshArg varCmdArg0 = { "[variable", iocshArgString}; +static const iocshArg varCmdArg1 = { "[value]]", iocshArgString}; +static const iocshArg *varCmdArgs[2] = {&varCmdArg0, &varCmdArg1}; +static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs}; + +void epicsShareAPI iocshRegisterVariable (const iocshVarDef *piocshVarDef) +{ + struct iocshVariable *l, *p, *n; + int i; + int found; + + iocshTableLock (); + while ((piocshVarDef != NULL) + && (piocshVarDef->name != NULL) + && (*piocshVarDef->name != '\0')) { + if (iocshVariableHead == NULL) + iocshRegister(&varFuncDef,varCallFunc); + found = 0; + for (l = NULL, p = iocshVariableHead ; p != NULL ; l = p, p = p->next) { + i = strcmp (piocshVarDef->name, p->pVarDef->name); + if (i == 0) { + errlogPrintf("Warning -- iocshRegisterVariable redefining %s.\n", piocshVarDef->name); + p->pVarDef = piocshVarDef; + found = 1; + break; + } + if (i < 0) + break; + } + if (!found) { + n = (struct iocshVariable *)callocMustSucceed(1, sizeof *n, "iocshRegisterVariable"); + if (!registryAdd(iocshVarID, piocshVarDef->name, (void *)n)) { + free(n); + iocshTableUnlock(); + errlogPrintf("iocshRegisterVariable failed to add %s.\n", piocshVarDef->name); + return; + } + if (l == NULL) { + n->next = iocshVariableHead; + iocshVariableHead = n; + } + else { + n->next = l->next; + l->next = n; + } + n->pVarDef = piocshVarDef; + } + piocshVarDef++; + } + iocshTableUnlock (); +} + +/* + * Free storage created by iocshRegister/iocshRegisterVariable + */ +void epicsShareAPI iocshFree(void) +{ + struct iocshCommand *pc, *nc; + struct iocshVariable *pv, *nv; + + iocshTableLock (); + for (pc = iocshCommandHead ; pc != NULL ; ) { + nc = pc->next; + free (pc); + pc = nc; + } + for (pv = iocshVariableHead ; pv != NULL ; ) { + nv = pv->next; + free (pv); + pv = nv; + } + iocshTableUnlock (); +} + +/* + * Report an error + */ +static void +showError (const char *filename, int lineno, const char *msg, ...) +{ + va_list ap; + + va_start (ap, msg); + if (filename) + fprintf (stderr, "%s -- Line %d -- ", filename, lineno); + vfprintf (stderr, msg, ap); + fputc ('\n', stderr); + va_end (ap); +} + +static int +cvtArg (const char *filename, int lineno, char *arg, iocshArgBuf *argBuf, const iocshArg *piocshArg) +{ + char *endp; + + switch (piocshArg->type) { + case iocshArgInt: + if (arg && *arg) { + errno = 0; + argBuf->ival = strtol (arg, &endp, 0); + if (errno == ERANGE) { + errno = 0; + argBuf->ival = strtoul (arg, &endp, 0); + if (errno == ERANGE) { + showError (filename, lineno, "Integer '%s' out of range", arg); + return 0; + } + } + if (*endp) { + showError (filename, lineno, "Illegal integer '%s'", arg); + return 0; + } + } + else { + argBuf->ival = 0; + } + break; + + case iocshArgDouble: + if (arg && *arg) { + argBuf->dval = epicsStrtod (arg, &endp); + if (*endp) { + showError (filename, lineno, "Illegal double '%s'", arg); + return 0; + } + } + else { + argBuf->dval = 0.0; + } + break; + + case iocshArgString: + argBuf->sval = arg; + break; + + case iocshArgPersistentString: + argBuf->sval = epicsStrDup(arg); + if (argBuf->sval == NULL) { + showError (filename, lineno, "Out of memory"); + return 0; + } + break; + + case iocshArgPdbbase: + /* Argument must be missing or 0 or pdbbase */ + if(!arg || !*arg || (*arg == '0') || (strcmp(arg, "pdbbase") == 0)) { + if(!iocshPpdbbase || !*iocshPpdbbase) { + showError (filename, lineno, "pdbbase not present"); + return 0; + } + argBuf->vval = *iocshPpdbbase; + break; + } + showError (filename, lineno, "Expecting 'pdbbase' got '%s'", arg); + return 0; + + default: + showError (filename, lineno, "Illegal argument type %d", piocshArg->type); + return 0; + } + return 1; +} + +/* + * Open redirected I/O + */ +static int +openRedirect(const char *filename, int lineno, struct iocshRedirect *redirect) +{ + int i; + + for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { + if (redirect->name != NULL) { + redirect->fp = fopen(redirect->name, redirect->mode); + if (redirect->fp == NULL) { + showError(filename, lineno, "Can't open \"%s\": %s.", + redirect->name, strerror(errno)); + redirect->name = NULL; + while (i--) { + redirect--; + if (redirect->fp) { + fclose(redirect->fp); + redirect->fp = NULL; + } + redirect->name = NULL; + } + return -1; + } + } + } + return 0; +} + +/* + * Start I/O redirection + */ +static void +startRedirect(const char * /*filename*/, int /*lineno*/, + struct iocshRedirect *redirect) +{ + int i; + + for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { + if (redirect->fp != NULL) { + switch(i) { + case 0: + redirect->oldFp = epicsGetThreadStdin(); + epicsSetThreadStdin(redirect->fp); + break; + case 1: + redirect->oldFp = epicsGetThreadStdout(); + epicsSetThreadStdout(redirect->fp); + break; + case 2: + redirect->oldFp = epicsGetThreadStderr(); + epicsSetThreadStderr(redirect->fp); + break; + } + } + } +} + +/* + * Finish up I/O redirection + */ +static void +stopRedirect(const char *filename, int lineno, struct iocshRedirect *redirect) +{ + int i; + + for (i = 0 ; i < NREDIRECTS ; i++, redirect++) { + if (redirect->fp != NULL) { + if (fclose(redirect->fp) != 0) + showError(filename, lineno, "Error closing \"%s\": %s.", + redirect->name, strerror(errno)); + redirect->fp = NULL; + switch(i) { + case 0: epicsSetThreadStdin(redirect->oldFp); break; + case 1: epicsSetThreadStdout(redirect->oldFp); break; + case 2: epicsSetThreadStderr(redirect->oldFp); break; + } + } + redirect->name = NULL; + } +} + +/* + * "help" command + */ +static const iocshArg helpArg0 = { "[command ...]",iocshArgArgv}; +static const iocshArg *helpArgs[1] = {&helpArg0}; +static const iocshFuncDef helpFuncDef = + {"help",1,helpArgs}; +static void helpCallFunc(const iocshArgBuf *args) +{ + int argc = args[0].aval.ac; + const char * const * argv = args[0].aval.av; + struct iocshFuncDef const *piocshFuncDef; + struct iocshCommand *pcmd; + + if (argc == 1) { + int l, col = 0; + + printf ("Type 'help command_name' to get more information about a particular command.\n"); + iocshTableLock (); + for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) { + piocshFuncDef = pcmd->pFuncDef; + l = strlen (piocshFuncDef->name); + if ((l + col) >= 79) { + fputc ('\n', stdout); + col = 0; + } + fputs (piocshFuncDef->name, stdout); + col += l; + if (col >= 64) { + fputc ('\n', stdout); + col = 0; + } + else { + do { + fputc (' ', stdout); + col++; + } while ((col % 16) != 0); + } + } + if (col) + fputc ('\n', stdout); + iocshTableUnlock (); + } + else { + for (int iarg = 1 ; iarg < argc ; iarg++) { + for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) { + piocshFuncDef = pcmd->pFuncDef; + if (epicsStrGlobMatch(piocshFuncDef->name, argv[iarg]) != 0) { + fputs (piocshFuncDef->name, stdout); + for (int a = 0 ; a < piocshFuncDef->nargs ; a++) { + const char *cp = piocshFuncDef->arg[a]->name; + if ((piocshFuncDef->arg[a]->type == iocshArgArgv) + || (strchr (cp, ' ') == NULL)) { + fprintf (stdout, " %s", cp); + } + else { + fprintf (stdout, " '%s'", cp); + } + } + fprintf (stdout,"\n");; + } + } + } + } +} + +/* + * The body of the command interpreter + */ +static int +iocshBody (const char *pathname, const char *commandLine) +{ + FILE *fp = NULL; + const char *filename = NULL; + int icin, icout; + char c; + int quote, inword, backslash; + const char *raw = NULL;; + char *line = NULL; + int lineno = 0; + int argc; + char **argv = NULL; + int argvCapacity = 0; + struct iocshRedirect *redirects = NULL; + struct iocshRedirect *redirect = NULL; + int sep; + const char *prompt = NULL; + const char *ifs = " \t(),\r"; + iocshArgBuf *argBuf = NULL; + int argBufCapacity = 0; + struct iocshCommand *found; + struct iocshFuncDef const *piocshFuncDef; + void *readlineContext = NULL; + int wasOkToBlock; + + /* + * See if command interpreter is interactive + */ + if (commandLine == NULL) { + if ((pathname == NULL) || (strcmp (pathname, "") == 0)) { + if ((prompt = envGetConfigParamPtr(&IOCSH_PS1)) == NULL) + prompt = "epics> "; + } + else { + fp = fopen (pathname, "r"); + if (fp == NULL) { + fprintf (stderr, "Can't open %s: %s\n", pathname, strerror (errno)); + return -1; + } + if ((filename = strrchr (pathname, '/')) == NULL) + filename = pathname; + else + filename++; + prompt = NULL; + } + + /* + * Create a command-line input context + */ + if ((readlineContext = epicsReadlineBegin(fp)) == NULL) { + fprintf(stderr, "Can't allocate command-line object.\n"); + if (fp) + fclose(fp); + return -1; + } + } + + /* + * Set up redirection + */ + redirects = (struct iocshRedirect *)calloc(NREDIRECTS, sizeof *redirects); + if (redirects == NULL) { + printf ("Out of memory!\n"); + return -1; + } + + /* + * Read commands till EOF or exit + */ + argc = 0; + wasOkToBlock = epicsThreadIsOkToBlock(); + epicsThreadSetOkToBlock(1); + for (;;) { + /* + * Read a line + */ + if (commandLine) { + if (raw != NULL) + break; + raw = commandLine; + } + else { + if ((raw = epicsReadline(prompt, readlineContext)) == NULL) + break; + } + lineno++; + + /* + * Ignore comment lines other than to echo + * them if they came from a script. + */ + if (*raw == '#') { + if ((prompt == NULL) && (commandLine == NULL)) + puts(raw); + continue; + } + + /* + * Expand macros + */ + free(line); + if ((line = macEnvExpand(raw)) == NULL) + continue; + + /* + * Echo commands read from scripts + */ + if ((prompt == NULL) && *line && (commandLine == NULL)) + puts(line); + + /* + * Break line into words + */ + icout = icin = 0; + inword = 0; + argc = 0; + quote = EOF; + backslash = 0; + redirect = NULL; + for (;;) { + if (argc >= argvCapacity) { + char **av; + argvCapacity += 50; + av = (char **)realloc (argv, argvCapacity * sizeof *argv); + if (av == NULL) { + printf ("Out of memory!\n"); + argc = -1; + break; + } + argv = av; + } + c = line[icin++]; + if (c == '\0') + break; + if ((quote == EOF) && !backslash && (strchr (ifs, c))) + sep = 1; + else + sep = 0; + if ((quote == EOF) && !backslash) { + int redirectFd = 1; + if (c == '\\') { + backslash = 1; + continue; + } + if (c == '<') { + if (redirect != NULL) { + break; + } + redirect = &redirects[0]; + sep = 1; + redirect->mode = "r"; + } + if ((c >= '1') && (c <= '9') && (line[icin] == '>')) { + redirectFd = c - '0'; + c = '>'; + icin++; + } + if (c == '>') { + if (redirect != NULL) + break; + if (redirectFd >= NREDIRECTS) { + redirect = &redirects[1]; + break; + } + redirect = &redirects[redirectFd]; + sep = 1; + if (line[icin] == '>') { + icin++; + redirect->mode = "a"; + } + else { + redirect->mode = "w"; + } + } + } + if (inword) { + if (c == quote) { + quote = EOF; + } + else { + if ((quote == EOF) && !backslash) { + if (sep) { + inword = 0; + line[icout++] = '\0'; + } + else if ((c == '"') || (c == '\'')) { + quote = c; + } + else { + line[icout++] = c; + } + } + else { + line[icout++] = c; + } + } + } + else { + if (!sep) { + if (((c == '"') || (c == '\'')) && !backslash) + quote = c; + if (redirect != NULL) { + if (redirect->name != NULL) { + argc = -1; + break; + } + redirect->name = line + icout; + redirect = NULL; + } + else { + argv[argc++] = line + icout; + } + if (quote == EOF) + line[icout++] = c; + inword = 1; + } + } + backslash = 0; + } + if (redirect != NULL) { + showError (filename, lineno, "Illegal redirection."); + continue; + } + if (argc < 0) + break; + if (quote != EOF) { + showError (filename, lineno, "Unbalanced quote."); + continue; + } + if (backslash) { + showError (filename, lineno, "Trailing backslash."); + continue; + } + if (inword) + line[icout++] = '\0'; + argv[argc] = NULL; + + /* + * Special case -- Redirected input but no command + * Treat as if 'iocsh filename'. + */ + if ((argc == 0) && (redirects[0].name != NULL)) { + const char *commandFile = redirects[0].name; + redirects[0].name = NULL; + if (openRedirect(filename, lineno, redirects) < 0) + continue; + startRedirect(filename, lineno, redirects); + iocshBody(commandFile, NULL); + stopRedirect(filename, lineno, redirects); + continue; + } + if (openRedirect(filename, lineno, redirects) < 0) + continue; + + /* + * Look up command + */ + if (argc) { + /* + * Special command? + */ + if (strncmp (argv[0], "exit", 4) == 0) + break; + if ((strcmp (argv[0], "?") == 0) + || (strncmp (argv[0], "help", 4) == 0)) { + } + + /* + * Look up command + */ + found = (iocshCommand *)registryFind (iocshCmdID, argv[0]); + if (!found) { + showError (filename, lineno, "Command %s not found.", argv[0]); + continue; + } + piocshFuncDef = found->pFuncDef; + + /* + * Process arguments and call function + */ + for (int iarg = 0 ; ; ) { + if (iarg == piocshFuncDef->nargs) { + startRedirect(filename, lineno, redirects); + (*found->func)(argBuf); + stopRedirect(filename, lineno, redirects); + break; + } + if (iarg >= argBufCapacity) { + void *np; + + argBufCapacity += 20; + np = realloc (argBuf, argBufCapacity * sizeof *argBuf); + if (np == NULL) { + fprintf (stderr, "Out of memory!\n"); + argBufCapacity -= 20; + break; + } + argBuf = (iocshArgBuf *)np; + } + if (piocshFuncDef->arg[iarg]->type == iocshArgArgv) { + argBuf[iarg].aval.ac = argc-iarg; + argBuf[iarg].aval.av = argv+iarg; + iarg = piocshFuncDef->nargs; + } + else { + if (!cvtArg (filename, lineno, + ((iarg < argc) ? argv[iarg+1] : NULL), + &argBuf[iarg], piocshFuncDef->arg[iarg])) + break; + iarg++; + } + } + if((prompt != NULL) && (strcmp(argv[0], "epicsEnvSet") == 0)) { + const char *newPrompt; + if ((newPrompt = envGetConfigParamPtr(&IOCSH_PS1)) != NULL) + prompt = newPrompt; + } + } + } + if (fp && (fp != stdin)) + fclose (fp); + if (redirects != NULL) { + stopRedirect(filename, lineno, redirects); + free (redirects); + } + free(line); + free (argv); + free (argBuf); + errlogFlush(); + if (readlineContext) + epicsReadlineEnd(readlineContext); + epicsThreadSetOkToBlock( wasOkToBlock); + return 0; +} + +/* + * External access to the command interpreter + */ +int epicsShareAPI +iocsh (const char *pathname) +{ + return iocshBody(pathname, NULL); +} + +int epicsShareAPI +iocshCmd (const char *cmd) +{ + if (cmd == NULL) + return 0; + return iocshBody(NULL, cmd); +} + +/* + * Internal commands + */ +static void varHandler(const iocshVarDef *v, const char *setString) +{ + switch(v->type) { + default: + printf("Can't handle variable %s of type %d.\n", v->name, v->type); + return; + case iocshArgInt: break; + case iocshArgDouble: break; + } + if(setString == NULL) { + switch(v->type) { + default: break; + case iocshArgInt: + printf("%s = %d\n", v->name, *(int *)v->pval); + break; + case iocshArgDouble: + printf("%s = %g\n", v->name, *(double *)v->pval); + break; + } + } + else { + switch(v->type) { + default: break; + case iocshArgInt: + { + char *endp; + long ltmp = strtol(setString, &endp, 0); + if((*setString != '\0') && (*endp == '\0')) + *(int *)v->pval = ltmp; + else + printf("Invalid value -- value of %s not changed.\n", v->name); + break; + } + case iocshArgDouble: + { + char *endp; + double dtmp = epicsStrtod(setString, &endp); + if((*setString != '\0') && (*endp == '\0')) + *(double *)v->pval = dtmp; + else + printf("Invalid value -- value of %s not changed.\n", v->name); + break; + } + } + } +} + +static void varCallFunc(const iocshArgBuf *args) +{ + struct iocshVariable *v; + if(args[0].sval == NULL) { + for (v = iocshVariableHead ; v != NULL ; v = v->next) + varHandler(v->pVarDef, args[1].sval); + } + else { + v = (iocshVariable *)registryFind(iocshVarID, args[0].sval); + if (v == NULL) { + printf("%s -- no such variable.\n", args[0].sval); + } + else { + varHandler(v->pVarDef, args[1].sval); + } + } +} + +/* iocshCmd */ +static const iocshArg iocshCmdArg0 = { "command",iocshArgString}; +static const iocshArg *iocshCmdArgs[1] = {&iocshCmdArg0}; +static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs}; +static void iocshCmdCallFunc(const iocshArgBuf *args) +{ + iocshCmd(args[0].sval); +} + +/* + * Dummy internal commands -- register and install in command table + * so they show up in the help display + */ + +/* comment */ +static const iocshArg commentArg0 = { "newline-terminated comment",iocshArgArgv}; +static const iocshArg *commentArgs[1] = {&commentArg0}; +static const iocshFuncDef commentFuncDef = {"#",1,commentArgs}; +static void commentCallFunc(const iocshArgBuf *) +{ +} + +/* exit */ +static const iocshFuncDef exitFuncDef = + {"exit",0,0}; +static void exitCallFunc(const iocshArgBuf *) +{ +} + +static void localRegister (void) +{ + iocshRegister(&commentFuncDef,commentCallFunc); + iocshRegister(&exitFuncDef,exitCallFunc); + iocshRegister(&helpFuncDef,helpCallFunc); + iocshRegister(&iocshCmdFuncDef,iocshCmdCallFunc); +} + +} /* extern "C" */ + +/* + * Register local commands on application startup + */ +class IocshRegister { + public: + IocshRegister() { localRegister(); } +}; +static IocshRegister iocshRegisterObj; diff --git a/src/libCom/iocsh/iocsh.h b/src/libCom/iocsh/iocsh.h new file mode 100644 index 000000000..d89991050 --- /dev/null +++ b/src/libCom/iocsh/iocsh.h @@ -0,0 +1,81 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* iocsh.h ioc: call registered function*/ +/* Author: Marty Kraimer Date: 27APR2000 */ + +#ifndef INCiocshH +#define INCiocshH + +#include +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + iocshArgInt, + iocshArgDouble, + iocshArgString, + iocshArgPdbbase, + iocshArgArgv, + iocshArgPersistentString +}iocshArgType; + +typedef union iocshArgBuf { + int ival; + double dval; + char *sval; + void *vval; + struct { + int ac; + char **av; + } aval; +}iocshArgBuf; + +typedef struct iocshVarDef { + const char *name; + iocshArgType type; + void * pval; +}iocshVarDef; + +typedef struct iocshArg { + const char *name; + iocshArgType type; +}iocshArg; + +typedef struct iocshFuncDef { + const char *name; + int nargs; + const iocshArg * const *arg; +}iocshFuncDef; + +typedef void (*iocshCallFunc)(const iocshArgBuf *argBuf); + +epicsShareFunc void epicsShareAPI iocshRegister( + const iocshFuncDef *piocshFuncDef, iocshCallFunc func); +epicsShareFunc void epicsShareAPI iocshRegisterVariable ( + const iocshVarDef *piocshVarDef); + +/* iocshFree frees storage used by iocshRegister*/ +/* This should only be called when iocsh is no longer needed*/ +epicsShareFunc void epicsShareAPI iocshFree(void); + +epicsShareFunc int epicsShareAPI iocsh(const char *pathname); +epicsShareFunc int epicsShareAPI iocshCmd(const char *cmd); + +/* 'weak' link to pdbbase */ +epicsShareExtern struct dbBase **iocshPpdbbase; + +#ifdef __cplusplus +} +#endif + +#endif /*INCiocshH*/ diff --git a/src/libCom/iocsh/libComRegister.c b/src/libCom/iocsh/libComRegister.c new file mode 100644 index 000000000..6cd79bb66 --- /dev/null +++ b/src/libCom/iocsh/libComRegister.c @@ -0,0 +1,368 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define epicsExportSharedSymbols +#include "iocsh.h" +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "envDefs.h" +#include "osiUnistd.h" +#include "logClient.h" +#include "errlog.h" +#include "taskwd.h" +#include "registry.h" +#include "epicsGeneralTime.h" +#include "libComRegister.h" + + +void date(const char *format) +{ + epicsTimeStamp now; + char nowText[80] = {'\0'}; + + if (epicsTimeGetCurrent(&now)) { + puts("Current time not available."); + return; + } + if (format == NULL || format[0] == '\0') + format = "%Y/%m/%d %H:%M:%S.%06f"; + epicsTimeToStrftime(nowText, sizeof(nowText), format, &now); + puts(nowText); +} + +/* date */ +static const iocshArg dateArg0 = { "format",iocshArgString}; +static const iocshArg * const dateArgs[] = {&dateArg0}; +static const iocshFuncDef dateFuncDef = {"date", 1, dateArgs}; +static void dateCallFunc (const iocshArgBuf *args) +{ + date(args[0].sval); +} + + +/* chdir */ +static const iocshArg chdirArg0 = { "directory name",iocshArgString}; +static const iocshArg * const chdirArgs[1] = {&chdirArg0}; +static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs}; +static void chdirCallFunc(const iocshArgBuf *args) +{ + int status; + status = chdir(args[0].sval); + if (status) { + printf ("Invalid directory path ignored\n"); + } +} + +/* print current working directory */ +static const iocshFuncDef pwdFuncDef = { "pwd", 0, 0 }; +static void pwdCallFunc (const iocshArgBuf *args) +{ + char buf[256]; + char *pwd = getcwd ( buf, sizeof(buf) - 1 ); + if ( pwd ) { + printf ( "%s\n", pwd ); + } +} + +/* epicsEnvSet */ +static const iocshArg epicsEnvSetArg0 = { "name",iocshArgString}; +static const iocshArg epicsEnvSetArg1 = { "value",iocshArgString}; +static const iocshArg * const epicsEnvSetArgs[2] = {&epicsEnvSetArg0,&epicsEnvSetArg1}; +static const iocshFuncDef epicsEnvSetFuncDef = {"epicsEnvSet",2,epicsEnvSetArgs}; +static void epicsEnvSetCallFunc(const iocshArgBuf *args) +{ + char *name = args[0].sval; + char *value = args[1].sval; + + if (name == NULL) { + printf ("Missing environment variable name argument.\n"); + return; + } + if (value == NULL) { + printf ("Missing environment variable value argument.\n"); + return; + } + epicsEnvSet (name, value); +} + +/* epicsParamShow */ +static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL}; +static void epicsParamShowCallFunc(const iocshArgBuf *args) +{ + epicsPrtEnvParams (); +} + +/* epicsPrtEnvParams */ +static const iocshFuncDef epicsPrtEnvParamsFuncDef = {"epicsPrtEnvParams",0,0}; +static void epicsPrtEnvParamsCallFunc(const iocshArgBuf *args) +{ + epicsPrtEnvParams (); +} + +/* epicsEnvShow */ +static const iocshArg epicsEnvShowArg0 = { "[name]",iocshArgString}; +static const iocshArg * const epicsEnvShowArgs[1] = {&epicsEnvShowArg0}; +static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs}; +static void epicsEnvShowCallFunc(const iocshArgBuf *args) +{ + epicsEnvShow (args[0].sval); +} + +/* registryDump */ +static const iocshFuncDef registryDumpFuncDef = {"registryDump",0,NULL}; +static void registryDumpCallFunc(const iocshArgBuf *args) +{ + registryDump (); +} + +/* iocLogInit */ +static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0}; +static void iocLogInitCallFunc(const iocshArgBuf *args) +{ + iocLogInit (); +} + +/* iocLogDisable */ +static const iocshArg iocLogDisableArg0 = {"(0,1)=>(false,true)",iocshArgInt}; +static const iocshArg * const iocLogDisableArgs[1] = {&iocLogDisableArg0}; +static const iocshFuncDef iocLogDisableFuncDef = {"setIocLogDisable",1,iocLogDisableArgs}; +static void iocLogDisableCallFunc(const iocshArgBuf *args) +{ + iocLogDisable = args[0].ival; +} + +/* iocLogShow */ +static const iocshArg iocLogShowArg0 = {"level",iocshArgInt}; +static const iocshArg * const iocLogShowArgs[1] = {&iocLogShowArg0}; +static const iocshFuncDef iocLogShowFuncDef = {"iocLogShow",1,iocLogShowArgs}; +static void iocLogShowCallFunc(const iocshArgBuf *args) +{ + iocLogShow (args[0].ival); +} + +/* eltc */ +static const iocshArg eltcArg0 = {"(0,1)=>(false,true)",iocshArgInt}; +static const iocshArg * const eltcArgs[1] = {&eltcArg0}; +static const iocshFuncDef eltcFuncDef = {"eltc",1,eltcArgs}; +static void eltcCallFunc(const iocshArgBuf *args) +{ + eltc(args[0].ival); +} + +/* errlogInit */ +static const iocshArg errlogInitArg0 = { "bufsize",iocshArgInt}; +static const iocshArg * const errlogInitArgs[1] = {&errlogInitArg0}; +static const iocshFuncDef errlogInitFuncDef = + {"errlogInit",1,errlogInitArgs}; +static void errlogInitCallFunc(const iocshArgBuf *args) +{ + errlogInit(args[0].ival); +} + +/* errlogInit2 */ +static const iocshArg errlogInit2Arg0 = { "bufSize",iocshArgInt}; +static const iocshArg errlogInit2Arg1 = { "maxMsgSize",iocshArgInt}; +static const iocshArg * const errlogInit2Args[] = + {&errlogInit2Arg0, &errlogInit2Arg1}; +static const iocshFuncDef errlogInit2FuncDef = + {"errlogInit2", 2, errlogInit2Args}; +static void errlogInit2CallFunc(const iocshArgBuf *args) +{ + errlogInit2(args[0].ival, args[1].ival); +} + +/* errlog */ +static const iocshArg errlogArg0 = { "message",iocshArgString}; +static const iocshArg * const errlogArgs[1] = {&errlogArg0}; +static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs}; +static void errlogCallFunc(const iocshArgBuf *args) +{ + errlogPrintfNoConsole("%s\n", args[0].sval); +} + +/* epicsThreadShowAll */ +static const iocshArg epicsThreadShowAllArg0 = { "level",iocshArgInt}; +static const iocshArg * const epicsThreadShowAllArgs[1] = {&epicsThreadShowAllArg0}; +static const iocshFuncDef epicsThreadShowAllFuncDef = + {"epicsThreadShowAll",1,epicsThreadShowAllArgs}; +static void epicsThreadShowAllCallFunc(const iocshArgBuf *args) +{ + epicsThreadShowAll(args[0].ival); +} + +/* epicsThreadShow */ +static const iocshArg threadArg0 = { "[-level] [thread ...]", iocshArgArgv}; +static const iocshArg * const threadArgs[1] = { &threadArg0 }; +static const iocshFuncDef threadFuncDef = {"epicsThreadShow",1,threadArgs}; +static void threadCallFunc(const iocshArgBuf *args) +{ + int i = 1; + int first = 1; + int level = 0; + const char *cp; + epicsThreadId tid; + unsigned long ltmp; + int argc = args[0].aval.ac; + char **argv = args[0].aval.av; + char *endp; + + if ((i < argc) && (*(cp = argv[i]) == '-')) { + level = atoi (cp + 1); + i++; + } + if (i >= argc) { + epicsThreadShowAll (level); + return; + } + for ( ; i < argc ; i++) { + cp = argv[i]; + ltmp = strtoul (cp, &endp, 0); + if (*endp) { + tid = epicsThreadGetId (cp); + if (!tid) { + printf ("\t'%s' is not a known thread name\n", cp); + continue; + } + } + else { + tid = (epicsThreadId)ltmp; + } + if (first) { + epicsThreadShow (0, level); + first = 0; + } + epicsThreadShow (tid, level); + } +} + +/* taskwdShow */ +static const iocshArg taskwdShowArg0 = { "level",iocshArgInt}; +static const iocshArg * const taskwdShowArgs[1] = {&taskwdShowArg0}; +static const iocshFuncDef taskwdShowFuncDef = + {"taskwdShow",1,taskwdShowArgs}; +static void taskwdShowCallFunc(const iocshArgBuf *args) +{ + taskwdShow(args[0].ival); +} + +/* epicsMutexShowAll */ +static const iocshArg epicsMutexShowAllArg0 = { "onlyLocked",iocshArgInt}; +static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt}; +static const iocshArg * const epicsMutexShowAllArgs[2] = + {&epicsMutexShowAllArg0,&epicsMutexShowAllArg1}; +static const iocshFuncDef epicsMutexShowAllFuncDef = + {"epicsMutexShowAll",1,epicsMutexShowAllArgs}; +static void epicsMutexShowAllCallFunc(const iocshArgBuf *args) +{ + epicsMutexShowAll(args[0].ival,args[1].ival); +} + +/* epicsThreadSleep */ +static const iocshArg epicsThreadSleepArg0 = { "seconds",iocshArgDouble}; +static const iocshArg * const epicsThreadSleepArgs[1] = {&epicsThreadSleepArg0}; +static const iocshFuncDef epicsThreadSleepFuncDef = + {"epicsThreadSleep",1,epicsThreadSleepArgs}; +static void epicsThreadSleepCallFunc(const iocshArgBuf *args) +{ + epicsThreadSleep(args[0].dval); +} + +/* epicsThreadResume */ +static const iocshArg epicsThreadResumeArg0 = { "[thread ...]", iocshArgArgv}; +static const iocshArg * const epicsThreadResumeArgs[1] = { &epicsThreadResumeArg0 }; +static const iocshFuncDef epicsThreadResumeFuncDef = {"epicsThreadResume",1,epicsThreadResumeArgs}; +static void epicsThreadResumeCallFunc(const iocshArgBuf *args) +{ + int i; + const char *cp; + epicsThreadId tid; + unsigned long ltmp; + char nameBuf[64]; + int argc = args[0].aval.ac; + char **argv = args[0].aval.av; + char *endp; + + for (i = 1 ; i < argc ; i++) { + cp = argv[i]; + ltmp = strtoul(cp, &endp, 0); + if (*endp) { + tid = epicsThreadGetId(cp); + if (!tid) { + printf("*** argument %d (%s) is not a valid thread name ***\n", i, cp); + continue; + } + } + else { + tid =(epicsThreadId)ltmp; + epicsThreadGetName(tid, nameBuf, sizeof nameBuf); + if (nameBuf[0] == '\0') { + printf("*** argument %d (%s) is not a valid thread id ***\n", i, cp); + continue; + } + + } + if (!epicsThreadIsSuspended(tid)) { + printf("*** Thread %s is not suspended ***\n", cp); + continue; + } + epicsThreadResume(tid); + } +} + +/* generalTimeReport */ +static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgArgv}; +static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 }; +static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs}; +static void generalTimeReportCallFunc(const iocshArgBuf *args) +{ + generalTimeReport(args[0].ival); +} + +/* installLastResortEventProvider */ +static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL}; +static void installLastResortEventProviderCallFunc(const iocshArgBuf *args) +{ + installLastResortEventProvider(); +} + +void epicsShareAPI libComRegister(void) +{ + iocshRegister(&dateFuncDef, dateCallFunc); + iocshRegister(&chdirFuncDef, chdirCallFunc); + iocshRegister(&pwdFuncDef, pwdCallFunc); + + iocshRegister(&epicsEnvSetFuncDef, epicsEnvSetCallFunc); + iocshRegister(&epicsParamShowFuncDef, epicsParamShowCallFunc); + iocshRegister(&epicsPrtEnvParamsFuncDef, epicsPrtEnvParamsCallFunc); + iocshRegister(&epicsEnvShowFuncDef, epicsEnvShowCallFunc); + iocshRegister(®istryDumpFuncDef, registryDumpCallFunc); + + iocshRegister(&iocLogInitFuncDef, iocLogInitCallFunc); + iocshRegister(&iocLogDisableFuncDef, iocLogDisableCallFunc); + iocshRegister(&iocLogShowFuncDef, iocLogShowCallFunc); + iocshRegister(&eltcFuncDef, eltcCallFunc); + iocshRegister(&errlogInitFuncDef,errlogInitCallFunc); + iocshRegister(&errlogInit2FuncDef,errlogInit2CallFunc); + iocshRegister(&errlogFuncDef, errlogCallFunc); + + iocshRegister(&epicsThreadShowAllFuncDef,epicsThreadShowAllCallFunc); + iocshRegister(&threadFuncDef, threadCallFunc); + iocshRegister(&taskwdShowFuncDef,taskwdShowCallFunc); + iocshRegister(&epicsMutexShowAllFuncDef,epicsMutexShowAllCallFunc); + iocshRegister(&epicsThreadSleepFuncDef,epicsThreadSleepCallFunc); + iocshRegister(&epicsThreadResumeFuncDef,epicsThreadResumeCallFunc); + + iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc); + iocshRegister(&installLastResortEventProviderFuncDef, installLastResortEventProviderCallFunc); +} diff --git a/src/libCom/iocsh/libComRegister.h b/src/libCom/iocsh/libComRegister.h new file mode 100644 index 000000000..ef93f4a8d --- /dev/null +++ b/src/libCom/iocsh/libComRegister.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_libComRegister_H +#define INC_libComRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI libComRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_libComRegister_H */ diff --git a/src/libCom/iocsh/registry.c b/src/libCom/iocsh/registry.c new file mode 100644 index 000000000..89b50ccca --- /dev/null +++ b/src/libCom/iocsh/registry.c @@ -0,0 +1,91 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*registry.c */ + +/* Author: Marty Kraimer Date: 08JUN99 */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "cantProceed.h" +#include "epicsFindSymbol.h" +#include "gpHash.h" +#include "registry.h" + +static struct gphPvt *gphPvt = 0; + +static void registryInit(int tableSize) +{ + if(tableSize==0) tableSize = DEFAULT_TABLE_SIZE; + gphInitPvt(&gphPvt,tableSize); + if(!gphPvt) cantProceed("registry why did gphInitPvt fail\n"); +} + +epicsShareFunc int epicsShareAPI registrySetTableSize(int size) +{ + if(gphPvt) { + printf("registryInit already called\n"); + return(-1); + } + registryInit(size); + return(0); +} + + +epicsShareFunc int epicsShareAPI registryAdd( + void *registryID,const char *name,void *data) +{ + GPHENTRY *pentry; + if(!gphPvt) registryInit(0); + pentry = gphAdd(gphPvt,name,registryID); + if(!pentry) return(FALSE); + pentry->userPvt = data; + return(TRUE); +} + +epicsShareFunc int epicsShareAPI registryChange( + void *registryID,const char *name,void *data) +{ + GPHENTRY *pentry; + if(!gphPvt) registryInit(0); + pentry = gphFind(gphPvt,(char *)name,registryID); + if(!pentry) return(FALSE); + pentry->userPvt = data; + return(TRUE); +} + +epicsShareFunc void * epicsShareAPI registryFind( + void *registryID,const char *name) +{ + GPHENTRY *pentry; + if(name==0) return(0); + if(registryID==0) return(epicsFindSymbol(name)); + if(!gphPvt) registryInit(0); + pentry = gphFind(gphPvt,(char *)name,registryID); + if(!pentry) return(0); + return(pentry->userPvt); +} + +epicsShareFunc void epicsShareAPI registryFree(void) +{ + if(!gphPvt) return; + gphFreeMem(gphPvt); +} + +epicsShareFunc int epicsShareAPI registryDump(void) +{ + if(!gphPvt) return(0); + gphDump(gphPvt); + return(0); +} diff --git a/src/libCom/iocsh/registry.h b/src/libCom/iocsh/registry.h new file mode 100644 index 000000000..ea1bb7ce7 --- /dev/null +++ b/src/libCom/iocsh/registry.h @@ -0,0 +1,35 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef INCregistryh +#define INCregistryh + +#include "shareLib.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFAULT_TABLE_SIZE 1024 + +epicsShareFunc int epicsShareAPI registryAdd( + void *registryID,const char *name,void *data); +epicsShareFunc void *epicsShareAPI registryFind( + void *registryID,const char *name); +epicsShareFunc int epicsShareAPI registryChange( + void *registryID,const char *name,void *data); + +epicsShareFunc int epicsShareAPI registrySetTableSize(int size); +epicsShareFunc void epicsShareAPI registryFree(void); +epicsShareFunc int epicsShareAPI registryDump(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INCregistryh */ diff --git a/src/libCom/logClient/iocLog.c b/src/libCom/logClient/iocLog.c new file mode 100644 index 000000000..c6c707c30 --- /dev/null +++ b/src/libCom/logClient/iocLog.c @@ -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. +\*************************************************************************/ +/* logClient.c,v 1.25.2.6 2004/10/07 13:37:34 mrk Exp */ +/* + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "envDefs.h" +#include "errlog.h" +#include "logClient.h" +#include "iocLog.h" + +int iocLogDisable = 0; + +static const int iocLogSuccess = 0; +static const int iocLogError = -1; + +static logClientId iocLogClient; + +/* + * getConfig() + * Get Server Configuration + */ +static int getConfig (struct in_addr *pserver_addr, unsigned short *pserver_port) +{ + long status; + long epics_port; + + status = envGetLongConfigParam (&EPICS_IOC_LOG_PORT, &epics_port); + if (status<0) { + fprintf (stderr, + "iocLog: EPICS environment variable \"%s\" undefined\n", + EPICS_IOC_LOG_PORT.name); + return iocLogError; + } + + if (epics_port<0 || epics_port>USHRT_MAX) { + fprintf (stderr, + "iocLog: EPICS environment variable \"%s\" out of range\n", + EPICS_IOC_LOG_PORT.name); + return iocLogError; + } + *pserver_port = (unsigned short) epics_port; + + status = envGetInetAddrConfigParam (&EPICS_IOC_LOG_INET, pserver_addr); + if (status<0) { + fprintf (stderr, + "iocLog: EPICS environment variable \"%s\" undefined\n", + EPICS_IOC_LOG_INET.name); + return iocLogError; + } + + return iocLogSuccess; +} + +/* + * iocLogFlush () + */ +void epicsShareAPI epicsShareAPI iocLogFlush (void) +{ + if (iocLogClient!=NULL) { + logClientFlush (iocLogClient); + } +} + +/* + * iocLogClientInit() + */ +static logClientId iocLogClientInit (void) +{ + int status; + logClientId id; + struct in_addr addr; + unsigned short port; + + status = getConfig (&addr, &port); + if (status) { + return NULL; + } + id = logClientCreate (addr, port); + if (id != NULL) { + errlogAddListener ( logClientSendMessage, id ); + } + return id; +} + +/* + * iocLogInit() + */ +int epicsShareAPI iocLogInit (void) +{ + /* + * check for global disable + */ + if (iocLogDisable) { + return iocLogSuccess; + } + /* + * dont init twice + */ + if (iocLogClient!=NULL) { + return iocLogSuccess; + } + iocLogClient = iocLogClientInit (); + if (iocLogClient) { + return iocLogSuccess; + } + else { + return iocLogError; + } +} + +/* + * iocLogShow () + */ +void epicsShareAPI iocLogShow (unsigned level) +{ + if (iocLogClient!=NULL) { + logClientShow (iocLogClient, level); + } +} + +/* + * logClientInit(); deprecated + */ +logClientId epicsShareAPI logClientInit (void) +{ + return iocLogClientInit (); +} + +/* + * logClientSendMessage (); deprecated + */ +void logClientSendMessage ( logClientId id, const char * message ) +{ + if ( !iocLogDisable ) { + logClientSend (id, message); + } +} diff --git a/src/libCom/logClient/iocLog.h b/src/libCom/logClient/iocLog.h new file mode 100644 index 000000000..b6f7ddf6e --- /dev/null +++ b/src/libCom/logClient/iocLog.h @@ -0,0 +1,38 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* logClient.h,v 1.5.2.1 2003/07/08 00:08:06 jhill Exp */ +/* + * + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#ifndef INCiocLogh +#define INCiocLogh 1 +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ioc log client interface + */ +epicsShareExtern int iocLogDisable; +epicsShareFunc int epicsShareAPI iocLogInit (void); +epicsShareFunc void epicsShareAPI iocLogShow (unsigned level); +epicsShareFunc void epicsShareAPI iocLogFlush (void); + +#ifdef __cplusplus +} +#endif + +#endif /*INCiocLogh*/ diff --git a/src/libCom/logClient/logClient.c b/src/libCom/logClient/logClient.c new file mode 100644 index 000000000..adaaa7f95 --- /dev/null +++ b/src/libCom/logClient/logClient.c @@ -0,0 +1,557 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* logClient.c,v 1.25.2.6 2004/10/07 13:37:34 mrk Exp */ +/* + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +/* + * ANSI C + */ +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "osiSock.h" +#include "epicsAssert.h" +#include "epicsExit.h" +#include "epicsSignal.h" + +#include "logClient.h" + +static const int logClientSuccess = 0; +static const int logClientError = -1; + +typedef struct { + char msgBuf[0x4000]; + struct sockaddr_in addr; + char name[64]; + epicsMutexId mutex; + SOCKET sock; + epicsThreadId restartThreadId; + epicsEventId stateChangeNotify; + unsigned connectCount; + unsigned nextMsgIndex; + unsigned connected; + unsigned shutdown; + unsigned shutdownConfirm; + int connFailStatus; +} logClient; + +static const double LOG_RESTART_DELAY = 5.0; /* sec */ +static const double LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT = 5.0; /* sec */ +static const double LOG_SERVER_SHUTDOWN_TIMEOUT = 30.0; /* sec */ + +/* + * logClientClose () + */ +static void logClientClose ( logClient *pClient ) +{ +# ifdef DEBUG + fprintf (stderr, "log client: lingering for connection close..."); + fflush (stderr); +# endif + + /* + * mutex on + */ + epicsMutexMustLock (pClient->mutex); + + /* + * close any preexisting connection to the log server + */ + if ( pClient->sock != INVALID_SOCKET ) { + epicsSocketDestroy ( pClient->sock ); + pClient->sock = INVALID_SOCKET; + } + + pClient->nextMsgIndex = 0u; + memset ( pClient->msgBuf, '\0', sizeof ( pClient->msgBuf ) ); + pClient->connected = 0u; + + /* + * mutex off + */ + epicsMutexUnlock (pClient->mutex); + +# ifdef DEBUG + fprintf (stderr, "done\n"); +# endif +} + +/* + * logClientDestroy + */ +static void logClientDestroy (logClientId id) +{ + enum epicsSocketSystemCallInterruptMechanismQueryInfo interruptInfo; + logClient *pClient = (logClient *) id; + epicsTimeStamp begin, current; + double diff; + + /* command log client thread to shutdown - taking mutex here */ + /* forces cache flush on SMP machines */ + epicsMutexMustLock ( pClient->mutex ); + pClient->shutdown = 1u; + epicsMutexUnlock ( pClient->mutex ); + + /* unblock log client thread blocking in send() or connect() */ + interruptInfo = + epicsSocketSystemCallInterruptMechanismQuery (); + switch ( interruptInfo ) { + case esscimqi_socketCloseRequired: + logClientClose ( pClient ); + break; + case esscimqi_socketBothShutdownRequired: + shutdown ( pClient->sock, SHUT_WR ); + break; + case esscimqi_socketSigAlarmRequired: + epicsSignalRaiseSigAlarm ( pClient->restartThreadId ); + break; + default: + break; + }; + + /* wait for confirmation that the thread exited - taking */ + /* mutex here forces cache update on SMP machines */ + epicsTimeGetCurrent ( & begin ); + epicsMutexMustLock ( pClient->mutex ); + do { + epicsMutexUnlock ( pClient->mutex ); + epicsEventWaitWithTimeout ( + pClient->stateChangeNotify, + LOG_SERVER_SHUTDOWN_TIMEOUT / 10.0 ); + epicsTimeGetCurrent ( & current ); + diff = epicsTimeDiffInSeconds ( & current, & begin ); + epicsMutexMustLock ( pClient->mutex ); + } + while ( ! pClient->shutdownConfirm && diff < LOG_SERVER_SHUTDOWN_TIMEOUT ); + epicsMutexUnlock ( pClient->mutex ); + + if ( ! pClient->shutdownConfirm ) { + fprintf ( stderr, "log client shutdown: timed out stopping" + " reconnect thread for \"%s\" after %.1f seconds - cleanup aborted\n", + pClient->name, LOG_SERVER_SHUTDOWN_TIMEOUT ); + return; + } + + logClientClose ( pClient ); + + epicsMutexDestroy ( pClient->mutex ); + + epicsEventDestroy ( pClient->stateChangeNotify ); + + free ( pClient ); +} + +/* + * logClientSend () + */ +void epicsShareAPI logClientSend ( logClientId id, const char * message ) +{ + logClient * pClient = ( logClient * ) id; + unsigned strSize; + + if ( ! pClient || ! message ) { + return; + } + + strSize = strlen ( message ); + + epicsMutexMustLock ( pClient->mutex ); + + while ( strSize ) { + unsigned msgBufBytesLeft = + sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex; + + if ( strSize > msgBufBytesLeft ) { + int status; + + if ( ! pClient->connected ) { + break; + } + + if ( msgBufBytesLeft > 0u ) { + memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], + message, msgBufBytesLeft ); + pClient->nextMsgIndex += msgBufBytesLeft; + strSize -= msgBufBytesLeft; + message += msgBufBytesLeft; + } + + status = send ( pClient->sock, pClient->msgBuf, + pClient->nextMsgIndex, 0 ); + if ( status > 0 ) { + unsigned nSent = (unsigned) status; + if ( nSent < pClient->nextMsgIndex ) { + unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent; + memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], + newNextMsgIndex ); + pClient->nextMsgIndex = newNextMsgIndex; + } + else { + pClient->nextMsgIndex = 0u; + } + } + else { + if ( ! pClient->shutdown ) { + char sockErrBuf[64]; + if ( status ) { + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + } + else { + strcpy ( sockErrBuf, "server initiated disconnect" ); + } + fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", + pClient->name, sockErrBuf ); + } + logClientClose ( pClient ); + break; + } + } + else { + memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], + message, strSize ); + pClient->nextMsgIndex += strSize; + break; + } + } + + epicsMutexUnlock (pClient->mutex); +} + +void epicsShareAPI logClientFlush ( logClientId id ) +{ + logClient * pClient = ( logClient * ) id; + + if ( ! pClient ) { + return; + } + + epicsMutexMustLock ( pClient->mutex ); + + while ( pClient->nextMsgIndex && pClient->connected ) { + int status = send ( pClient->sock, pClient->msgBuf, + pClient->nextMsgIndex, 0 ); + if ( status > 0 ) { + unsigned nSent = (unsigned) status; + if ( nSent < pClient->nextMsgIndex ) { + unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent; + memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], + newNextMsgIndex ); + pClient->nextMsgIndex = newNextMsgIndex; + } + else { + pClient->nextMsgIndex = 0u; + } + } + else { + if ( ! pClient->shutdown ) { + char sockErrBuf[64]; + if ( status ) { + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + } + else { + strcpy ( sockErrBuf, "server initiated disconnect" ); + } + fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", + pClient->name, sockErrBuf ); + } + logClientClose ( pClient ); + break; + } + } + epicsMutexUnlock ( pClient->mutex ); +} + +/* + * logClientMakeSock () + */ +static void logClientMakeSock (logClient *pClient) +{ + +# ifdef DEBUG + fprintf (stderr, "log client: creating socket..."); +# endif + + epicsMutexMustLock (pClient->mutex); + + /* + * allocate a socket + */ + pClient->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, 0 ); + if ( pClient->sock == INVALID_SOCKET ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "log client: no socket error %s\n", + sockErrBuf ); + } + + epicsMutexUnlock (pClient->mutex); + +# ifdef DEBUG + fprintf (stderr, "done\n"); +# endif + +} + +/* + * logClientConnect() + */ +static void logClientConnect (logClient *pClient) +{ + osiSockIoctl_t optval; + int errnoCpy; + int status; + + if ( pClient->sock == INVALID_SOCKET ) { + logClientMakeSock ( pClient ); + if ( pClient->sock == INVALID_SOCKET ) { + return; + } + } + + while ( 1 ) { + status = connect (pClient->sock, + (struct sockaddr *)&pClient->addr, sizeof(pClient->addr)); + if ( status >= 0 ) { + break; + } + else { + errnoCpy = SOCKERRNO; + if ( errnoCpy == SOCK_EINTR ) { + continue; + } + else if ( + errnoCpy==SOCK_EINPROGRESS || + errnoCpy==SOCK_EWOULDBLOCK ) { + return; + } + else if ( errnoCpy==SOCK_EALREADY ) { + break; + } + else { + if ( pClient->connFailStatus != errnoCpy && ! pClient->shutdown ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, + "log client: failed to connect to \"%s\" because %d=\"%s\"\n", + pClient->name, errnoCpy, sockErrBuf); + pClient->connFailStatus = errnoCpy; + } + logClientClose ( pClient ); + return; + } + } + } + + epicsMutexMustLock (pClient->mutex); + + pClient->connected = 1u; + pClient->connFailStatus = 0; + + /* + * discover that the connection has expired + * (after a long delay) + */ + optval = TRUE; + status = setsockopt (pClient->sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "log client: unable to enable keepalive option because \"%s\"\n", sockErrBuf); + } + + /* + * we don't need full-duplex (clients only write), so we shutdown + * the read end of our socket + */ + status = shutdown (pClient->sock, SHUT_RD); + if (status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "%s:%d shutdown(%d,SHUT_RD) error was \"%s\"\n", + __FILE__, __LINE__, pClient->sock, sockErrBuf); + /* not fatal (although it shouldn't happen) */ + } + + /* + * set how long we will wait for the TCP state machine + * to clean up when we issue a close(). This + * guarantees that messages are serialized when we + * switch connections. + */ + { + struct linger lingerval; + + lingerval.l_onoff = TRUE; + lingerval.l_linger = 60*5; + status = setsockopt (pClient->sock, SOL_SOCKET, SO_LINGER, (char *) &lingerval, sizeof(lingerval)); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "log client: unable to set linger options because \"%s\"\n", sockErrBuf); + } + } + + pClient->connectCount++; + + epicsMutexUnlock ( pClient->mutex ); + + epicsEventSignal ( pClient->stateChangeNotify ); + + fprintf ( stderr, "log client: connected to log server at \"%s\"\n", pClient->name ); +} + +/* + * logClientRestart () + */ +static void logClientRestart ( logClientId id ) +{ + logClient *pClient = (logClient *)id; + + /* SMP safe state inspection */ + epicsMutexMustLock ( pClient->mutex ); + while ( ! pClient->shutdown ) { + unsigned isConn; + + isConn = pClient->connected; + + epicsMutexUnlock ( pClient->mutex ); + + if ( isConn ) { + logClientFlush ( pClient ); + } + else { + logClientConnect ( pClient ); + } + + epicsThreadSleep ( LOG_RESTART_DELAY ); + + epicsMutexMustLock ( pClient->mutex ); + } + epicsMutexUnlock ( pClient->mutex ); + + pClient->shutdownConfirm = 1u; + epicsEventSignal ( pClient->stateChangeNotify ); +} + +/* + * logClientCreate() + */ +logClientId epicsShareAPI logClientCreate ( + struct in_addr server_addr, unsigned short server_port) +{ + epicsTimeStamp begin, current; + logClient *pClient; + double diff; + + pClient = calloc (1, sizeof (*pClient)); + if (pClient==NULL) { + return NULL; + } + + pClient->addr.sin_family = AF_INET; + pClient->addr.sin_addr = server_addr; + pClient->addr.sin_port = htons(server_port); + ipAddrToDottedIP (&pClient->addr, pClient->name, sizeof(pClient->name)); + + pClient->mutex = epicsMutexCreate (); + if ( ! pClient->mutex ) { + free ( pClient ); + return NULL; + } + + pClient->sock = INVALID_SOCKET; + pClient->connected = 0u; + pClient->connFailStatus = 0; + pClient->shutdown = 0; + pClient->shutdownConfirm = 0; + + epicsAtExit (logClientDestroy, (void*) pClient); + + pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty); + if ( ! pClient->stateChangeNotify ) { + epicsMutexDestroy ( pClient->mutex ); + free ( pClient ); + return NULL; + } + + pClient->restartThreadId = epicsThreadCreate ( + "logRestart", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + logClientRestart, pClient ); + if ( pClient->restartThreadId == NULL ) { + epicsMutexDestroy ( pClient->mutex ); + epicsEventDestroy ( pClient->stateChangeNotify ); + free (pClient); + fprintf(stderr, "log client: unable to start log client connection watch dog thread\n"); + return NULL; + } + + /* + * attempt to synchronize with circuit connect + */ + epicsTimeGetCurrent ( & begin ); + epicsMutexMustLock ( pClient->mutex ); + do { + epicsMutexUnlock ( pClient->mutex ); + epicsEventWaitWithTimeout ( + pClient->stateChangeNotify, + LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT / 10.0 ); + epicsTimeGetCurrent ( & current ); + diff = epicsTimeDiffInSeconds ( & current, & begin ); + epicsMutexMustLock ( pClient->mutex ); + } + while ( ! pClient->connected && diff < LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); + epicsMutexUnlock ( pClient->mutex ); + + if ( ! pClient->connected ) { + fprintf (stderr, "log client create: timed out synchronizing with circuit connect to \"%s\" after %.1f seconds\n", + pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); + } + + return (void *) pClient; +} + +/* + * logClientShow () + */ +void epicsShareAPI logClientShow (logClientId id, unsigned level) +{ + logClient *pClient = (logClient *) id; + + if ( pClient->connected ) { + printf ("log client: connected to log server at \"%s\"\n", pClient->name); + } + else { + printf ("log client: disconnected from log server at \"%s\"\n", pClient->name); + } + + if (level>1) { + printf ("log client: sock=%s, connect cycles = %u\n", + pClient->sock==INVALID_SOCKET?"INVALID":"OK", + pClient->connectCount); + } +} + diff --git a/src/libCom/logClient/logClient.h b/src/libCom/logClient/logClient.h new file mode 100644 index 000000000..8f1ec09d5 --- /dev/null +++ b/src/libCom/logClient/logClient.h @@ -0,0 +1,46 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* logClient.h,v 1.5.2.1 2003/07/08 00:08:06 jhill Exp */ +/* + * + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#ifndef INClogClienth +#define INClogClienth 1 +#include "shareLib.h" +#include "osiSock.h" /* for 'struct in_addr' */ + +/* include default log client interface for backward compatibility */ +#include "iocLog.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *logClientId; +epicsShareFunc logClientId epicsShareAPI logClientCreate ( + struct in_addr server_addr, unsigned short server_port); +epicsShareFunc void epicsShareAPI logClientSend (logClientId id, const char *message); +epicsShareFunc void epicsShareAPI logClientShow (logClientId id, unsigned level); +epicsShareFunc void epicsShareAPI logClientFlush (logClientId id); + +/* deprecated interface; retained for backward compatibility */ +/* note: implementations are in iocLog.c, not logClient.c */ +epicsShareFunc logClientId epicsShareAPI logClientInit (void); +epicsShareFunc void logClientSendMessage (logClientId id, const char *message); + +#ifdef __cplusplus +} +#endif + +#endif /*INClogClienth*/ diff --git a/src/libCom/macLib/macCore.c b/src/libCom/macLib/macCore.c new file mode 100644 index 000000000..4965671d9 --- /dev/null +++ b/src/libCom/macLib/macCore.c @@ -0,0 +1,941 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is 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 + * + * Implementation of core macro substitution library (macLib) + * + * The implementation is fairly unsophisticated and linked lists are + * used to store macro values. Typically there will will be only a + * small number of macros and performance won't be a problem. Special + * measures are taken to avoid unnecessary expansion of macros whose + * definitions reference other macros. Whenever a macro is created, + * modified or deleted, a "dirty" flag is set; this causes a full + * expansion of all macros the next time a macro value is read + * + * Original Author: William Lupton, W. M. Keck Observatory + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#include "dbmf.h" +#include "macLib.h" + + +/*** Local structure definitions ***/ + +/* + * Entry in linked list of macro definitions + */ +typedef struct mac_entry { + ELLNODE node; /* prev and next pointers */ + char *name; /* entry name */ + char *type; /* entry type */ + char *rawval; /* raw (unexpanded) value */ + char *value; /* expanded macro value */ + size_t length; /* length of value */ + int error; /* error expanding value? */ + int visited; /* ever been visited? */ + int special; /* special (internal) entry? */ + int level; /* scoping level */ +} MAC_ENTRY; + + +/*** Local function prototypes ***/ + +/* + * These static functions peform low-level operations on macro entries + */ +static MAC_ENTRY *first ( MAC_HANDLE *handle ); +static MAC_ENTRY *last ( MAC_HANDLE *handle ); +static MAC_ENTRY *next ( MAC_ENTRY *entry ); +static MAC_ENTRY *previous( MAC_ENTRY *entry ); + +static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special ); +static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special ); +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, const char *value ); +static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ); +static long expand( MAC_HANDLE *handle ); +static void trans ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char *term, const char **rawval, char **value, + char *valend ); +static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char **rawval, char **value, char *valend ); + +static void cpy2val( const char *src, char **value, char *valend ); +static char *Strdup( const char *string ); + + +/*** Constants ***/ + +/* + * Magic number for validating context. + */ +#define MAC_MAGIC 0xbadcafe /* ...sells sub-standard coffee? */ + +/* + * Flag bits + */ +#define FLAG_SUPPRESS_WARNINGS 0x1 +#define FLAG_USE_ENVIRONMENT 0x80 + + +/*** Library routines ***/ + +/* + * Create a new macro substitution context and return an opaque handle + * associated with the new context. Also optionally load an initial set + * of macro definitions + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macCreateHandle( + MAC_HANDLE **pHandle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + char *pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + MAC_HANDLE *handle; /* pointer to macro substitution context */ + + /* ensure NULL handle pointer is returned on error */ + *pHandle = NULL; + + /* allocate macro substitution context */ + handle = ( MAC_HANDLE * ) dbmfMalloc( sizeof( MAC_HANDLE ) ); + if ( handle == NULL ) { + errlogPrintf( "macCreateHandle: failed to allocate context\n" ); + return -1; + } + + /* initialize context */ + handle->magic = MAC_MAGIC; + handle->dirty = FALSE; + handle->level = 0; + handle->debug = 0; + handle->flags = 0; + ellInit( &handle->list ); + + /* use environment variables if so specified */ + if (pairs && pairs[0] && !strcmp(pairs[0],"") && pairs[1] && !strcmp(pairs[1],"environ") && !pairs[3]) { + handle->flags |= FLAG_USE_ENVIRONMENT; + } + else { + /* if supplied, load macro definitions */ + for ( ; pairs && pairs[0]; pairs += 2 ) { + if ( macPutValue( handle, pairs[0], pairs[1] ) < 0 ) { + dbmfFree( handle ); + return -1; + } + } + } + + /* set returned handle pointer */ + *pHandle = handle; + + return 0; +} + +/* + * Turn on/off suppression of printed warnings from macLib calls + * for the given handle + */ +void +epicsShareAPI macSuppressWarning( + MAC_HANDLE *handle, /* opaque handle */ + int suppress /* 0 means issue, 1 means suppress */ +) +{ + if ( handle && handle->magic == MAC_MAGIC ) { + handle->flags = (handle->flags & ~FLAG_SUPPRESS_WARNINGS) | + (suppress ? FLAG_SUPPRESS_WARNINGS : 0); + } +} + +/* + * Expand a string that may contain macro references and return the + * expanded string + * + * This is a very basic and powerful routine. It's basically a wrapper + * around the the translation "engine" trans() + */ +long /* strlen(dest), <0 if any macros are */ + /* undefined */ +epicsShareAPI macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + const char *src, /* source string */ + + char *dest, /* destination string */ + + long capacity ) /* capacity of destination buffer (dest) */ +{ + MAC_ENTRY entry; + const char *s; + char *d; + long length; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macExpandString: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString( %s, capacity = %ld )\n", src, capacity ); + + /* Check size */ + if (capacity <= 1) + return -1; + + /* expand raw values if necessary */ + if ( expand( handle ) < 0 ) + errlogPrintf( "macExpandString: failed to expand raw values\n" ); + + /* fill in necessary fields in fake macro entry structure */ + entry.name = (char *) src; + entry.type = "string"; + entry.error = FALSE; + + /* expand the string */ + s = src; + d = dest; + *d = '\0'; + trans( handle, &entry, 0, "", &s, &d, d + capacity - 1 ); + + /* return +/- #chars copied depending on successful expansion */ + length = d - dest; + length = ( entry.error ) ? -length : length; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macExpandString() -> %ld\n", length ); + + return length; +} + +/* + * Define the value of a macro. A NULL value deletes the macro if it + * already existed + */ +long /* strlen(value) */ +epicsShareAPI macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name */ + + const char *value ) /* macro value */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macPutValue: NULL or invalid handle\n" ); + return -1; + } + + if ( handle->debug & 1 ) + printf( "macPutValue( %s, %s )\n", name, value ? value : "NULL" ); + + /* handle NULL value case: if name was found, delete entry (may be + several entries at different scoping levels) */ + if ( value == NULL ) { + /* FIXME: shouldn't be able to delete entries from lower scopes */ + while ( ( entry = lookup( handle, name, FALSE ) ) != NULL ) + delete( handle, entry ); + return 0; + } + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* new entry must be created if macro doesn't exist or if it only + exists at a lower scoping level */ + if ( entry == NULL || entry->level < handle->level ) { + entry = create( handle, name, FALSE ); + if ( entry == NULL ) { + errlogPrintf( "macPutValue: failed to create macro %s = %s\n", + name, value ); + return -1; + } else { + entry->type = "macro"; + } + } + + /* copy raw value */ + if ( rawval( handle, entry, value ) == NULL ) { + errlogPrintf( "macPutValue: failed to copy macro %s = %s\n", + name, value ) ; + return -1; + } + + /* return length of value */ + return strlen( value ); +} + +/* + * Return the value of a macro + */ +long /* strlen(value), <0 if undefined */ +epicsShareAPI macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long capacity ) /* capacity of destination buffer (value) */ +{ + MAC_ENTRY *entry; /* pointer to this macro's entry structure */ + long length; /* number of characters returned */ + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macGetValue: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macGetValue( %s )\n", name ); + + /* look up macro name */ + entry = lookup( handle, name, FALSE ); + + /* if capacity <= 1 or VALUE == NULL just return -1 / 0 for undefined / + defined macro */ + if ( capacity <= 1 || value == NULL ) { + return ( entry == NULL ) ? -1 : 0; + } + + /* if not found, copy name to value and return minus #chars copied */ + if ( entry == NULL ) { + strncpy( value, name, capacity ); + return ( value[capacity-1] == '\0' ) ? - (long) strlen( name ) : -capacity; + } + + /* expand raw values if necessary; if fail (can only fail because of + memory allocation failure), return same as if not found */ + if ( expand( handle ) < 0 ) { + errlogPrintf( "macGetValue: failed to expand raw values\n" ); + strncpy( value, name, capacity ); + return ( value[capacity-1] == '\0' ) ? - (long) strlen( name ) : -capacity; + } + + /* copy value and return +/- #chars copied depending on successful + expansion */ + strncpy( value, entry->value, capacity ); + length = ( value[capacity-1] == '\0' ) ? entry->length : capacity; + + return ( entry->error ) ? -length : length; +} + +/* + * Free up all storage associated with and delete a macro substitution + * context + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macDeleteHandle( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macDeleteHandle: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macDeleteHandle()\n" ); + + /* delete all entries */ + for ( entry = first( handle ); entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* clear magic field and free context structure */ + handle->magic = 0; + dbmfFree( handle ); + + return 0; +} + +/* + * Mark the start of a new scoping level + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPushScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macPushScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPushScope()\n" ); + + /* increment scoping level */ + handle->level++; + + /* create new "special" entry of name "" */ + entry = create( handle, "", TRUE ); + if ( entry == NULL ) { + handle->level--; + errlogPrintf( "macPushScope: failed to push scope\n" ); + return -1; + } else { + entry->type = "scope marker"; + } + + return 0; +} + +/* + * Pop all macros defined since the last call to macPushScope() + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPopScope( + MAC_HANDLE *handle ) /* opaque handle */ +{ + MAC_ENTRY *entry, *nextEntry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macPopScope: NULL or invalid handle\n" ); + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macPopScope()\n" ); + + /* check scoping level isn't already zero */ + if ( handle->level == 0 ) { + errlogPrintf( "macPopScope: no scope to pop\n" ); + return -1; + } + + /* look up most recent scope entry */ + entry = lookup( handle, "", TRUE ); + if ( entry == NULL ) { + errlogPrintf( "macPopScope: no scope to pop\n" ); + return -1; + } + + /* delete scope entry and all macros defined since it */ + for ( ; entry != NULL; entry = nextEntry ) { + nextEntry = next( entry ); + delete( handle, entry ); + } + + /* decrement scoping level */ + handle->level--; + + return 0; +} + +/* + * Report macro details to standard output + */ +long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macReportMacros( + MAC_HANDLE *handle ) /* opaque handle */ +{ + const char *format = "%-1s %-16s %-16s %s\n"; + MAC_ENTRY *entry; + + /* check handle */ + if ( handle == NULL || handle->magic != MAC_MAGIC ) { + errlogPrintf( "macReportMacros: NULL or invalid handle\n" ); + return -1; + } + + /* expand raw values if necessary; report but ignore failure */ + if ( expand( handle ) < 0 ) + errlogPrintf( "macGetValue: failed to expand raw values\n" ); + + /* loop through macros, reporting names and values */ + printf( format, "e", "name", "rawval", "value" ); + printf( format, "-", "----", "------", "-----" ); + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + /* differentiate between "special" (scope marker) and ordinary + entries */ + if ( entry->special ) + printf( format, "s", "----", "------", "-----" ); + else + printf( format, entry->error ? "*" : " ", entry->name, + entry->rawval ? entry->rawval : "", + entry->value ? entry->value : ""); + } + + return 0; +} + +/******************** beginning of static functions ********************/ + +/* + * Return pointer to first macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *first( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellFirst( &handle->list ); +} + +/* + * Return pointer to last macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *last( MAC_HANDLE *handle ) +{ + return ( MAC_ENTRY * ) ellLast( &handle->list ); +} + +/* + * Return pointer to next macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *next( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellNext( ( ELLNODE * ) entry ); +} + +/* + * Return pointer to previous macro entry (could be preprocessor macro) + */ +static MAC_ENTRY *previous( MAC_ENTRY *entry ) +{ + return ( MAC_ENTRY * ) ellPrevious( ( ELLNODE * ) entry ); +} + +/* + * Create new macro entry (can assume it doesn't exist) + */ +static MAC_ENTRY *create( MAC_HANDLE *handle, const char *name, int special ) +{ + ELLLIST *list = &handle->list; + MAC_ENTRY *entry = ( MAC_ENTRY * ) dbmfMalloc( sizeof( MAC_ENTRY ) ); + + if ( entry != NULL ) { + entry->name = Strdup( name ); + if ( entry->name == NULL ) { + dbmfFree( entry ); + entry = NULL; + } + else { + entry->type = ""; + entry->rawval = NULL; + entry->value = NULL; + entry->length = 0; + entry->error = FALSE; + entry->visited = FALSE; + entry->special = special; + entry->level = handle->level; + + ellAdd( list, ( ELLNODE * ) entry ); + } + } + + return entry; +} + +/* + * Look up macro entry with matching "special" attribute by name + */ +static MAC_ENTRY *lookup( MAC_HANDLE *handle, const char *name, int special ) +{ + MAC_ENTRY *entry; + + if ( handle->debug & 2 ) + printf( "lookup-> level = %d, name = %s, special = %d\n", + handle->level, name, special ); + + /* search backwards so scoping works */ + for ( entry = last( handle ); entry != NULL; entry = previous( entry ) ) { + if ( entry->special != special ) + continue; + if ( strcmp( name, entry->name ) == 0 ) + break; + } + if ( (special == FALSE) && (entry == NULL) && + (handle->flags & FLAG_USE_ENVIRONMENT) ) { + char *value = getenv(name); + if (value) { + entry = create( handle, name, FALSE ); + if ( entry ) { + entry->type = "environment variable"; + if ( rawval( handle, entry, value ) == NULL ) + entry = NULL; + } + } + } + + if ( handle->debug & 2 ) + printf( "<-lookup level = %d, name = %s, result = %p\n", + handle->level, name, entry ); + + return entry; +} + +/* + * Copy raw value to macro entry + */ +static char *rawval( MAC_HANDLE *handle, MAC_ENTRY *entry, const char *value ) +{ + if ( entry->rawval != NULL ) + dbmfFree( entry->rawval ); + entry->rawval = Strdup( value ); + + handle->dirty = TRUE; + + return entry->rawval; +} + +/* + * Delete a macro entry; requires re-expansion of macro values since this + * macro may be referenced by another one + */ +static void delete( MAC_HANDLE *handle, MAC_ENTRY *entry ) +{ + ELLLIST *list = &handle->list; + + ellDelete( list, ( ELLNODE * ) entry ); + + dbmfFree( entry->name ); + if ( entry->rawval != NULL ) + dbmfFree( entry->rawval ); + if ( entry->value != NULL ) + free( entry->value ); + dbmfFree( entry ); + + handle->dirty = TRUE; +} + +/* + * Expand macro definitions (expensive but done very infrequently) + */ +static long expand( MAC_HANDLE *handle ) +{ + MAC_ENTRY *entry; + const char *rawval; + char *value; + + if ( !handle->dirty ) + return 0; + + for ( entry = first( handle ); entry != NULL; entry = next( entry ) ) { + + if ( handle->debug & 2 ) + printf( "\nexpand %s = %s\n", entry->name, + entry->rawval ? entry->rawval : "" ); + + if ( entry->value == NULL ) { + if ( ( entry->value = malloc( MAC_SIZE + 1 ) ) == NULL ) { + return -1; + } + } + + /* start at level 1 so quotes and escapes will be removed from + expanded value */ + rawval = entry->rawval; + value = entry->value; + *value = '\0'; + entry->error = FALSE; + trans( handle, entry, 1, "", &rawval, &value, entry->value + MAC_SIZE ); + entry->length = value - entry->value; + entry->value[MAC_SIZE] = '\0'; + } + + handle->dirty = FALSE; + + return 0; +} + +/* + * Translate raw macro value (recursive). This is by far the most complicated + * of the macro routines and calls itself recursively both to translate any + * macros referenced in the name and to translate the resulting name + */ +static void trans( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char *term, const char **rawval, char **value, + char *valend ) +{ + char quote; + const char *r; + char *v; + int discard; + int macRef; + + /* return immediately if raw value is NULL */ + if ( *rawval == NULL ) return; + + /* discard quotes and escapes if level is > 0 (i.e. if these aren't + the user's quotes and escapes) */ + discard = ( level > 0 ); + + /* debug output */ + if ( handle->debug & 2 ) + printf( "trans-> entry = %p, level = %d, capacity = %u, discard = %s, " + "rawval = %s\n", entry, level, (unsigned int)(valend - *value), discard ? "T" : "F", *rawval ); + + /* initially not in quotes */ + quote = 0; + + /* scan characters until hit terminator or end of string */ + for ( r = *rawval, v = *value; strchr( term, *r ) == NULL; r++ ) { + + /* handle quoted characters (quotes are discarded if in name) */ + if ( quote ) { + if ( *r == quote ) { + quote = 0; + if ( discard ) continue; + } + } + else if ( *r == '"' || *r == '\'' ) { + quote = *r; + if ( discard ) continue; + } + + /* macro reference if '$' followed by '(' or '{' */ + macRef = ( *r == '$' && + *( r + 1 ) != '\0' && + strchr( "({", *( r + 1 ) ) != NULL ); + + /* macros are not expanded in single quotes */ + if ( macRef && quote != '\'' ) { + /* Handle macro reference */ + refer ( handle, entry, level, &r, &v, valend ); + } + + else { + /* handle escaped characters (escape is discarded if in name) */ + if ( *r == '\\' && *( r + 1 ) != '\0' ) { + if ( v < valend && !discard ) *v++ = '\\'; + if ( v < valend ) *v++ = *++r; + } + + /* copy character to output */ + else { + if ( v < valend ) *v++ = *r; + } + + /* ensure string remains properly terminated */ + if ( v <= valend ) *v = '\0'; + } + } + + /* debug output */ + if ( handle->debug & 2 ) + printf( "<-trans level = %d, length = %4u, value = %s\n", + level, (unsigned int)(v - *value), *value ); + + /* update pointers to next characters to scan in raw value and to fill + in in output value (if at end of input, step back so terminator is + still there to be seen) */ + *rawval = ( *r == '\0' ) ? r - 1 : r; + *value = v; + + return; +} + +/* + * Expand a macro reference, handling default values and defining scoped + * macros as encountered. This code used to part of trans(), but was + * pulled out for ease of understanding. + */ +static void refer ( MAC_HANDLE *handle, MAC_ENTRY *entry, int level, + const char **rawval, char **value, char *valend ) +{ + const char *r = *rawval; + char *v = *value; + char refname[MAC_SIZE + 1] = {'\0'}; + char *rn = refname; + MAC_ENTRY *refentry; + const char *defval = NULL; + const char *macEnd; + const char *errval = NULL; + int pop = FALSE; + + /* debug output */ + if ( handle->debug & 2 ) + printf( "refer-> entry = %p, level = %d, capacity = %u, rawval = %s\n", + entry, level, (unsigned int)(valend - *value), *rawval ); + + /* step over '$(' or '${' */ + r++; + macEnd = ( *r == '(' ) ? "=,)" : "=,}"; + r++; + + /* translate name (may contain macro references); truncated + quietly if too long but always guaranteed zero-terminated */ + trans( handle, entry, level + 1, macEnd, &r, &rn, rn + MAC_SIZE ); + refname[MAC_SIZE] = '\0'; + + /* Is there a default value? */ + if ( *r == '=' ) { + MAC_ENTRY dflt; + int flags = handle->flags; + handle->flags |= FLAG_SUPPRESS_WARNINGS; + + /* store its location in case we need it */ + defval = ++r; + + /* Find the end, discarding its value */ + dflt.name = refname; + dflt.type = "default value"; + dflt.error = FALSE; + trans( handle, &dflt, level + 1, macEnd+1, &r, &v, v); + + handle->flags = flags; + } + + /* extract and set values for any scoped macros */ + if ( *r == ',' ) { + MAC_ENTRY subs; + int flags = handle->flags; + handle->flags |= FLAG_SUPPRESS_WARNINGS; + + subs.type = "scoped macro"; + subs.error = FALSE; + + macPushScope( handle ); + pop = TRUE; + + while ( *r == ',' ) { + char subname[MAC_SIZE + 1] = {'\0'}; + char subval[MAC_SIZE + 1] = {'\0'}; + char *sn = subname; + char *sv = subval; + + /* translate the macro name */ + ++r; + subs.name = refname; + + trans( handle, &subs, level + 1, macEnd, &r, &sn, sn + MAC_SIZE ); + subname[MAC_SIZE] = '\0'; + + /* If it has a value, translate that and assign it */ + if ( *r == '=' ) { + ++r; + subs.name = subname; + + trans( handle, &subs, level + 1, macEnd+1, &r, &sv, + sv + MAC_SIZE); + subval[MAC_SIZE] = '\0'; + + macPutValue( handle, subname, subval ); + handle->dirty = TRUE; /* re-expand with new macro values */ + } + } + + handle->flags = flags; + } + + /* Now we can look up the translated name */ + refentry = lookup( handle, refname, FALSE ); + + if ( refentry ) { + if ( !refentry->visited ) { + /* reference is good, use it */ + if ( !handle->dirty ) { + /* copy the already-expanded value, and its error status! */ + cpy2val( refentry->value, &v, valend ); + entry->error = refentry->error; + } else { + /* translate raw value */ + const char *rv = refentry->rawval; + refentry->visited = TRUE; + trans( handle, entry, level + 1, "", &rv, &v, valend ); + refentry->visited = FALSE; + } + goto cleanup; + } + /* reference is recursive */ + entry->error = TRUE; + errval = ",recursive)"; + if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) { + errlogPrintf( "macLib: %s %s is recursive (expanding %s %s)\n", + entry->type, entry->name, + refentry->type, refentry->name ); + } + } else { + /* no macro found by this name */ + if ( defval ) { + /* there was a default value, translate that instead */ + trans( handle, entry, level + 1, macEnd+1, &defval, &v, valend ); + goto cleanup; + } + entry->error = TRUE; + errval = ",undefined)"; + if ( (handle->flags & FLAG_SUPPRESS_WARNINGS) == 0 ) { + errlogPrintf( "macLib: macro %s is undefined (expanding %s %s)\n", + refname, entry->type, entry->name ); + } + } + + /* Bad reference, insert $(name,errval) */ + if ( v < valend ) *v++ = '$'; + if ( v < valend ) *v++ = '('; + cpy2val( refname, &v, valend ); + cpy2val( errval, &v, valend ); + +cleanup: + if (pop) { + macPopScope( handle ); + } + + /* debug output */ + if ( handle->debug & 2 ) + printf( "<-refer level = %d, length = %4u, value = %s\n", + level, (unsigned int)(v - *value), *value ); + + *rawval = r; + *value = v; + return; +} + +/* + * Copy a string, honoring the 'end of destination string' pointer + * Returns with **value pointing to the '\0' terminator + */ +static void cpy2val(const char *src, char **value, char *valend) +{ + char *v = *value; + while ((v < valend) && (*v = *src++)) { v++; } + *v = '\0'; + *value = v; +} + +/* + * strdup() implementation which uses our own memory allocator + */ +static char *Strdup(const char *string ) +{ + char *copy = dbmfMalloc( strlen( string ) + 1 ); + + if ( copy != NULL ) + strcpy( copy, string ); + + return copy; +} + diff --git a/src/libCom/macLib/macEnv.c b/src/libCom/macLib/macEnv.c new file mode 100644 index 000000000..caa60fe70 --- /dev/null +++ b/src/libCom/macLib/macEnv.c @@ -0,0 +1,57 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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 + * + * Macro expansion of environment variables + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsString.h" +#include "macLib.h" + +char * epicsShareAPI +macEnvExpand(const char *str) +{ + MAC_HANDLE *handle; + static char *pairs[] = { "", "environ", NULL, NULL }; + long destCapacity = 128; + char *dest = NULL; + int n; + + if (macCreateHandle(&handle, pairs)) + cantProceed("macEnvExpand: macCreateHandle failed."); + + do { + destCapacity *= 2; + /* + * Use free/malloc rather than realloc since there's no need to + * keep the original contents. + */ + free(dest); + dest = mallocMustSucceed(destCapacity, "macEnvExpand"); + n = macExpandString(handle, str, dest, destCapacity); + } while (n >= (destCapacity - 1)); + + if (n < 0) { + free(dest); + dest = NULL; + } else { + size_t unused = destCapacity - ++n; + + if (unused >= 20) + dest = realloc(dest, n); + } + + if (macDeleteHandle(handle)) + cantProceed("macEnvExpand: macDeleteHandle failed."); + return dest; +} diff --git a/src/libCom/macLib/macLib.h b/src/libCom/macLib/macLib.h new file mode 100644 index 000000000..b4e5af030 --- /dev/null +++ b/src/libCom/macLib/macLib.h @@ -0,0 +1,158 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is 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 + * + * Definitions for macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +#ifndef INCmacLibH +#define INCmacLibH + +/* + * EPICS include files needed by this file + */ +#include "ellLib.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Maximum size of macro name or value string (simpler to make fixed) + */ +#define MAC_SIZE 256 + +/* + * Macro substitution context. One of these contexts is allocated each time + * macCreateHandle() is called + */ +typedef struct { + long magic; /* magic number (used for authentication) */ + int dirty; /* values need expanding from raw values? */ + int level; /* scoping level */ + int debug; /* debugging level */ + ELLLIST list; /* macro name / value list */ + int flags; /* operating mode flags */ +} MAC_HANDLE; + +/* + * Function prototypes (core library) + */ +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macCreateHandle( + MAC_HANDLE **handle, /* address of variable to receive pointer */ + /* to new macro substitution context */ + + char *pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +epicsShareFunc void +epicsShareAPI macSuppressWarning( + MAC_HANDLE *handle, /* opaque handle */ + + int falseTrue /*0 means issue, 1 means suppress*/ +); + +epicsShareFunc long /* strlen(dest), <0 if any macros are */ + /* undefined */ +epicsShareAPI macExpandString( + MAC_HANDLE *handle, /* opaque handle */ + + const char *src, /* source string */ + + char *dest, /* destination string */ + + long capacity /* capacity of destination buffer (dest) */ +); + + +epicsShareFunc long /* strlen(value) */ +epicsShareAPI macPutValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name */ + + const char *value /* macro value */ +); + +epicsShareFunc long /* strlen(value), <0 if undefined */ +epicsShareAPI macGetValue( + MAC_HANDLE *handle, /* opaque handle */ + + const char *name, /* macro name or reference */ + + char *value, /* string to receive macro value or name */ + /* argument if macro is undefined */ + + long capacity /* capacity of destination buffer (value) */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macDeleteHandle( + MAC_HANDLE *handle /* opaque handle */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPushScope( + MAC_HANDLE *handle /* opaque handle */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macPopScope( + MAC_HANDLE *handle /* opaque handle */ +); + +epicsShareFunc long /* 0 = OK; <0 = ERROR */ +epicsShareAPI macReportMacros( + MAC_HANDLE *handle /* opaque handle */ +); + +/* + * Function prototypes (utility library) + */ +epicsShareFunc long /* #defns encountered; <0 = ERROR */ +epicsShareAPI macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + const char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +); + +epicsShareFunc long /* #macros defined; <0 = ERROR */ +epicsShareAPI macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +); + +epicsShareFunc char * /* expanded string; NULL if any undefined macros */ +epicsShareAPI macEnvExpand( + const char *str /* string to be expanded */ +); + +#ifdef __cplusplus +} +#endif + +#endif /*INCmacLibH*/ diff --git a/src/libCom/macLib/macLibNOTES b/src/libCom/macLib/macLibNOTES new file mode 100644 index 000000000..457cb9488 --- /dev/null +++ b/src/libCom/macLib/macLibNOTES @@ -0,0 +1,160 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Test input file for macTest filter, doubling as notes on usage of the +# macro library. Some special strings at start of line are supported: +# +# 1. '#' indicates a comment and is ignored +# +# 2. '%set' is followed by "a=b, c=d" style macro definitions which are +# passed through macParseDefs() and macInstallMacros() +# +# 3. '%push' pushes a scoping level by calling macPushScope() +# +# 4. '%pop' pops a scoping level by calling macPopScope() +# +# 5. '%report' reports macro definitions by calling macReportMacros() +# +# 6. all other lines are expanded by callins macExpandString() +# +introduction +------------ + +See the README file for the library specification and see the header +comments of macCore.c (the core library) for notes on what has been +implemented. + +This file contains tutorial information and examples. It's the +best documentation that there is. + + +simple examples +--------------- + +To define the a, b, c and d macros to be 1, 2, 3 and 4 (note optional +white space is ignored around '=' and ',' characters): + +%set a=1, b=2, c = 3 , d = 4 + +These macros can be dereferenced using '$(xxx)' or '${xxx}' notation: +a = $(a), b = $(b), c = ${c}, d = ${d}. + +Macro values can reference other macros in an arbitrarily complex way. +The only current restrictions are that a macro name or value cannot +exceed 256 characters in length. This restriction could fairly easily +be lifted. + +Here's an example: + +%set x = ${$(y$(z))} + +If this is expanded now: $(x), it won't work because the other macros +aren't defined yet. So: + +%set cash=lucre, ywork=cash, z=work + +Now expansion yields "$(x)" (work -> ywork -> cash -> lucre). + +One can inadvertently set up circular references. For example: + +%set mac1=$(mac2), mac2=$(mac3), mac3=$(mac1) + +An attempt to dereference mac1 gives $(mac1). When a macro expansion +fails, the translation that failed is replaced with the text that could +not be expanded. + +You can get a report of current macro definitions by doing +%report + +The '*' character in the first column indicates a problem expanding that +macro. We'll get rid of these problem macros: +%set mac1, mac2, mac3 + +You can also push a new scoping level by doing +%push + +and pop back to it by doing +%pop + +For example: +%set level = 0 +%push +%set level = 1 +%push +%set level = 2 +%push +%set level = 3 +%report +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +Level is $(level) +%pop +(That last error was deliberate) + + +quote and escape handling +------------------------- + +Both single and double quotes are supported, as are escapes. Some of the +implications are quite subtle and some of the design choices made may not +meet with universal approval. The basic idea is that a string in which +macro references have been expanded should look like the source string +apart from the where macros have been expanded. This implies that quote +and escape characters should not be removed from the string. + +Single and double quotes are different in that (as in most shells), +macros are substituted within double quotes but not within single +quotes. Back quotes are not special characters. Missing quotes at the +end of a string are automatically and quietly supplied. + +We've already seen some examples but what happens here: $(x)? +Should not that have been expanded? Why wasn't it? The answer is given +later on. As a clue, this is: $(x). This isn't: $(x)! + +Characters may be escaped by preceding them with a \ (back slash). +Escapes work even within quotes, which is more like C than most shells. +Thus, '\'' works, and it doesn't with the C shell. + +Quotes and escapes can also be used in "a=b, c=d" style assignments +but they are not part of macro names. For example: + +%set \= = equal, \' = quote + +defines macros called "=" and "'", not "\=" and "\'". To reference +these macros, "$(=)" ('$(=)') works because "=" is not special in +this context. However the quote must be escaped because quotes are +always special, as in "$(\')" ('$(\')'). + + +miscellaneous notes +------------------- + +In the "a=b, c=d" strings that are parsed by macParseDefns(), a macro +can be set to an empty value by putting nothing after the equals +sign, as in + +%set empty = +resulting in empty = "$(empty)". Omitting the equal sign gives the +macro a NULL rather than empty value (with the result that the macro +is deleted since this is how macPutValue() interprets a NULL value), so + +%set empty +deletes the empty macro, as can be seen when we try to de-reference +it: $(empty). + +The only special characters in these "a=b, c=d" strings are "=", "," +and white space surrounding these characters (plus quotes and escapes +of course). This means that + +%set fred 2 +actually sets a macro called "fred 2" to a NULL value (i.e. it deletes +it if it existed). This is probably wrong and the space should be +taken as an implicit '='. However, to do this and still to support +ignored white space around '=' and ',' characters is a bigger change +than I am prepared to make at present. + +What was the problem expanding "$(x)" before? It was single quotes +from words like "wasn't"! Is this a problem or a feature? diff --git a/src/libCom/macLib/macLibREADME b/src/libCom/macLib/macLibREADME new file mode 100644 index 000000000..ec521d8a6 --- /dev/null +++ b/src/libCom/macLib/macLibREADME @@ -0,0 +1,170 @@ +1. Macro substitution library +----------------------------- + +This library could be used directly by applications which need to +support macro substitution. It will be implemented on all platforms. + +1.1 Core library +---------------- + +The core library provides a minimal set of basic operations. Some +utility routines, described later, use core routines to provide a more +convenient interface for some purposes. + +a) long macCreateHandle( MAC_HANDLE **handle, char *pairs[] ); + void macSuppressWarning(MAC_HANDLE *handle,int falseTrue); + + Creates a new macro substitution context and returns an opaque handle + to that context. An application can, if it desires, have several + active contexts, although most will not. + + If desired, an initial set of macro definitions may be loaded + ("pairs" is set to NULL to avoid this). The definitions are in the + "standard" pairs format, as described under macParseDefns(). + + Note that MAC_HANDLE is a typedef for the context structure. The + opaque handle is of type "MAC_HANDLE *", which is a pointer to the + context structure. The memory for this context is allocated by this + routine. + + macSuppressWarning can be called to suppress the marning message + when macExpandString cant expand a macro. A non zero value will + suppress the messages. + + +c) long macGetValue ( MAC_HANDLE *handle, char *name, + char *value, long maxlen ); + + Returns up to maxlen characters of the value of macro "name". "value" + will be zero-terminated if the length of the value is less than + maxlen. The function value will be the number of characters copied to + "value" (see below for behavior if the macro is undefined). If maxlen + is zero, no characters will be copied to "value" (which can be NULL) + and the call can be used to check whether the macro is defined. Note + that truncation of the value is therefore not reported. Is this a + problem? + + If the macro is undefined, the macro reference will be returned in + the value string (if permitted by maxlen) and the function value will + be _minus_ the number of characters copied. Note that treatment of + maxlen is intended to be consistent with what people are used to with + strncpy(). + + If the value contains macro references then the references will be + expanded recursively. This expansion will detect a direct or indirect + self reference. + + Macro references begin with a "$" immediately followed by either a + "(" or a "{" character. The macro name comes next, and may optionally + be succeeded by an "=" and a default value, which will be returned if + the named macro is undefined at the moment of expansion. The reference + is terminated by the matching ")" or "}" character. + +d) long macPutValue( MAC_HANDLE *handle, char *name, char *value ); + + Sets the value of the macro "name". If "value" is NULL, it undefines + all instances of "name" at all scoping levels (it's not an error if + "name" is not defined in this case). Macros referenced by "value" + need not be defined at this point. + + The function returns the length of the value string. + +e) long macDeleteHandle( MAC_HANDLE *handle ); + + Marks a handle invalid, and frees all storage associated with it. + + Note that this does not free any strings into which macro values have + been returned. Macro values are always returned into strings which + were pre-allocated by the caller. + +f) long macPushScope( MAC_HANDLE *handle ); + + Marks the start of a new scoping level such that all definitions made + up until the next macPopScope() call will be lost on macPopScope() + and those current at macPushScope() will be re-instated. + +g) long macPopScope( MAC_HANDLE *handle ); + + See macPushScope. + +h) Error handling + + These routines conform to 0 (=OK) for success, -1 (=ERROR) for + failure, and small positive values for extra info. I contravened this + for macGetValue() and macExpandString() because I felt that it was + worth returning information both on success / failure and on value + length. + + Errors and warnings are reported using errlogPrintf(). + + + +1.2 Utility library +------------------- + +These are convenience functions. If other useful functions emerge during +implementation, the list may grow. + +a) macParseDefns( MAC_HANDLE *handle, char *defns, char **pairs[] ); + + This takes a set of macro definitions in "a=xxx,b=yyy" format and + converts them into an array of pointers to character strings which + are, in order, "first name", "first value", "second name", "second + value" etc. The array is terminated with two NULL pointers and all + storage is allocated contiguously so that it can be freed with a + single call to free(). + + This routine is independent of any handle and provides a generally + useful service which may be used elsewhere. Any macro references in + values are not expanded at this point since the referenced macros may + be defined or redefined before the macro actually has to be + translated. + + Shell-style escapes and quotes are supported, as are things like + "A=B,B=$(C$(A)),CA=CA,CB=CB" (sets B to "CB"). White space is + significant within values but ignored elsewhere (i.e. surrounding "=" + and "," characters). + + Probably noone will ever want to, but the special meanings of "$", + "{", "}", "(", ")", "=" and "," can all be changed via macPutXxxx() + calls. This routine does not have a handle argument, so they must be + changed globally for it to use the new definitions. Should it have a + handle argument? This makes it more of a pain to use but guarantees + that there will be no conflicts. I think it should really. + + The function value is the number of definitions encountered, or -1 if + the supplied string is invalid. + +b) long macInstallMacros( MAC_HANDLE *handle, char *pairs[] ); + + This takes an array of pairs as defined above and installs them as + definitions by calling macPutValue(). The pairs array is terminated + by a NULL pointer. + + The function value is the number of macros defined. + +c) long macExpandString( MAC_HANDLE *handle, char *src, + char *dest, long maxlen ); + + This operates on a string which may contain macro references. It + parses the string looking for such references and passes them to + macGetValue() for translation. It returns the expanded string in the + supplied argument. + + The function value is similar to that of macGetValue(). Its absolute + value is the number of characters copied. If negative, at least one + undefined macro has been left unexpanded. + +d) char *macEnvExpand( char *src ); + + This operates on a string which may contain macros defined by + environment variables. It parses the string looking for such + references and passes them to macGetValue() for translation. It uses + malloc() to allocate space for the expanded string and returns a + pointer to this null-terminated string. It returns NULL if the source + string contains any undefined references. + +e) long macReportMacros( MAC_HANDLE *handle ); + + This reports details of current definitions to standard output, and is + intended purely for debugging purposes. diff --git a/src/libCom/macLib/macUtil.c b/src/libCom/macLib/macUtil.c new file mode 100644 index 000000000..904d0f889 --- /dev/null +++ b/src/libCom/macLib/macUtil.c @@ -0,0 +1,286 @@ +/*************************************************************************\ +* 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 + * + * Implementation of utility macro substitution library (macLib) + * + * William Lupton, W. M. Keck Observatory + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#include "macLib.h" + +/* + * Parse macros definitions in "a=xxx,b=yyy" format and convert them to + * a contiguously allocated array pointers to names and values, and the + * name and value strings, terminated with two NULL pointers. Quotes + * and escapes are honored but only removed from macro names (not + * values) + */ +long /* #defns encountered; <0 = ERROR */ +epicsShareAPI macParseDefns( + MAC_HANDLE *handle, /* opaque handle; can be NULL if default */ + /* special characters are to be used */ + + const char *defns, /* macro definitions in "a=xxx,b=yyy" */ + /* format */ + + char **pairs[] ) /* address of variable to receive pointer */ + /* to NULL-terminated array of {name, */ + /* value} pair strings; all storage is */ + /* allocated contiguously */ +{ + static const size_t altNumMax = 4; + size_t numMax; + int i; + int num; + int quote; + int escape; + size_t nbytes; + const unsigned char **ptr; + const unsigned char **end; + int *del; + char *memCp, **memCpp; + const unsigned char *c; + char *s, *d, **p; + enum { preName, inName, preValue, inValue } state; + + /* debug output */ + if ( handle && (handle->debug & 1) ) + printf( "macParseDefns( %s )\n", defns ); + + /* allocate temporary pointer arrays; in worst case they need to have + as many entries as the length of the defns string */ + numMax = strlen( defns ); + if ( numMax < altNumMax ) + numMax = altNumMax; + ptr = (const unsigned char **) calloc( numMax, sizeof( char * ) ); + end = (const unsigned char **) calloc( numMax, sizeof( char * ) ); + del = (int *) calloc( numMax, sizeof( int ) ); + if ( ptr == NULL || end == NULL || del == NULL ) goto error; + + /* go through definitions, noting pointers to starts and ends of macro + names and values; honor quotes and escapes; ignore white space + around assignment and separator characters */ + num = 0; + del[0] = FALSE; + quote = 0; + state = preName; + for ( c = (const unsigned char *) defns; *c != '\0'; c++ ) { + + /* handle quotes */ + if ( quote ) + quote = ( *c == quote ) ? 0 : quote; + else if ( *c == '\'' || *c == '"' ) + quote = *c; + + /* handle escapes (pointer incremented below) */ + escape = ( *c == '\\' && *( c + 1 ) != '\0' ); + + switch ( state ) { + case preName: + if ( !quote && !escape && ( isspace( (int) *c ) || *c == ',' ) ) break; + ptr[num] = c; + state = inName; + /* fall through (may be empty name) */ + + case inName: + if ( quote || escape || ( *c != '=' && *c != ',' ) ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preValue; + if ( *c != ',' ) break; + del[num] = TRUE; + /* fall through (','; will delete) */ + + case preValue: + if ( !quote && !escape && isspace( (int) *c ) ) break; + ptr[num] = c; + state = inValue; + /* fall through (may be empty value) */ + + case inValue: + if ( quote || escape || *c != ',' ) break; + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + state = preName; + break; + } + + /* if this was escape, increment pointer now (couldn't do + before because could have ignored escape at start of name + or value) */ + if ( escape ) c++; + } + + /* tidy up from state at end of string */ + switch ( state ) { + case preName: + break; + case inName: + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = TRUE; + case preValue: + ptr[num] = c; + case inValue: + end[num] = c; + while ( end[num] > ptr[num] && isspace( (int) *( end[num] - 1 ) ) ) + end[num]--; + num++; + del[num] = FALSE; + } + + /* debug output */ + if ( handle != NULL && handle->debug & 4 ) + for ( i = 0; i < num; i += 2 ) + printf( "[%ld] %.*s = [%ld] %.*s (%s) (%s)\n", + (long) (end[i+0] - ptr[i+0]), (int) (end[i+0] - ptr[i+0]), ptr[i+0], + (long) (end[i+1] - ptr[i+1]), (int) (end[i+1] - ptr[i+1]), ptr[i+1], + del[i+0] ? "del" : "nodel", + del[i+1] ? "del" : "nodel" ); + + /* calculate how much memory to allocate: pointers followed by + strings */ + nbytes = ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) + nbytes += end[i] - ptr[i] + 1; + + /* allocate memory and set returned pairs pointer */ + memCp = malloc( nbytes ); + if ( memCp == NULL ) goto error; + memCpp = ( char ** ) memCp; + *pairs = memCpp; + + /* copy pointers and strings (memCpp accesses the pointer section + and memCp accesses the string section) */ + memCp += ( num + 2 ) * sizeof( char * ); + for ( i = 0; i < num; i++ ) { + + /* if no '=' followed the name, macro will be deleted */ + if ( del[i] ) + *memCpp++ = NULL; + else + *memCpp++ = memCp; + + /* copy value regardless of the above */ + strncpy( memCp, (const char *) ptr[i], end[i] - ptr[i] ); + memCp += end[i] - ptr[i]; + *memCp++ = '\0'; + } + + /* add two NULL pointers */ + *memCpp++ = NULL; + *memCpp++ = NULL; + + /* remove quotes and escapes from names in place (unlike values, they + will not be re-parsed) */ + for ( p = *pairs; *p != NULL; p += 2 ) { + quote = 0; + for ( s = d = *p; *s != '\0'; s++ ) { + + /* quotes are not copied */ + if ( quote ) { + if ( *s == quote ) { + quote = 0; + continue; + } + } + else if ( *s == '\'' || *s == '"' ) { + quote = *s; + continue; + } + + /* escapes are not copied but next character is */ + if ( *s == '\\' && *( s + 1 ) != '\0' ) + s++; + + /* others are copied */ + *d++ = *s; + } + + /* need to terminate destination */ + *d++ = '\0'; + } + + /* free workspace */ + free( ptr ); + free( end ); + free( ( char * ) del ); + + /* debug output */ + if ( handle != NULL && handle->debug & 1 ) + printf( "macParseDefns() -> %d\n", num / 2 ); + + /* success exit; return number of definitions */ + return num / 2; + + /* error exit */ +error: + errlogPrintf( "macParseDefns: failed to allocate memory\n" ); + if ( ptr != NULL ) free( ptr ); + if ( end != NULL ) free( end ); + if ( del != NULL ) free( ( char * ) del ); + *pairs = NULL; + return -1; +} + +/* + * Install an array of name / value pairs as macro definitions. The + * array should have an even number of elements followed by at least + * one (preferably two) NULL pointers + */ +long /* #macros defined; <0 = ERROR */ +epicsShareAPI macInstallMacros( + MAC_HANDLE *handle, /* opaque handle */ + + char *pairs[] ) /* pointer to NULL-terminated array of */ + /* {name,value} pair strings; a NULL */ + /* value implies undefined; a NULL */ + /* argument implies no macros */ +{ + int n; + char **p; + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros( %s, %s, ... )\n", + pairs && pairs[0] ? pairs[0] : "NULL", + pairs && pairs[1] ? pairs[1] : "NULL" ); + + /* go through array defining macros */ + for ( n = 0, p = pairs; p != NULL && p[0] != NULL; n++, p += 2 ) { + if ( macPutValue( handle, p[0], p[1] ) < 0 ) + return -1; + } + + /* debug output */ + if ( handle->debug & 1 ) + printf( "macInstallMacros() -> %d\n", n ); + + /* return number of macros defined */ + return n; +} + diff --git a/src/libCom/misc/aToIPAddr.c b/src/libCom/misc/aToIPAddr.c new file mode 100644 index 000000000..c5d53bc30 --- /dev/null +++ b/src/libCom/misc/aToIPAddr.c @@ -0,0 +1,136 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * rational replacement for inet_addr() + * + * author: Jeff Hill + */ +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" + +#ifndef NELEMENTS +#define NELEMENTS(A) (sizeof(A)/sizeof(A[0])) +#endif /*NELEMENTS*/ + +/* + * addrArrayToUL () + */ +static int addrArrayToUL (const unsigned short *pAddr, unsigned nElements, struct in_addr *pIpAddr) +{ + unsigned i; + unsigned long addr = 0ul; + + for ( i=0u; i < nElements; i++ ) { + if ( pAddr[i] > 0xff ) { + return -1; + } + addr <<= 8; + addr |= pAddr[i]; + } + pIpAddr->s_addr = htonl ( addr ); + + return 0; +} + +/* + * initIPAddr() + * !! ipAddr should be passed in in network byte order !! + * !! port is passed in in host byte order !! + */ +static int initIPAddr (struct in_addr ipAddr, unsigned short port, struct sockaddr_in *pIP) +{ + memset (pIP, '\0', sizeof(*pIP)); + pIP->sin_family = AF_INET; + pIP->sin_port = htons(port); + pIP->sin_addr = ipAddr; + return 0; +} + +/* + * rational replacement for inet_addr() + * which allows the limited broadcast address + * 255.255.255.255, allows the user + * to specify a port number, and allows also a + * named host to be specified. + * + * Sets the port number to "defaultPort" only if + * "pAddrString" does not contain an address of the form + * "n.n.n.n:p" + */ +epicsShareFunc int epicsShareAPI + aToIPAddr(const char *pAddrString, unsigned short defaultPort, struct sockaddr_in *pIP) +{ + int status; + unsigned short addr[4]; + unsigned long rawAddr; + char hostName[512]; /* !! change n elements here requires change in format below !! */ + unsigned short port; + struct in_addr ina; + + /* + * dotted ip addresses + */ + status = sscanf (pAddrString, " %hu.%hu.%hu.%hu:%hu", + addr, addr+1u, addr+2u, addr+3u, &port); + if (status>0) { + if (status>=4) { + if ( addrArrayToUL ( addr, NELEMENTS ( addr ), &ina ) < 0 ) { + return -1; + } + if (status==4) { + port = defaultPort; + } + return initIPAddr (ina, port, pIP); + } + else { + return -1; + } + } + + /* + * IP address as a raw number + */ + status = sscanf ( pAddrString, " %lu:%hu", &rawAddr, &port ); + if (status>=1) { + if ( rawAddr > 0xffffffff ) { + return -1; + } + if ( status == 1 ) { + port = defaultPort; + } + ina.s_addr = htonl ( rawAddr ); + return initIPAddr ( ina, port, pIP ); + } + + /* + * check for a valid host name before giving up + */ + status = sscanf ( pAddrString, " %511[^:]:%hu", hostName, &port ); + if ( status >= 1 ) { + if ( status == 1 ) { + port = defaultPort; + } + status = hostToIPAddr ( hostName, &ina ); + if ( status == 0 ) { + return initIPAddr ( ina, port, pIP ); + } + else { + return -1; + } + } + else { + return -1; + } +} diff --git a/src/libCom/misc/adjustment.c b/src/libCom/misc/adjustment.c new file mode 100644 index 000000000..ac10e28c9 --- /dev/null +++ b/src/libCom/misc/adjustment.c @@ -0,0 +1,55 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* src/libCom/adjustment.c */ + +/* Author: Peregrine McGehee */ + +#include +#include +#include + +/* Up to now epicsShareThings have been declared as imports + * (Appropriate for other stuff) + * After setting the following they will be declared as exports + * (Appropriate for what we implenment) + */ +#define epicsExportSharedSymbols +#include "adjustment.h" + +epicsShareFunc size_t adjustToWorstCaseAlignment(size_t size) +{ + int align_size, adjust; + struct test_long_word { char c; long lw; }; + struct test_double { char c; double d; }; + struct test_ptr { char c; void *p; }; + int test_long_size = sizeof(struct test_long_word) - sizeof(long); + int test_double_size = sizeof(struct test_double) - sizeof(double); + int test_ptr_size = sizeof(struct test_ptr) - sizeof(void *); + size_t adjusted_size = size; + + /* + * Use Jeff's alignment tests to determine worst case of long, + * double or pointer alignment requirements. + */ + align_size = test_long_size > test_ptr_size ? + test_long_size : test_ptr_size; + + align_size = align_size > test_double_size ? + align_size : test_double_size; + + /* + * Increase the size to fit worst case alignment if not already + * properly aligned. + */ + adjust = align_size - size%align_size; + if (adjust != align_size) adjusted_size += adjust; + + return (adjusted_size); +} diff --git a/src/libCom/misc/adjustment.h b/src/libCom/misc/adjustment.h new file mode 100644 index 000000000..3f152039f --- /dev/null +++ b/src/libCom/misc/adjustment.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* src/libCom/adjustment.h */ + +#ifndef INCadjustmenth +#define INCadjustmenth +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc size_t adjustToWorstCaseAlignment(size_t size); + +#ifdef __cplusplus +} +#endif + + +#endif /*INCadjustmenth*/ + diff --git a/src/libCom/misc/cantProceed.c b/src/libCom/misc/cantProceed.c new file mode 100644 index 000000000..ab80f0467 --- /dev/null +++ b/src/libCom/misc/cantProceed.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* 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: Marty Kraimer Date: 04JAN99 */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "errlog.h" +#include "cantProceed.h" +#include "epicsThread.h" + +epicsShareFunc void * callocMustSucceed(size_t count, size_t size, const char *msg) +{ + void * mem = NULL; + if (count > 0 && size > 0) { + while ((mem = calloc(count, size)) == NULL) { + errlogPrintf("%s: callocMustSucceed(%lu, %lu) - calloc failed\n", + msg, (unsigned long)count, (unsigned long)size); + errlogPrintf("Thread %s (%p) suspending.\n", + epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); + errlogFlush(); + epicsThreadSuspendSelf(); + } + } + return mem; +} + +epicsShareFunc void * mallocMustSucceed(size_t size, const char *msg) +{ + void * mem = NULL; + if (size > 0) { + while ((mem = malloc(size)) == NULL) { + errlogPrintf("%s: mallocMustSucceed(%lu) - malloc failed\n", + msg, (unsigned long)size); + errlogPrintf("Thread %s (%p) suspending.\n", + epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); + errlogFlush(); + epicsThreadSuspendSelf(); + } + } + return mem; +} + +epicsShareFunc void cantProceed(const char *msg, ...) +{ + va_list pvar; + va_start(pvar, msg); + if (msg) + errlogVprintf(msg, pvar); + va_end(pvar); + + errlogPrintf("Thread %s (%p) can't proceed, suspending.\n", + epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); + errlogFlush(); + + epicsThreadSleep(1.0); + while (1) + epicsThreadSuspendSelf(); +} diff --git a/src/libCom/misc/cantProceed.h b/src/libCom/misc/cantProceed.h new file mode 100644 index 000000000..9f4dc1cd8 --- /dev/null +++ b/src/libCom/misc/cantProceed.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef INCcantProceedh +#define INCcantProceedh + +#include + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void cantProceed(const char *errorMessage, ...); +epicsShareFunc void * callocMustSucceed(size_t count, size_t size, const char *errorMessage); +epicsShareFunc void * mallocMustSucceed(size_t size, const char *errorMessage); + +#ifdef __cplusplus +} +#endif + +#endif /* cantProceedh */ diff --git a/src/libCom/misc/compilerDependencies.h b/src/libCom/misc/compilerDependencies.h new file mode 100644 index 000000000..4d451469e --- /dev/null +++ b/src/libCom/misc/compilerDependencies.h @@ -0,0 +1,104 @@ + +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: + * Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef compilerDependencies_h +#define compilerDependencies_h + +/* + * This is an attempt to move all tests identifying what features a + * compiler supports into one file. + * + * Since this is a compiler, and not os dependent, issue then ifdefs + * are used. The ifdefs allow us to make the default assumption that + * standards incompliance issues will be fixed by future compiler + * releases. + */ + +#ifdef __cplusplus + +/* + * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete + * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification + */ + +#if defined ( _MSC_VER ) +# if _MSC_VER >= 1200 /* visual studio 6.0 or later */ +# define CXX_PLACEMENT_DELETE +# endif +# if _MSC_VER > 1300 /* some release after visual studio 7 we hope */ +# define CXX_THROW_SPECIFICATION +# endif +#elif defined ( __HP_aCC ) +# if _HP_aCC > 33300 +# define CXX_PLACEMENT_DELETE +# endif +# define CXX_THROW_SPECIFICATION +#elif defined ( __BORLANDC__ ) +# if __BORLANDC__ >= 0x600 +# define CXX_PLACEMENT_DELETE +# endif +# define CXX_THROW_SPECIFICATION +#elif defined ( __GNUC__ ) +# if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 95 ) +# define CXX_THROW_SPECIFICATION +# endif +# if __GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC_MINOR__ >= 96 ) +# define CXX_PLACEMENT_DELETE +# endif +#else +# define CXX_PLACEMENT_DELETE +# define CXX_THROW_SPECIFICATION +#endif + +/* + * usage: void func () epicsThrows (( std::bad_alloc, std::logic_error )) + */ +#if defined ( CXX_THROW_SPECIFICATION ) +# define epicsThrows(X) throw X +#else +# define epicsThrows(X) +#endif + +/* + * usage: epicsPlacementDeleteOperator (( void *, myMemoryManager & )) + */ +#if defined ( CXX_PLACEMENT_DELETE ) +# define epicsPlacementDeleteOperator(X) void operator delete X; +#else +# define epicsPlacementDeleteOperator(X) +#endif + +#endif /* __cplusplus */ + +/* + * Enable format-string checking if possible + */ +#ifdef __GNUC__ +# define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) +#else +# define EPICS_PRINTF_STYLE(f,a) +#endif + +/* + * Deprecation marker + */ +#ifdef __GNUC__ +# define EPICS_DEPRECATED __attribute__((deprecated)) +#else +# define EPICS_DEPRECATED +#endif + +#endif /* ifndef compilerDependencies_h */ diff --git a/src/libCom/misc/dbDefs.h b/src/libCom/misc/dbDefs.h new file mode 100644 index 000000000..0b5a23166 --- /dev/null +++ b/src/libCom/misc/dbDefs.h @@ -0,0 +1,65 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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: Marty Kraimer + * Date: 6-1-90 + */ + +#ifndef INC_dbDefs_H +#define INC_dbDefs_H + +#include + +#ifdef TRUE +# undef TRUE +#endif +#define TRUE 1 + +#ifdef FALSE +# undef FALSE +#endif +#define FALSE 0 + +/* deprecated, use static */ +#ifndef LOCAL +# define LOCAL static +#endif + +/* number of elements in an array */ +#ifndef NELEMENTS +# define NELEMENTS(array) (sizeof (array) / sizeof ((array) [0])) +#endif + +/* byte offset of member in structure - deprecated, use offsetof */ +#ifndef OFFSET +# define OFFSET(structure, member) offsetof(structure, member) +#endif + +/* Subtract member byte offset, returning pointer to parent object */ +#ifndef CONTAINER +# ifdef __GNUC__ +# define CONTAINER(ptr, structure, member) ({ \ + const __typeof(((structure*)0)->member) *_ptr = (ptr); \ + (structure*)((char*)_ptr - offsetof(structure, member)); \ + }) +# else +# define CONTAINER(ptr, structure, member) \ + ((structure*)((char*)(ptr) - offsetof(structure, member))) +# endif +#endif + +/*Process Variable Name Size */ +/* PVNAME_STRINGSZ includes the nil terminator */ +#define PVNAME_STRINGSZ 61 +#define PVNAME_SZ (PVNAME_STRINGSZ - 1) + +#define DB_MAX_CHOICES 30 + +#endif /* INC_dbDefs_H */ diff --git a/src/libCom/misc/epicsConvert.c b/src/libCom/misc/epicsConvert.c new file mode 100644 index 000000000..11778c33a --- /dev/null +++ b/src/libCom/misc/epicsConvert.c @@ -0,0 +1,45 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsConvert.c*/ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMath.h" +#include "epicsConvert.h" +#include "cantProceed.h" + +epicsShareFunc float epicsConvertDoubleToFloat(double value) +{ + float rtnvalue; + + if (value == 0) { + rtnvalue = 0; + } else if (!finite(value)) { + rtnvalue = (float)value; + } else { + double abs = fabs(value); + + if (abs >= FLT_MAX) { + if (value > 0) + rtnvalue = FLT_MAX; + else + rtnvalue = -FLT_MAX; + } else if (abs <= FLT_MIN) { + if (value > 0) + rtnvalue = FLT_MIN; + else + rtnvalue = -FLT_MIN; + } else { + rtnvalue = (float)value; + } + } + return rtnvalue; +} diff --git a/src/libCom/misc/epicsConvert.h b/src/libCom/misc/epicsConvert.h new file mode 100644 index 000000000..9674db0ef --- /dev/null +++ b/src/libCom/misc/epicsConvert.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*epicsConvert.h*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc float epicsConvertDoubleToFloat(double value); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libCom/misc/epicsExit.c b/src/libCom/misc/epicsExit.c new file mode 100644 index 000000000..5992d4ae8 --- /dev/null +++ b/src/libCom/misc/epicsExit.c @@ -0,0 +1,163 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsExit.c*/ +/* + * Author: Marty Kraimer + * Date: 23AUG2004 + * Thread exit revisions: Jeff Hill + * Date: 06Dec2006 + * + * Note that epicsExitCallAtThreadExits is currently called directly from the + * thread entry wrapper in OS dependent code. That approach might not work + * correctly if the thread exits indirectly instead of just returning from + * the function specified to epicsThreadCreate. For example the thread might + * exit via the exit() call. There might be OS dependent solutions for that + * weakness. + * + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "ellLib.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "cantProceed.h" +#include "epicsExit.h" + +typedef void (*epicsExitFunc)(void *arg); + +typedef struct exitNode { + ELLNODE node; + epicsExitFunc func; + void *arg; +}exitNode; + +typedef struct exitPvt { + ELLLIST list; +} exitPvt; + +static epicsThreadOnceId exitPvtOnce = EPICS_THREAD_ONCE_INIT; +static exitPvt * pExitPvtPerProcess = 0; +static epicsMutexId exitPvtLock = 0; +static epicsThreadPrivateId exitPvtPerThread = 0; + +static void destroyExitPvt ( exitPvt * pep ) +{ + ellFree ( &pep->list ); + free ( pep ); +} + +static exitPvt * createExitPvt (void) +{ + exitPvt * pep = calloc ( 1, sizeof ( * pep ) ); + if ( pep ) { + ellInit ( &pep->list ); + } + return pep; +} + +static void exitPvtOnceFunc ( void * pParm ) +{ + exitPvtPerThread = epicsThreadPrivateCreate (); + assert ( exitPvtPerThread ); + pExitPvtPerProcess = createExitPvt (); + assert ( pExitPvtPerProcess ); + exitPvtLock = epicsMutexMustCreate (); +} + +static void epicsExitCallAtExitsPvt ( exitPvt * pep ) +{ + exitNode *pexitNode; + while ( ( pexitNode = (exitNode *) ellLast ( & pep->list ) ) ) { + pexitNode->func ( pexitNode->arg ); + ellDelete ( & pep->list, & pexitNode->node ); + free ( pexitNode ); + } +} + +epicsShareFunc void epicsShareAPI epicsExitCallAtExits ( void ) +{ + exitPvt * pep = 0; + epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + epicsMutexMustLock ( exitPvtLock ); + if ( pExitPvtPerProcess ) { + pep = pExitPvtPerProcess; + pExitPvtPerProcess = 0; + } + epicsMutexUnlock ( exitPvtLock ); + if ( pep ) { + epicsExitCallAtExitsPvt ( pep ); + destroyExitPvt ( pep ); + } +} + +epicsShareFunc void epicsShareAPI epicsExitCallAtThreadExits ( void ) +{ + exitPvt * pep; + epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + pep = epicsThreadPrivateGet ( exitPvtPerThread ); + if ( pep ) { + epicsExitCallAtExitsPvt ( pep ); + destroyExitPvt ( pep ); + epicsThreadPrivateSet ( exitPvtPerThread, 0 ); + } +} + +static int epicsAtExitPvt ( + exitPvt * pep, epicsExitFunc func, void *arg ) +{ + int status = -1; + exitNode * pExitNode + = calloc ( 1, sizeof( *pExitNode ) ); + if ( pExitNode ) { + pExitNode->func = func; + pExitNode->arg = arg; + ellAdd ( & pep->list, & pExitNode->node ); + status = 0; + } + return status; +} + +epicsShareFunc int epicsShareAPI epicsAtThreadExit ( + epicsExitFunc func, void *arg ) +{ + exitPvt * pep; + epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + pep = epicsThreadPrivateGet ( exitPvtPerThread ); + if ( ! pep ) { + pep = createExitPvt (); + if ( ! pep ) { + return -1; + } + epicsThreadPrivateSet ( exitPvtPerThread, pep ); + } + return epicsAtExitPvt ( pep, func, arg ); +} + +epicsShareFunc int epicsShareAPI epicsAtExit( + epicsExitFunc func, void *arg ) +{ + int status = -1; + epicsThreadOnce ( & exitPvtOnce, exitPvtOnceFunc, 0 ); + epicsMutexMustLock ( exitPvtLock ); + if ( pExitPvtPerProcess ) { + status = epicsAtExitPvt ( pExitPvtPerProcess, func, arg ); + } + epicsMutexUnlock ( exitPvtLock ); + return status; +} + +epicsShareFunc void epicsShareAPI epicsExit(int status) +{ + epicsExitCallAtExits(); + epicsThreadSleep(1.0); + exit(status); +} diff --git a/src/libCom/misc/epicsExit.h b/src/libCom/misc/epicsExit.h new file mode 100644 index 000000000..212467368 --- /dev/null +++ b/src/libCom/misc/epicsExit.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*epicsExit.h*/ +#ifndef epicsExith +#define epicsExith +#include + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI epicsExit(int status); +epicsShareFunc void epicsShareAPI epicsExitCallAtExits(void); +epicsShareFunc int epicsShareAPI epicsAtExit( + void (*epicsExitFunc)(void *arg),void *arg); + +epicsShareFunc void epicsShareAPI epicsExitCallAtThreadExits(void); +epicsShareFunc int epicsShareAPI epicsAtThreadExit( + void (*epicsExitFunc)(void *arg),void *arg); + + +#ifdef __cplusplus +} +#endif + +#endif /*epicsExith*/ diff --git a/src/libCom/misc/epicsExport.h b/src/libCom/misc/epicsExport.h new file mode 100644 index 000000000..dea8dd67a --- /dev/null +++ b/src/libCom/misc/epicsExport.h @@ -0,0 +1,43 @@ +/*epicsExport.h */ + +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef INCepicsExporth +#define INCepicsExporth + +#ifdef __cplusplus +extern "C" { +#endif + +#define epicsExportSharedSymbols +#include + +typedef void (*REGISTRAR)(void); + +#define EPICS_EXPORT_POBJ(typ,obj) pvar_ ## typ ## _ ## obj +#define EPICS_EXPORT_PFUNC(obj) pvar_func_ ## obj + +#define epicsExportAddress(typ,obj) \ +epicsShareExtern typ *EPICS_EXPORT_POBJ(typ,obj); \ +epicsShareDef typ *EPICS_EXPORT_POBJ(typ,obj) = (typ *)(char *)&obj + +#define epicsExportRegistrar(func) \ +epicsShareFunc REGISTRAR EPICS_EXPORT_PFUNC(func) = (REGISTRAR)(void*)&func + +#define epicsRegisterFunction(func) \ +static void register_func_ ## func(void) { \ + registryFunctionAdd(#func,(REGISTRYFUNCTION)func);} \ +epicsExportRegistrar(register_func_ ## func) + +#ifdef __cplusplus +} +#endif + +#endif /* epicsExporth */ diff --git a/src/libCom/misc/epicsStdlib.c b/src/libCom/misc/epicsStdlib.c new file mode 100644 index 000000000..494fc60be --- /dev/null +++ b/src/libCom/misc/epicsStdlib.c @@ -0,0 +1,91 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonna LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/*epicsStdlib.c*/ +/*Author: Eric Norum */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMath.h" +#include "epicsStdlib.h" +#include "epicsString.h" + + +epicsShareFunc int epicsScanDouble(const char *str, double *dest) +{ + char *endp; + double dtmp; + + dtmp = epicsStrtod(str, &endp); + if (endp == str) + return 0; + *dest = dtmp; + return 1; +} + +epicsShareFunc int epicsScanFloat(const char *str, float *dest) +{ + char *endp; + double dtmp; + + dtmp = epicsStrtod(str, &endp); + if (endp == str) + return 0; + *dest = (float)dtmp; + return 1; +} + +/* Systems with a working strtod() just #define epicsStrtod strtod */ +#ifndef epicsStrtod +epicsShareFunc double epicsStrtod(const char *str, char **endp) +{ + const char *cp = str; + int negative = 0; + double res; + + while (isspace((int)*cp)) + cp++; + + if (*cp == '+') { + cp++; + } else if (*cp == '-') { + negative = 1; + cp++; + } + + if (!isalpha((int)*cp)) + return strtod(str, endp); + + if (epicsStrnCaseCmp("NAN", cp, 3) == 0) { + res = epicsNAN; + cp += 3; + if (*cp == '(') { + cp++; + while (*cp && (*cp++ != ')')) + continue; + } + } + else if (epicsStrnCaseCmp("INF", cp, 3) == 0) { + res = negative ? -epicsINF : epicsINF; + cp += 3; + if (epicsStrnCaseCmp("INITY", cp, 5) == 0) { + cp += 5; + } + } else { + cp = str; + res = 0; + } + + if (endp) + *endp = (char *)cp; + + return res; +} +#endif diff --git a/src/libCom/misc/epicsStdlib.h b/src/libCom/misc/epicsStdlib.h new file mode 100644 index 000000000..84d4af062 --- /dev/null +++ b/src/libCom/misc/epicsStdlib.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*epicsStdlib.h*/ +/*Author: Eric Norum */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsScanDouble(const char *str, double *dest); +epicsShareFunc int epicsScanFloat(const char *str, float *dest); + +#include +#include + +#ifdef __cplusplus +} +#endif + diff --git a/src/libCom/misc/epicsString.c b/src/libCom/misc/epicsString.c new file mode 100644 index 000000000..907bba985 --- /dev/null +++ b/src/libCom/misc/epicsString.c @@ -0,0 +1,338 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ + +/* Authors: Jun-ichi Odagiri, Marty Kraimer, Eric Norum, + * Mark Rivers, Andrew Johnson, Ralph Lange + * + * Routines in this file should have corresponding test code in + * libCom/test/epicsStringTest.c + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "cantProceed.h" +#include "epicsString.h" + +/* Deprecated, use epicsStrnRawFromEscaped() instead */ +int dbTranslateEscape(char *to, const char *from) +{ + size_t big_enough = strlen(from)+1; + return epicsStrnRawFromEscaped(to, big_enough, from, big_enough); +} + +int epicsStrnRawFromEscaped(char *to, size_t outsize, const char *from, + size_t inlen) +{ + const char *pfrom = from; + char *pto = to; + char c; + int nto = 0, nfrom = 0; + + while ((c = *pfrom++) && nto < outsize && nfrom < inlen) { + nfrom++; + if (c == '\\') { + if (nfrom >= inlen || *pfrom == '\0') break; + switch (*pfrom) { + case 'a': pfrom++; nfrom++; *pto++ = '\a' ; nto++; break; + case 'b': pfrom++; nfrom++; *pto++ = '\b' ; nto++; break; + case 'f': pfrom++; nfrom++; *pto++ = '\f' ; nto++; break; + case 'n': pfrom++; nfrom++; *pto++ = '\n' ; nto++; break; + case 'r': pfrom++; nfrom++; *pto++ = '\r' ; nto++; break; + case 't': pfrom++; nfrom++; *pto++ = '\t' ; nto++; break; + case 'v': pfrom++; nfrom++; *pto++ = '\v' ; nto++; break; + case '\\': pfrom++; nfrom++; *pto++ = '\\' ; nto++; break; + case '\?': pfrom++; nfrom++; *pto++ = '\?' ; nto++; break; + case '\'': pfrom++; nfrom++; *pto++ = '\'' ; nto++; break; + case '\"': pfrom++; nfrom++; *pto++ = '\"' ; nto++; break; + case '0' :case '1' :case '2' :case '3' : + case '4' :case '5' :case '6' :case '7' : + { + int i; + char strval[4] = {0,0,0,0}; + unsigned int ival; + unsigned char *pchar; + + for (i=0; i<3; i++) { + if ((*pfrom < '0') || (*pfrom > '7')) break; + strval[i] = *pfrom++; nfrom++; + } + sscanf(strval,"%o",&ival); + pchar = (unsigned char *)(pto++); nto++; + *pchar = (unsigned char)(ival); + } + break; + case 'x' : + { + int i; + char strval[3] = {0,0,0}; + unsigned int ival; + unsigned char *pchar; + + pfrom++; /*skip the x*/ + for (i=0; i<2; i++) { + if (!isxdigit((int)*pfrom)) break; + strval[i] = *pfrom++; nfrom++; + } + sscanf(strval,"%x",&ival); + pchar = (unsigned char *)(pto++); nto++; + *pchar = (unsigned char)(ival); + } + break; + default: + *pto++ = *pfrom++; nfrom++; nto++; + } + } else { + *pto++ = c; nto++; + } + } + *pto = '\0'; /* NOTE that nto does not have to be incremented */ + return nto; +} + +int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, const char *inbuf, + size_t inlen) +{ + int maxout = outsize; + int nout = 0; + int len; + char *outpos = outbuf; + + while (inlen--) { + char c = *inbuf++; + switch (c) { + case '\a': len = epicsSnprintf(outpos, maxout, "\\a"); break; + case '\b': len = epicsSnprintf(outpos, maxout, "\\b"); break; + case '\f': len = epicsSnprintf(outpos, maxout, "\\f"); break; + case '\n': len = epicsSnprintf(outpos, maxout, "\\n"); break; + case '\r': len = epicsSnprintf(outpos, maxout, "\\r"); break; + case '\t': len = epicsSnprintf(outpos, maxout, "\\t"); break; + case '\v': len = epicsSnprintf(outpos, maxout, "\\v"); break; + case '\\': len = epicsSnprintf(outpos, maxout, "\\\\"); ; break; + case '\'': len = epicsSnprintf(outpos, maxout, "\\'"); break; + case '\"': len = epicsSnprintf(outpos, maxout, "\\\""); break; + default: + if (isprint((int)c)) + len = epicsSnprintf(outpos, maxout, "%c", c); + else + len = epicsSnprintf(outpos, maxout, "\\%03o", + (unsigned char)c); + break; + } + if (len<0) return -1; + nout += len; + if (nout < outsize) { + maxout -= len; + outpos += len; + } else { + outpos = outpos + maxout -1; + maxout = 1; + } + } + *outpos = '\0'; + return nout; +} + +size_t epicsStrnEscapedFromRawSize(const char *inbuf, size_t inlen) +{ + size_t nout = inlen; + + while (inlen--) { + char c = *inbuf++; + + switch (c) { + case '\a': case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': case '\\': + case '\'': case '\"': + nout++; + break; + default: + if (!isprint((int)c)) + nout += 3; + } + } + return nout; +} + +int epicsStrCaseCmp(const char *s1, const char *s2) +{ + while (1) { + int ch1 = toupper(*s1); + int ch2 = toupper(*s2); + + if (ch1 == 0) return (ch2 != 0); + if (ch2 == 0) return -1; + if (ch1 < ch2) return -1; + if (ch1 > ch2) return 1; + s1++; + s2++; + } +} + +int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len) +{ + size_t i = 0; + + while (i++ < len) { + int ch1 = toupper(*s1); + int ch2 = toupper(*s2); + + if (ch1 == 0) return (ch2 != 0); + if (ch2 == 0) return -1; + if (ch1 < ch2) return -1; + if (ch1 > ch2) return 1; + s1++; + s2++; + } + return 0; +} + +char * epicsStrDup(const char *s) +{ + return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s); +} + +int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len) +{ + int nout = 0; + + while (len--) { + char c = *s++; + + switch (c) { + case '\a': nout += fprintf(fp, "\\a"); break; + case '\b': nout += fprintf(fp, "\\b"); break; + case '\f': nout += fprintf(fp, "\\f"); break; + case '\n': nout += fprintf(fp, "\\n"); break; + case '\r': nout += fprintf(fp, "\\r"); break; + case '\t': nout += fprintf(fp, "\\t"); break; + case '\v': nout += fprintf(fp, "\\v"); break; + case '\\': nout += fprintf(fp, "\\\\"); break; + case '\'': nout += fprintf(fp, "\\'"); break; + case '\"': nout += fprintf(fp, "\\\""); break; + default: + if (isprint((int)c)) + nout += fprintf(fp, "%c", c); + else + nout += fprintf(fp, "\\%03o", (unsigned char)c); + break; + } + } + return nout; +} + +int epicsStrGlobMatch(const char *str, const char *pattern) +{ + const char *cp = NULL, *mp = NULL; + + while ((*str) && (*pattern != '*')) { + if ((*pattern != *str) && (*pattern != '?')) + return 0; + pattern++; + str++; + } + while (*str) { + if (*pattern == '*') { + if (!*++pattern) + return 1; + mp = pattern; + cp = str+1; + } + else if ((*pattern == *str) || (*pattern == '?')) { + pattern++; + str++; + } + else { + pattern = mp; + str = cp++; + } + } + while (*pattern == '*') + pattern++; + return !*pattern; +} + +char * epicsStrtok_r(char *s, const char *delim, char **lasts) +{ + const char *spanp; + int c, sc; + char *tok; + + if (s == NULL && (s = *lasts) == NULL) + return NULL; + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *lasts = NULL; + return NULL; + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *lasts = s; + return tok; + } + } while (sc != 0); + } +} + + +unsigned int epicsStrHash(const char *str, unsigned int seed) +{ + unsigned int hash = seed; + char c; + + while ((c = *str++)) { + hash ^= ~((hash << 11) ^ c ^ (hash >> 5)); + if (!(c = *str++)) break; + hash ^= (hash << 7) ^ c ^ (hash >> 3); + } + return hash; +} + +unsigned int epicsMemHash(const char *str, size_t length, unsigned int seed) +{ + unsigned int hash = seed; + + while (length--) { + hash ^= ~((hash << 11) ^ *str++ ^ (hash >> 5)); + if (!length--) break; + hash ^= (hash << 7) ^ *str++ ^ (hash >> 3); + } + return hash; +} diff --git a/src/libCom/misc/epicsString.h b/src/libCom/misc/epicsString.h new file mode 100644 index 000000000..658f333b1 --- /dev/null +++ b/src/libCom/misc/epicsString.h @@ -0,0 +1,50 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ + +/* Authors: Jun-ichi Odagiri, Marty Kraimer, Eric Norum, + * Mark Rivers, Andrew Johnson, Ralph Lange + */ + +#ifndef INC_epicsString_H +#define INC_epicsString_H + +#include +#include "epicsTypes.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsStrnRawFromEscaped(char *outbuf, size_t outsize, + const char *inbuf, size_t inlen); +epicsShareFunc int epicsStrnEscapedFromRaw(char *outbuf, size_t outsize, + const char *inbuf, size_t inlen); +epicsShareFunc size_t epicsStrnEscapedFromRawSize(const char *buf, size_t len); +epicsShareFunc int epicsStrCaseCmp(const char *s1, const char *s2); +epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len); +epicsShareFunc char * epicsStrDup(const char *s); +epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n); +#define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw +epicsShareFunc int epicsStrGlobMatch(const char *str, const char *pattern); +epicsShareFunc char * epicsStrtok_r(char *s, const char *delim, char **lasts); +epicsShareFunc unsigned int epicsStrHash(const char *str, unsigned int seed); +epicsShareFunc unsigned int epicsMemHash(const char *str, size_t length, + unsigned int seed); + +/* dbTranslateEscape is deprecated, use epicsStrnRawFromEscaped instead */ +epicsShareFunc int dbTranslateEscape(char *s, const char *ct); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsString_H */ diff --git a/src/libCom/misc/epicsTypes.h b/src/libCom/misc/epicsTypes.h new file mode 100644 index 000000000..c250baa99 --- /dev/null +++ b/src/libCom/misc/epicsTypes.h @@ -0,0 +1,240 @@ +/*************************************************************************\ +* 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 + * Date: 5-95 + */ + +#ifndef INCepicsTypesh +#define INCepicsTypesh 1 + +#include "shareLib.h" + +#if __STDC_VERSION__ >= 199901L +#include +#endif + +#ifndef stringOf +# if defined (__STDC__ ) || defined (__cplusplus) +# define stringOf(TOKEN) #TOKEN +# else +# define stringOf(TOKEN) "TOKEN" +# endif +#endif + +typedef enum { + epicsFalse=0, + epicsTrue=1 } epicsBoolean; + +/* + * Architecture Independent Data Types + * (so far, this is sufficient for all archs we have ported to) + */ +#if __STDC_VERSION__ >= 199901L + typedef int8_t epicsInt8; + typedef uint8_t epicsUInt8; + typedef int16_t epicsInt16; + typedef uint16_t epicsUInt16; + typedef epicsUInt16 epicsEnum16; + typedef int32_t epicsInt32; + typedef uint32_t epicsUInt32; + typedef int64_t epicsInt64; + typedef uint64_t epicsUInt64; +#else + typedef char epicsInt8; + typedef unsigned char epicsUInt8; + typedef short epicsInt16; + typedef unsigned short epicsUInt16; + typedef epicsUInt16 epicsEnum16; + typedef int epicsInt32; + typedef unsigned int epicsUInt32; +#endif +typedef float epicsFloat32; +typedef double epicsFloat64; +typedef epicsInt32 epicsStatus; + + +typedef struct { + unsigned length; + char *pString; +}epicsString; + +/* + * !! Dont use this - it may vanish in the future !! + * + * Provided only for backwards compatibility with + * db_access.h + * + */ +#define MAX_STRING_SIZE 40 +typedef char epicsOldString[MAX_STRING_SIZE]; + +/* + * union of all types + * + * Strings included here as pointers only so that we support + * large string types. + * + * Arrays included here as pointers because large arrays will + * not fit in this union. + */ +typedef union epics_any{ + epicsInt8 int8; + epicsUInt8 uInt8; + epicsInt16 int16; + epicsUInt16 uInt16; + epicsEnum16 enum16; + epicsInt32 int32; + epicsUInt32 uInt32; + epicsFloat32 float32; + epicsFloat64 float64; + epicsString string; +}epicsAny; + +/* + * Corresponding Type Codes + * (this enum must start at zero) + * + * !! Update epicsTypeToDBR_XXXX[] and DBR_XXXXToEpicsType + * in db_access.h if you edit this enum !! + */ +typedef enum { + epicsInt8T, + epicsUInt8T, + epicsInt16T, + epicsUInt16T, + epicsEnum16T, + epicsInt32T, + epicsUInt32T, + epicsFloat32T, + epicsFloat64T, + epicsStringT, + epicsOldStringT +}epicsType; +#define firstEpicsType epicsInt8T +#define lastEpicsType epicsOldStringT +#define validEpicsType(x) ((x>=firstEpicsType) && (x<=lastEpicsType)) +#define invalidEpicsType(x) ((xlastEpicsType)) + + +/* + * The enumeration "epicsType" is an index to this array + * of type name strings. + */ +#ifdef epicsTypesGLOBAL +epicsShareDef const char *epicsTypeNames [lastEpicsType+1] = { + "epicsInt8", + "epicsUInt8", + "epicsInt16", + "epicsUInt16", + "epicsEnum16", + "epicsInt32", + "epicsUInt32", + "epicsFloat32", + "epicsFloat64", + "epicsString", + "epicsOldString", +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const char *epicsTypeNames [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +/* + * The enumeration "epicsType" is an index to this array + * of type code name strings. + */ +#ifdef epicsTypesGLOBAL +epicsShareDef const char *epicsTypeCodeNames [lastEpicsType+1] = { + "epicsInt8T", + "epicsUInt8T", + "epicsInt16T", + "epicsUInt16T", + "epicsEnum16T", + "epicsInt32T", + "epicsUInt32T", + "epicsFloat32T", + "epicsFloat64T", + "epicsStringT", + "epicsOldStringT", +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const char *epicsTypeCodeNames [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +#ifdef epicsTypesGLOBAL +epicsShareDef const unsigned epicsTypeSizes [lastEpicsType+1] = { + sizeof (epicsInt8), + sizeof (epicsUInt8), + sizeof (epicsInt16), + sizeof (epicsUInt16), + sizeof (epicsEnum16), + sizeof (epicsInt32), + sizeof (epicsUInt32), + sizeof (epicsFloat32), + sizeof (epicsFloat64), + sizeof (epicsString), + sizeof (epicsOldString), +}; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const unsigned epicsTypeSizes [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +/* + * The enumeration "epicsType" is an index to this array + * of type class identifiers. + */ +typedef enum { + epicsIntC, + epicsUIntC, + epicsEnumC, + epicsFloatC, + epicsStringC, + epicsOldStringC} epicsTypeClass; +#ifdef epicsTypesGLOBAL +epicsShareDef const epicsTypeClass epicsTypeClasses [lastEpicsType+1] = { + epicsIntC, + epicsUIntC, + epicsIntC, + epicsUIntC, + epicsEnumC, + epicsIntC, + epicsUIntC, + epicsFloatC, + epicsFloatC, + epicsStringC, + epicsOldStringC + }; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const epicsTypeClass epicsTypeClasses [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + + +#ifdef epicsTypesGLOBAL +epicsShareDef const char *epicsTypeAnyFieldName [lastEpicsType+1] = { + "int8", + "uInt8", + "int16", + "uInt16", + "enum16", + "int32", + "uInt32", + "float32", + "float64", + "string", + "", /* Old Style Strings will not be in epicsAny type */ + }; +#else /* epicsTypesGLOBAL */ +epicsShareExtern const char *epicsTypeAnyFieldName [lastEpicsType+1]; +#endif /* epicsTypesGLOBAL */ + +#endif /* INCepicsTypesh */ + diff --git a/src/libCom/misc/epicsUnitTest.c b/src/libCom/misc/epicsUnitTest.c new file mode 100644 index 000000000..d4afe2a0b --- /dev/null +++ b/src/libCom/misc/epicsUnitTest.c @@ -0,0 +1,263 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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: Andrew Johnson + * + * Unit test module which generates output in the Test Anything Protocol + * format. See perldoc Test::Harness for details of output format. + * + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsUnitTest.h" +#include "epicsExit.h" +#include "epicsTime.h" +#include "ellLib.h" +#include "cantProceed.h" + +typedef struct { + ELLNODE node; + const char *name; + int tests; + int failures; + int skips; +} testFailure; + +static epicsMutexId testLock = 0; +static int perlHarness; +static int planned; +static int tested; +static int passed; +static int failed; +static int skipped; +static int bonus; +static const char *todo; + +epicsTimeStamp started; +static int Harness; +static int Programs; +static int Tests; + +ELLLIST faults; +const char *testing = NULL; + +static epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; + +static void testOnce(void *dummy) { + testLock = epicsMutexMustCreate(); + perlHarness = (getenv("HARNESS_ACTIVE") != NULL); +} + +void testPlan(int plan) { + epicsThreadOnce(&onceFlag, testOnce, NULL); + epicsMutexMustLock(testLock); + planned = plan; + tested = passed = failed = skipped = bonus = 0; + todo = NULL; + if (plan) printf("1..%d\n", plan); + epicsMutexUnlock(testLock); +} + +int testOkV(int pass, const char *fmt, va_list pvar) { + const char *result = "not ok"; + epicsMutexMustLock(testLock); + tested++; + if (pass) { + result += 4; /* skip "not " */ + passed++; + if (todo) + bonus++; + } else { + if (todo) + passed++; + else + failed++; + } + printf("%s %2d - ", result, tested); + vprintf(fmt, pvar); + if (todo) + printf(" # TODO %s", todo); + putchar('\n'); + fflush(stdout); + epicsMutexUnlock(testLock); + return pass; +} + +int testOk(int pass, const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + testOkV(pass, fmt, pvar); + va_end(pvar); + return pass; +} + +void testPass(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + testOkV(1, fmt, pvar); + va_end(pvar); +} + +void testFail(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + testOkV(0, fmt, pvar); + va_end(pvar); +} + +void testSkip(int skip, const char *why) { + epicsMutexMustLock(testLock); + while (skip-- > 0) { + tested++; + passed++; + skipped++; + printf("ok %2d # SKIP %s\n", tested, why); + } + fflush(stdout); + epicsMutexUnlock(testLock); +} + +void testTodoBegin(const char *why) { + epicsMutexMustLock(testLock); + todo = why; + epicsMutexUnlock(testLock); +} + +void testTodoEnd(void) { + todo = NULL; +} + +int testDiag(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + epicsMutexMustLock(testLock); + printf("# "); + vprintf(fmt, pvar); + putchar('\n'); + fflush(stdout); + epicsMutexUnlock(testLock); + va_end(pvar); + return 0; +} + +void testAbort(const char *fmt, ...) { + va_list pvar; + va_start(pvar, fmt); + printf("Bail out! "); + vprintf(fmt, pvar); + putchar('\n'); + fflush(stdout); + va_end(pvar); + abort(); +} + +static void testResult(const char *result, int count) { + printf("%12.12s: %3d = %5.2f%%\n", result, count, 100.0 * count / tested); +} + +int testDone(void) { + int status = 0; + + epicsMutexMustLock(testLock); + if (perlHarness) { + if (!planned) printf("1..%d\n", tested); + } else { + if (planned && tested > planned) { + printf("\nRan %d tests but only planned for %d!\n", tested, planned); + status = 2; + } else if (planned && tested < planned) { + printf("\nPlanned %d tests but only ran %d\n", planned, tested); + status = 2; + } + printf("\n Results\n =======\n Tests: %-3d\n", tested); + if (tested) { + testResult("Passed", passed); + if (bonus) testResult("Todo Passes", bonus); + if (failed) { + testResult("Failed", failed); + status = 1; + } + if (skipped) testResult("Skipped", skipped); + } + } + if (Harness) { + if (failed) { + testFailure *fault = callocMustSucceed(1, sizeof(testFailure), + "testDone calloc"); + fault->name = testing; + fault->tests = tested; + fault->failures = failed; + fault->skips = skipped; + ellAdd(&faults, &fault->node); + } + Programs++; + Tests += tested; + } + epicsMutexUnlock(testLock); + return (status); +} + + +/* Our test harness, for RTEMS and vxWorks */ + +static void harnessExit(void *dummy) { + epicsTimeStamp ended; + int Faulty; + + if (!Harness) return; + + epicsTimeGetCurrent(&ended); + + printf("\n\n EPICS Test Harness Results" + "\n ==========================\n\n"); + + Faulty = ellCount(&faults); + if (!Faulty) + printf("All tests successful.\n"); + else { + int Failures = 0; + testFailure *f; + + printf("Failing Program Tests Faults\n" + "---------------------------------------\n"); + while ((f = (testFailure *)ellGet(&faults))) { + Failures += f->failures; + printf("%-25s %5d %5d\n", f->name, f->tests, f->failures); + if (f->skips) + printf("%d subtests skipped\n", f->skips); + free(f); + } + printf("\nFailed %d/%d test programs. %d/%d subtests failed.\n", + Faulty, Programs, Failures, Tests); + } + + printf("Programs=%d, Tests=%d, %.0f wallclock secs\n\n", + Programs, Tests, epicsTimeDiffInSeconds(&ended, &started)); +} + +void testHarness(void) { + epicsThreadOnce(&onceFlag, testOnce, NULL); + epicsAtExit(harnessExit, NULL); + Harness = 1; + Programs = 0; + Tests = 0; + ellInit(&faults); + epicsTimeGetCurrent(&started); +} + +void runTestFunc(const char *name, TESTFUNC func) { + printf("\n***** %s *****\n", name); + testing = name; + func(); /* May not return */ + epicsThreadSleep(1.0); +} diff --git a/src/libCom/misc/epicsUnitTest.h b/src/libCom/misc/epicsUnitTest.h new file mode 100644 index 000000000..0e3f3927d --- /dev/null +++ b/src/libCom/misc/epicsUnitTest.h @@ -0,0 +1,49 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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: Andrew Johnson + */ + +#include + +#include "compilerDependencies.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void testPlan(int tests); +epicsShareFunc int testOkV(int pass, const char *fmt, va_list pvar); +epicsShareFunc int testOk(int pass, const char *fmt, ...) + EPICS_PRINTF_STYLE(2, 3); +epicsShareFunc void testPass(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc void testFail(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc void testSkip(int skip, const char *why); +epicsShareFunc void testTodoBegin(const char *why); +epicsShareFunc void testTodoEnd(void); +epicsShareFunc int testDiag(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc void testAbort(const char *fmt, ...) + EPICS_PRINTF_STYLE(1, 2); +epicsShareFunc int testDone(void); + +#define testOk1(cond) testOk(cond, "%s", #cond) + + +typedef int (*TESTFUNC)(void); +epicsShareFunc void testHarness(void); +epicsShareFunc void runTestFunc(const char *name, TESTFUNC func); + +#define runTest(func) runTestFunc(#func, func) + +#ifdef __cplusplus +} +#endif diff --git a/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp b/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp new file mode 100644 index 000000000..b3c216d04 --- /dev/null +++ b/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp @@ -0,0 +1,450 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "ipAddrToAsciiAsynchronous.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsGuard.h" +#include "epicsExit.h" +#include "tsDLList.h" +#include "tsFreeList.h" +#include "errlog.h" + +// - this class implements the asynchronous DNS query +// - it completes early with the host name in dotted IP address form +// if the ipAddrToAsciiEngine is destroyed before IO completion +// or if there are too many items already in the engine's queue. +class ipAddrToAsciiTransactionPrivate : + public ipAddrToAsciiTransaction, + public tsDLNode < ipAddrToAsciiTransactionPrivate > { +public: + ipAddrToAsciiTransactionPrivate ( class ipAddrToAsciiEnginePrivate & engineIn ); + virtual ~ipAddrToAsciiTransactionPrivate (); + osiSockAddr address () const; + void show ( unsigned level ) const; + void * operator new ( size_t size, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & ); + epicsPlacementDeleteOperator (( void *, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & )) +private: + osiSockAddr addr; + ipAddrToAsciiEnginePrivate & engine; + ipAddrToAsciiCallBack * pCB; + bool pending; + void ipAddrToAscii ( const osiSockAddr &, ipAddrToAsciiCallBack & ); + void release (); + void * operator new ( size_t ); + void operator delete ( void * ); + friend class ipAddrToAsciiEnginePrivate; + ipAddrToAsciiTransactionPrivate & operator = ( const ipAddrToAsciiTransactionPrivate & ); + ipAddrToAsciiTransactionPrivate ( const ipAddrToAsciiTransactionPrivate & ); +}; + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +extern "C" { +static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * ); +static void ipAddrToAsciiEngineShutdownRequest ( void * ); +} + +// - this class executes the synchronous DNS query +// - it creates one thread +class ipAddrToAsciiEnginePrivate : + public ipAddrToAsciiEngine, + public epicsThreadRunable { +public: + ipAddrToAsciiEnginePrivate (); + virtual ~ipAddrToAsciiEnginePrivate (); + void show ( unsigned level ) const; +private: + char nameTmp [1024]; + tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > + transactionFreeList; + tsDLList < ipAddrToAsciiTransactionPrivate > labor; + mutable epicsMutex mutex; + epicsEvent laborEvent; + epicsEvent destructorBlockEvent; + epicsThread thread; + ipAddrToAsciiTransactionPrivate * pCurrent; + unsigned cancelPendingCount; + bool exitFlag; + bool callbackInProgress; + static epicsMutex * pGlobalMutex; + static ipAddrToAsciiEnginePrivate * pEngine; + static unsigned numberOfReferences; + static bool shutdownRequest; + ipAddrToAsciiTransaction & createTransaction (); + void release (); + void run (); + ipAddrToAsciiEnginePrivate ( const ipAddrToAsciiEngine & ); + ipAddrToAsciiEnginePrivate & operator = ( const ipAddrToAsciiEngine & ); + friend class ipAddrToAsciiEngine; + friend class ipAddrToAsciiTransactionPrivate; + friend void ipAddrToAsciiEngineShutdownRequest ( void * ); + friend void ipAddrToAsciiEngineGlobalMutexConstruct ( void * ); +}; + +epicsMutex * ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0; +ipAddrToAsciiEnginePrivate * ipAddrToAsciiEnginePrivate :: pEngine = 0; +unsigned ipAddrToAsciiEnginePrivate :: numberOfReferences = 0u; +bool ipAddrToAsciiEnginePrivate :: shutdownRequest = false; +static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = 0; + +// the users are not required to supply a show routine +// for there transaction callback class +void ipAddrToAsciiCallBack::show ( unsigned /* level */ ) const {} + +// some noop pure virtual destructures +ipAddrToAsciiCallBack::~ipAddrToAsciiCallBack () {} +ipAddrToAsciiTransaction::~ipAddrToAsciiTransaction () {} +ipAddrToAsciiEngine::~ipAddrToAsciiEngine () {} + +static void ipAddrToAsciiEngineShutdownRequest ( void * ) +{ + bool deleteGlobalMutexCondDetected = false; + { + epicsGuard < epicsMutex > + guard ( * ipAddrToAsciiEnginePrivate :: pGlobalMutex ); + ipAddrToAsciiEnginePrivate :: shutdownRequest = true; + deleteGlobalMutexCondDetected = + ( ipAddrToAsciiEnginePrivate :: numberOfReferences == 0 ); + } + if ( deleteGlobalMutexCondDetected ) { + delete ipAddrToAsciiEnginePrivate :: pGlobalMutex; + ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0; + } +} + +static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * ) +{ + ipAddrToAsciiEnginePrivate :: pGlobalMutex = new epicsMutex (); + epicsAtExit ( ipAddrToAsciiEngineShutdownRequest, 0 ); +} + +// for now its probably sufficent to allocate one +// DNS transaction thread for all codes sharing +// the same process that need DNS services but we +// leave our options open for the future +ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate () +{ + epicsThreadOnce ( + & ipAddrToAsciiEngineGlobalMutexOnceFlag, + ipAddrToAsciiEngineGlobalMutexConstruct, 0 ); + // since we must not own lock when checking this flag + // this diagnostic has imperfect detection, but never + // incorrect detection + if ( ipAddrToAsciiEnginePrivate :: shutdownRequest ) { + throw std :: runtime_error ( + "ipAddrToAsciiEngine::allocate (): " + "attempts to create an " + "ipAddrToAsciiEngine while the exit " + "handlers are running are rejected"); + } + epicsGuard < epicsMutex > guard ( * ipAddrToAsciiEnginePrivate::pGlobalMutex ); + if ( ! ipAddrToAsciiEnginePrivate::pEngine ) { + ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiEnginePrivate (); + } + ipAddrToAsciiEnginePrivate::numberOfReferences++; + return * ipAddrToAsciiEnginePrivate::pEngine; +} + +ipAddrToAsciiEnginePrivate::ipAddrToAsciiEnginePrivate () : + thread ( *this, "ipToAsciiProxy", + epicsThreadGetStackSize(epicsThreadStackBig), + epicsThreadPriorityLow ), + pCurrent ( 0 ), cancelPendingCount ( 0u ), exitFlag ( false ), + callbackInProgress ( false ) +{ + this->thread.start (); // start the thread +} + +ipAddrToAsciiEnginePrivate::~ipAddrToAsciiEnginePrivate () +{ + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->exitFlag = true; + } + this->laborEvent.signal (); + this->thread.exitWait (); +} + +// for now its probably sufficent to allocate one +// DNS transaction thread for all codes sharing +// the same process that need DNS services but we +// leave our options open for the future +void ipAddrToAsciiEnginePrivate::release () +{ + bool deleteGlobalMutexCondDetected = false; + epicsThreadOnce ( + & ipAddrToAsciiEngineGlobalMutexOnceFlag, + ipAddrToAsciiEngineGlobalMutexConstruct, 0 ); + { + epicsGuard < epicsMutex > + guard ( * ipAddrToAsciiEnginePrivate::pGlobalMutex ); + assert ( ipAddrToAsciiEnginePrivate::numberOfReferences > 0u ); + ipAddrToAsciiEnginePrivate::numberOfReferences--; + if ( ipAddrToAsciiEnginePrivate::numberOfReferences == 0u ) { + deleteGlobalMutexCondDetected = + ipAddrToAsciiEnginePrivate :: shutdownRequest; + delete ipAddrToAsciiEnginePrivate :: pEngine; + ipAddrToAsciiEnginePrivate :: pEngine = 0; + } + } + if ( deleteGlobalMutexCondDetected ) { + delete ipAddrToAsciiEnginePrivate :: pGlobalMutex; + ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0; + } +} + +void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + printf ( "ipAddrToAsciiEngine at %p with %u requests pending\n", + static_cast (this), this->labor.count () ); + if ( level > 0u ) { + tsDLIterConst < ipAddrToAsciiTransactionPrivate > + pItem = this->labor.firstIter (); + while ( pItem.valid () ) { + pItem->show ( level - 1u ); + pItem++; + } + } + if ( level > 1u ) { + printf ( "mutex:\n" ); + this->mutex.show ( level - 2u ); + printf ( "laborEvent:\n" ); + this->laborEvent.show ( level - 2u ); + printf ( "exitFlag boolean = %u\n", this->exitFlag ); + printf ( "exit event:\n" ); + } +} + +inline void * ipAddrToAsciiTransactionPrivate::operator new ( size_t size, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void ipAddrToAsciiTransactionPrivate::operator delete ( void * pTrans, tsFreeList + < ipAddrToAsciiTransactionPrivate, 0x80 > & freeList ) +{ + freeList.release ( pTrans ); +} +#endif + +void * ipAddrToAsciiTransactionPrivate::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void ipAddrToAsciiTransactionPrivate::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + + +ipAddrToAsciiTransaction & ipAddrToAsciiEnginePrivate::createTransaction () +{ + return * new ( this->transactionFreeList ) ipAddrToAsciiTransactionPrivate ( *this ); +} + +void ipAddrToAsciiEnginePrivate::run () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + while ( ! this->exitFlag ) { + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->laborEvent.wait (); + } + while ( true ) { + ipAddrToAsciiTransactionPrivate * pItem = this->labor.get (); + if ( ! pItem ) { + break; + } + osiSockAddr addr = pItem->addr; + this->pCurrent = pItem; + + if ( this->exitFlag ) + { + sockAddrToDottedIP ( & addr.sa, this->nameTmp, + sizeof ( this->nameTmp ) ); + } + else { + epicsGuardRelease < epicsMutex > unguard ( guard ); + // depending on DNS configuration, this could take a very long time + // so we release the lock + sockAddrToA ( &addr.sa, this->nameTmp, sizeof ( this->nameTmp ) ); + } + + // the ipAddrToAsciiTransactionPrivate destructor is allowed to + // set pCurrent to nill and avoid blocking on a slow DNS + // operation + if ( ! this->pCurrent ) { + continue; + } + + this->callbackInProgress = true; + + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + // dont call callback with lock applied + this->pCurrent->pCB->transactionComplete ( this->nameTmp ); + } + + this->callbackInProgress = false; + + if ( this->pCurrent ) { + this->pCurrent->pending = false; + this->pCurrent = 0; + } + if ( this->cancelPendingCount ) { + this->destructorBlockEvent.signal (); + } + } + } +} + +ipAddrToAsciiTransactionPrivate::ipAddrToAsciiTransactionPrivate + ( ipAddrToAsciiEnginePrivate & engineIn ) : + engine ( engineIn ), pCB ( 0 ), pending ( false ) +{ + memset ( & this->addr, '\0', sizeof ( this->addr ) ); + this->addr.sa.sa_family = AF_UNSPEC; +} + +void ipAddrToAsciiTransactionPrivate::release () +{ + this->~ipAddrToAsciiTransactionPrivate (); + this->engine.transactionFreeList.release ( this ); +} + +ipAddrToAsciiTransactionPrivate::~ipAddrToAsciiTransactionPrivate () +{ + epicsGuard < epicsMutex > guard ( this->engine.mutex ); + while ( this->pending ) { + if ( this->engine.pCurrent == this && + this->engine.callbackInProgress && + ! this->engine.thread.isCurrentThread() ) { + assert ( this->engine.cancelPendingCount < UINT_MAX ); + this->engine.cancelPendingCount++; + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->engine.destructorBlockEvent.wait (); + } + assert ( this->engine.cancelPendingCount > 0u ); + this->engine.cancelPendingCount--; + if ( ! this->pending ) { + if ( this->engine.cancelPendingCount ) { + this->engine.destructorBlockEvent.signal (); + } + break; + } + } + else { + if ( this->engine.pCurrent == this ) { + this->engine.pCurrent = 0; + } + else { + this->engine.labor.remove ( *this ); + } + this->pending = false; + } + } +} + +void ipAddrToAsciiTransactionPrivate::ipAddrToAscii ( + const osiSockAddr & addrIn, ipAddrToAsciiCallBack & cbIn ) +{ + bool success; + + { + epicsGuard < epicsMutex > guard ( this->engine.mutex ); + // put some reasonable limit on queue expansion + if ( !this->pending && engine.labor.count () < 16u ) { + this->addr = addrIn; + this->pCB = & cbIn; + this->pending = true; + this->engine.labor.add ( *this ); + success = true; + } + else { + success = false; + } + } + + if ( success ) { + this->engine.laborEvent.signal (); + } + else { + char autoNameTmp[256]; + sockAddrToDottedIP ( & addrIn.sa, autoNameTmp, + sizeof ( autoNameTmp ) ); + cbIn.transactionComplete ( autoNameTmp ); + } +} + +osiSockAddr ipAddrToAsciiTransactionPrivate::address () const +{ + return this->addr; +} + +void ipAddrToAsciiTransactionPrivate::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > guard ( this->engine.mutex ); + char ipAddr [64]; + sockAddrToDottedIP ( &this->addr.sa, ipAddr, sizeof ( ipAddr ) ); + printf ( "ipAddrToAsciiTransactionPrivate for address %s\n", ipAddr ); + if ( level > 0u ) { + printf ( "\tengine %p\n", + static_cast ( & this->engine ) ); + this->pCB->show ( level - 1u ); + } +} diff --git a/src/libCom/misc/ipAddrToAsciiAsynchronous.h b/src/libCom/misc/ipAddrToAsciiAsynchronous.h new file mode 100644 index 000000000..0e4cfbadb --- /dev/null +++ b/src/libCom/misc/ipAddrToAsciiAsynchronous.h @@ -0,0 +1,59 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef ipAddrToAsciiAsynchronous_h +#define ipAddrToAsciiAsynchronous_h + +#include "osiSock.h" +#include "shareLib.h" + +class epicsShareClass ipAddrToAsciiCallBack { +public: + virtual void transactionComplete ( const char * pHostName ) = 0; + virtual void show ( unsigned level ) const; + virtual ~ipAddrToAsciiCallBack () = 0; +}; + +class epicsShareClass ipAddrToAsciiTransaction { // X aCC 655 +public: + virtual void release () = 0; + virtual void ipAddrToAscii ( const osiSockAddr &, ipAddrToAsciiCallBack & ) = 0; + virtual osiSockAddr address () const = 0; + virtual void show ( unsigned level ) const = 0; +protected: + virtual ~ipAddrToAsciiTransaction () = 0; +}; + +class epicsShareClass ipAddrToAsciiEngine { // X aCC 655 +public: + virtual void release () = 0; + virtual ipAddrToAsciiTransaction & createTransaction () = 0; + virtual void show ( unsigned level ) const = 0; + static ipAddrToAsciiEngine & allocate (); +protected: + virtual ~ipAddrToAsciiEngine () = 0; +}; + +#endif // ifdef ipAddrToAsciiAsynchronous_h diff --git a/src/libCom/misc/locationException.h b/src/libCom/misc/locationException.h new file mode 100644 index 000000000..0fd846f25 --- /dev/null +++ b/src/libCom/misc/locationException.h @@ -0,0 +1,77 @@ +/*************************************************************************\ +* 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 +// + +#ifndef locationException_h +#define locationException_h + +#include + +#include "cantProceed.h" +#include "errlog.h" + +template +class sourceFileLocation : public T { +public: + sourceFileLocation (const T &parm, const char *fileName, unsigned lineNumber); + sourceFileLocation ( const sourceFileLocation & ); + sourceFileLocation & operator = ( const sourceFileLocation & ); + const char *fileName () const; + unsigned lineNumber () const; +private: + const char *pFileName; + unsigned lineNumberCopy; +}; + +template +inline sourceFileLocation::sourceFileLocation (const T &parm, const char *fileName, unsigned lineNumber) : + T ( parm ), pFileName ( fileName ) , lineNumberCopy ( lineNumber ) {} + +template +inline sourceFileLocation::sourceFileLocation ( const sourceFileLocation &in ) : + T ( in ), pFileName ( in.pFileName ), lineNumberCopy ( in.lineNumberCopy ) +{ +} + +template < class T > +inline sourceFileLocation & sourceFileLocation::operator = ( const sourceFileLocation &in ) +{ + this->pFileName = in.pFileName; + this->lineNumberCopy = in.lineNumberCopy; + return *this; +} + +template < class T > +inline unsigned sourceFileLocation::lineNumber () const +{ + return this->lineNumberCopy; +} + +template +inline const char * sourceFileLocation::fileName () const +{ + return this->pFileName; +} + +#define throwWithLocation(parm) throwExceptionWithLocation (parm, __FILE__, __LINE__); + +template +inline void throwExceptionWithLocation (const T &parm, const char *pFileName, unsigned lineNo) +{ + throw sourceFileLocation (parm, pFileName, lineNo); +} + +#endif // ifdef locationException_h + diff --git a/src/libCom/misc/makeEpicsVersion.pl b/src/libCom/misc/makeEpicsVersion.pl new file mode 100644 index 000000000..62dea3d5e --- /dev/null +++ b/src/libCom/misc/makeEpicsVersion.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +use strict; + +my ($ver, $rev, $mod, $patch, $snapshot, $commit_date); + +my ($infile, $outdir, $site_ver) = @ARGV; + +die "Usage: perl makeEpicsVersion.pl CONFIG_BASE_VERSION outdir siteversion" + unless ($infile && $outdir); + +print "Building epicsVersion.h from $infile\n"; + +open my $VARS, '<', $infile + or die "Can't open $infile: $!\n"; + +while (<$VARS>) { + chomp; + next if m/^\s*#/; # Skip comments + if (m/^EPICS_VERSION\s*=\s*(\d+)/) { $ver = $1; } + if (m/^EPICS_REVISION\s*=\s*(\d+)/) { $rev = $1; } + if (m/^EPICS_MODIFICATION\s*=\s*(\d+)/) { $mod = $1; } + if (m/^EPICS_PATCH_LEVEL\s*=\s*(\d+)/) { $patch = $1; } + if (m/^EPICS_DEV_SNAPSHOT\s*=\s*([-\w]*)/) { $snapshot = $1; } + if (m/^COMMIT_DATE\s*=\s*"\\(.*)"/) { $commit_date = $1; } +} +close $VARS; + +map { + die "Variable missing from $infile" unless defined $_; +} $ver, $rev, $mod, $patch, $snapshot, $commit_date; + +my $ver_str = "$ver.$rev.$mod"; +$ver_str .= ".$patch" if $patch > 0; +$ver_str .= $snapshot if $snapshot ne ''; +$ver_str .= "-$site_ver" if $site_ver; + +print "Found EPICS Version $ver_str\n"; + +my $epicsVersion="$outdir/epicsVersion.h"; + +mkdir ($outdir, 0777) unless -d $outdir; + +open my $OUT, '>', $epicsVersion + or die "Cannot create $epicsVersion: $!\n"; + +print $OUT <<"END_OUTPUT"; +/* Generated epicsVersion.h */ + +#define EPICS_VERSION $ver +#define EPICS_REVISION $rev +#define EPICS_MODIFICATION $mod +#define EPICS_PATCH_LEVEL $patch +#define EPICS_DEV_SNAPSHOT "$snapshot" +#define EPICS_SITE_VERSION "$site_ver" +#define EPICS_VERSION_STRING "EPICS $ver_str" +#define epicsReleaseVersion "EPICS R$ver_str $commit_date" + +/* The following names are deprecated, use the equivalent name above */ +#define EPICS_UPDATE_LEVEL EPICS_PATCH_LEVEL +#define EPICS_CVS_SNAPSHOT EPICS_DEV_SNAPSHOT + +END_OUTPUT + +close $OUT; diff --git a/src/libCom/misc/shareLib.h b/src/libCom/misc/shareLib.h new file mode 100644 index 000000000..ba23b282f --- /dev/null +++ b/src/libCom/misc/shareLib.h @@ -0,0 +1,196 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Compiler specific key words to set up external symbols and entry points + * + * Currently this is only necessary for WIN32 DLLs and for VAXC on VMS but + * other systems with similar requirements could be supported. + * + * USAGE: + * There are two distinct classes of keywords in this file: + * + * 1) epicsShareAPI - specifies a multi-language calling mechanism. On windows + * this is the pascal calling convention which is used by visual basic and other + * high level tools. This is only necessary if a C/C++ function needs to be called + * from other languages or from high level tools. The epicsShareAPI keyword + * must be present between the function's returned data type and the function's + * name. All compilers targeting windows accept the __stdcall keyword in this + * location. Functions with variable argument lists should not use the epicsShareAPI + * keyword because __stdcall (pascal) calling convention cannot support variable + * length ed argument lists. + * + * int epicsShareAPI myExtFunc ( int arg ); + * int epicsShareAPI myExtFunc ( int arg ) {} + * + * ** NOTE ** epicsShareAPI is deprecated for new routines, don't use it! + * In a future major release (R3.15) we will remove this keyword + * from most EPICS APIs, although CA may continue to use it. + * This is a major release though, since it affects the order + * that arguments are pushed onto the stack on Windows and we + * don't want a replacement DLL to silent cause mayhem... + * + * 2) epicsShare{Func,Class,Extern,Def} - specifies shareable library (DLL) + * export/import related information in the source code. On windows these keywords + * allow faster dispatching of calls to DLLs because more is known at compile time. + * It is also not necessary to maintain a linker input files specifying the DLL + * entry points. This maintenance can be more laborious with C++ decorated symbol + * names. These keywords are only necessary if the address of a function or data + * internal to a shareable library (DLL) needs to be visible outside of this shareable + * library (DLL). All compilers targeting windows accept the __declspec(dllexport) + * and __declspec(dllimport) keywords. + * + * In header files declare references to externally visible variables, classes and + * functions like this: + * + * #include "shareLib.h" + * epicsShareFunc int myExtFunc ( int arg ); + * epicsShareExtern int myExtVar; + * class epicsShareClass myClass { int func ( void ); }; + * + * In the implementation file, however, you write: + * + * #include + * #define epicsExportSharedSymbols + * #include + * + * epicsShareDef int myExtVar = 4; + * int myExtFunc ( int arg ) {} + * int myClass::func ( void ) {} + * + * By default shareLib.h sets the DLL import / export keywords to import from + * a DLL so that, for DLL consumers (users), nothing special must be done. However, + * DLL implementors must set epicsExportSharedSymbols as above to specify + * which functions are exported from the DLL and which of them are imported + * from other DLLs. + * + * You must first #include what you import and then define epicsExportSharedSymbols + * only right before you #include the prototypes for what you implement! You must + * include shareLib.h again each time that the state of the import/ export keywords + * changes, but this usually occurs as a side effect of including the shareable + * libraries header file(s). + * + * Frequently a header file for a shareable library exported interface will + * have some preprocessor switches like this if this header file must also + * include header files describing interfaces to other shareable libraries. + * + * #ifdef epicsExportSharedSymbols + * # define interfacePDQ_epicsExportSharedSymbols + * # undef epicsExportSharedSymbols + * #endif + * + * #include "epicsTypes.h" + * #include "epicsTime.h" + * + * #ifdef interfacePDQ_epicsExportSharedSymbols + * # define epicsExportSharedSymbols + * # include "shareLib.h" + * #endif + * + * epicsShareFunc int myExtFunc ( int arg ); + * epicsShareExtern int myExtVar; + * class epicsShareClass myClass {}; + * + * Fortunately, the above is only the concern of library authors and will have no + * impact on persons using functions and or external data from a library. + */ + +#undef epicsShareExtern +#undef epicsShareDef +#undef epicsShareClass +#undef epicsShareFunc +#undef epicsShareAPI +#undef READONLY + +/* + * + * Also check for "EPICS_DLL_NO" not defined so that we will not use these + * keywords if it is an object library build of base under WIN32. + */ +#if defined(_WIN32) || defined(__CYGWIN32__) + +# if defined(epicsExportSharedSymbols) +# if defined(EPICS_DLL_NO) /* this indicates that we are not building a DLL */ +# define epicsShareExtern extern +# define epicsShareClass +# define epicsShareFunc +# else +# define epicsShareExtern __declspec(dllexport) extern +# define epicsShareClass __declspec(dllexport) +# define epicsShareFunc __declspec(dllexport) +# endif +# else +# if defined(_DLL) /* this indicates that we are being compiled to call DLLs */ +# define epicsShareExtern __declspec(dllimport) extern +# define epicsShareClass __declspec(dllimport) +# define epicsShareFunc __declspec(dllimport) +# else +# define epicsShareExtern extern +# define epicsShareClass +# define epicsShareFunc +# endif +# endif +# define epicsShareDef +# define epicsShareAPI __stdcall /* function removes arguments */ +# define READONLY const +/* + * if its the old VAX C Compiler (not DEC C) + */ +#elif defined(VAXC) + + /* + * VAXC creates FORTRAN common blocks when + * we use "extern int fred"/"int fred=4". Therefore, + * the initialization is not loaded unless we + * call a function in that object module. + * + * DEC CXX does not have this problem. + * We suspect (but do not know) that DEC C + * also does not have this problem. + */ +# define epicsShareExtern globalref +# define epicsShareDef globaldef +# define READONLY const +# define epicsShareClass +# define epicsShareFunc +# define epicsShareAPI + +#else + +/* else => no import/export specifiers */ + +# define epicsShareExtern extern +# define epicsShareAPI +# define epicsShareClass +# define epicsShareDef + +# define epicsShareFunc +# if defined(__STDC__) || defined (__cplusplus) +# define READONLY const +# else +# define READONLY +# endif + +#endif + +#ifndef INLINE_defs_EPICS +#define INLINE_defs_EPICS +# ifndef __cplusplus +# if defined (__GNUC__) +# define INLINE static __inline__ +# elif defined (_MSC_VER) +# define INLINE __inline +# elif defined (_SUNPRO_C) +# pragma error_messages (off, E_EXTERN_PRIOR_REDECL_STATIC) +# define INLINE static +# else +# define INLINE static +# endif +# endif /* ifndef __cplusplus */ +#endif /* ifdef INLINE_defs_EPICS */ diff --git a/src/libCom/misc/testMain.h b/src/libCom/misc/testMain.h new file mode 100644 index 000000000..5681a6ceb --- /dev/null +++ b/src/libCom/misc/testMain.h @@ -0,0 +1,46 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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 */ + +#ifndef INC_testMain_H +#define INC_testMain_H + +/* This header defines a convenience macro for use by pure test programs. + * A pure test program cannot take any arguments since it must be fully + * automatable. If your program needs to use argv/argc, it may be doing + * measurements not unit and/or regression testing. On Host architectures + * these programs needs to be named main and take dummy argc/argv args, + * but on vxWorks and RTEMS they must be named as the test program. + * + * Use this macro as follows: + * + * #include "testMain.h" + * #include "epicsUnitTest.h" + * + * MAIN(myProgTest) { + * testPlan(...); + * testOk(...) + * return testDone(); + * } + */ + +#if defined(vxWorks) || defined(__rtems__) + #ifdef __cplusplus + #define MAIN(prog) extern "C" int prog(void) + #else + #define MAIN(prog) int prog() + #endif +#else + #ifdef __cplusplus + #define MAIN(prog) int main(int /*argc*/, char * /*argv*/ [] ) + #else + #define MAIN(prog) int main(int argc, char *argv[] ) + #endif +#endif + + +#endif /* INC_testMain_H */ diff --git a/src/libCom/misc/truncateFile.c b/src/libCom/misc/truncateFile.c new file mode 100644 index 000000000..9ff2455ec --- /dev/null +++ b/src/libCom/misc/truncateFile.c @@ -0,0 +1,136 @@ +/*************************************************************************\ +* 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 +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +/* + * truncate to specified size (we dont use truncate() + * because it is not portable) + */ +epicsShareFunc enum TF_RETURN truncateFile (const char *pFileName, unsigned size) +{ + char tmpName[256>L_tmpnam?256:L_tmpnam]; + long filePos; + FILE *pFile; + FILE *ptmp; + int status; + int c; + unsigned charNo; + + /* + * see cast of size to long below + */ + if (size>LONG_MAX) { + return TF_ERROR; + } + + pFile = fopen(pFileName, "r"); + if (!pFile) { + fprintf (stderr, + "File access problems to `%s' because `%s'\n", + pFileName, + strerror(errno)); + return TF_ERROR; + } + + /* + * This is not required under UNIX but does appear + * to be required under WIN32. + */ + status = fseek (pFile, 0L, SEEK_END); + if (status!=TF_OK) { + fclose (pFile); + return TF_ERROR; + } + + filePos = ftell(pFile); + if (filePos <= (long) size) { + fclose (pFile); + return TF_OK; + } + + epicsTempName ( tmpName, sizeof (tmpName) ); + if ( tmpName[0] == '\0' ) { + fprintf (stderr,"Unable to create tmp file name?\n"); + fclose (pFile); + return TF_ERROR; + } + + ptmp = fopen (tmpName, "w"); + if (!ptmp) { + fprintf (stderr, + "File access problems to `%s' because `%s'\n", + tmpName, + strerror(errno)); + fclose (pFile); + return TF_ERROR; + } + rewind (pFile); + charNo = 0u; + while (charNosizeof(double)?sizeof(double)-1:sizeof(CTYPE)-1) + +/* + * pointer aligned test + * (returns true if the pointer is on the worst case alignemnt + * boundary for its type) + */ +#define devPtrAlignTest(PTR) (!(devCreateAlignmentMask(*PTR)&(long)(PTR))) + +/* + * error codes (and messages) associated with devLib.c + */ +#define S_dev_success 0 +#define S_dev_vectorInUse (M_devLib| 1) /*interrupt vector in use*/ +#define S_dev_vecInstlFail (M_devLib| 2) /*interrupt vector install failed*/ +#define S_dev_uknIntType (M_devLib| 3) /*Unrecognized interrupt type*/ +#define S_dev_vectorNotInUse (M_devLib| 4) /*Interrupt vector not in use by caller*/ +#define S_dev_badA16 (M_devLib| 5) /*Invalid VME A16 address*/ +#define S_dev_badA24 (M_devLib| 6) /*Invalid VME A24 address*/ +#define S_dev_badA32 (M_devLib| 7) /*Invalid VME A32 address*/ +#define S_dev_uknAddrType (M_devLib| 8) /*Unrecognized address space type*/ +#define S_dev_addressOverlap (M_devLib| 9) /*Specified device address overlaps another device*/ +#define S_dev_identifyOverlap (M_devLib| 10) /*This device already owns the address range*/ +#define S_dev_addrMapFail (M_devLib| 11) /*unable to map address*/ +#define S_dev_intDisconnect (M_devLib| 12) /*Interrupt at vector disconnected from an EPICS device*/ +#define S_dev_internal (M_devLib| 13) /*Internal failure*/ +#define S_dev_intEnFail (M_devLib| 14) /*unable to enable interrupt level*/ +#define S_dev_intDissFail (M_devLib| 15) /*unable to disable interrupt level*/ +#define S_dev_noMemory (M_devLib| 16) /*Memory allocation failed*/ +#define S_dev_addressNotFound (M_devLib| 17) /*Specified device address unregistered*/ +#define S_dev_noDevice (M_devLib| 18) /*No device at specified address*/ +#define S_dev_wrongDevice (M_devLib| 19) /*Wrong device type found at specified address*/ +#define S_dev_badSignalNumber (M_devLib| 20) /*Signal number (offset) to large*/ +#define S_dev_badSignalCount (M_devLib| 21) /*Signal count to large*/ +#define S_dev_badRequest (M_devLib| 22) /*Device does not support requested operation*/ +#define S_dev_highValue (M_devLib| 23) /*Parameter to high*/ +#define S_dev_lowValue (M_devLib| 24) /*Parameter to low*/ +#define S_dev_multDevice (M_devLib| 25) /*Specified address is ambiguous (more than one device responds)*/ +#define S_dev_badSelfTest (M_devLib| 26) /*Device self test failed*/ +#define S_dev_badInit (M_devLib| 27) /*Device failed during initialization*/ +#define S_dev_hdwLimit (M_devLib| 28) /*Input exceeds Hardware Limit*/ +#define S_dev_deviceDoesNotFit (M_devLib| 29) /*Unable to locate address space for device*/ +#define S_dev_deviceTMO (M_devLib| 30) /*device timed out*/ +#define S_dev_badFunction (M_devLib| 31) /*bad function pointer*/ +#define S_dev_badVector (M_devLib| 32) /*bad interrupt vector*/ +#define S_dev_badArgument (M_devLib| 33) /*bad function argument*/ +#define S_dev_badISA (M_devLib| 34) /*Invalid ISA address*/ +#define S_dev_badCRCSR (M_devLib| 35) /*Invalid VME CR/CSR address*/ +#define S_dev_vxWorksIntEnFail S_dev_intEnFail + + +#endif /* EPICSDEVLIB_H */ + +/* + * Retain compatibility by including VME by default + */ +#ifndef NO_DEVLIB_COMPAT +# include "devLibVME.h" +#endif diff --git a/src/libCom/osi/devLibVME.c b/src/libCom/osi/devLibVME.c new file mode 100644 index 000000000..4295c6439 --- /dev/null +++ b/src/libCom/osi/devLibVME.c @@ -0,0 +1,1159 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLib.c - support for allocation of common device resources */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + * + * NOTES: + * .01 06-14-93 joh needs devAllocInterruptVector() routine + */ + +static const char sccsID[] = "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd"; + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "ellLib.h" + +#define NO_DEVLIB_COMPAT +#include "devLibVME.h" +#include "devLibVMEImpl.h" + +static ELLLIST addrAlloc[atLast]; +static ELLLIST addrFree[atLast]; + +static size_t addrLast[atLast] = { + 0xffff, + 0xffffff, + 0xffffffff, + 0xffffff, + 0xffffff, + }; + +static unsigned addrHexDig[atLast] = { + 4, + 6, + 8, + 6, + 6 + }; + +static long addrFail[atLast] = { + S_dev_badA16, + S_dev_badA24, + S_dev_badA32, + S_dev_badISA, + S_dev_badCRCSR + }; + +static epicsMutexId addrListLock; +static char devLibInitFlag; + +const char *epicsAddressTypeName[] + = { + "VME A16", + "VME A24", + "VME A32", + "ISA", + "VME CR/CSR" + }; + +typedef struct{ + ELLNODE node; + const char *pOwnerName; + volatile void *pPhysical; + /* + * first, last is used here instead of base, size + * so that we can store a block that is the maximum size + * available in type size_t + */ + size_t begin; + size_t end; +}rangeItem; + +/* + * These routines are not exported + */ + +static long devLibInit(void); + +static long addrVerify( + epicsAddressType addrType, + size_t base, + size_t size); + +static long blockFind ( + epicsAddressType addrType, + const rangeItem *pRange, + /* size needed */ + size_t requestSize, + /* n ls bits zero in base addr */ + unsigned alignment, + /* base address found */ + size_t *pFirst); + +static void report_conflict( + epicsAddressType addrType, + size_t base, + size_t size, + const char *pOwnerName); + +static void report_conflict_device( + epicsAddressType addrType, + const rangeItem *pRange); + +static void devInsertAddress( + ELLLIST *pRangeList, + rangeItem *pNewRange); + +static long devListAddressMap( + ELLLIST *pRangeList); + +static long devCombineAdjacentBlocks( + ELLLIST *pRangeList, + rangeItem *pRange); + +static long devInstallAddr( + rangeItem *pRange, /* item on the free list to be split */ + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress); + +#define SUCCESS 0 + +/* + * devBusToLocalAddr() + */ +long devBusToLocalAddr( + epicsAddressType addrType, + size_t busAddr, + volatile void **ppLocalAddress) +{ + long status; + volatile void *localAddress; + + /* + * Make sure that devLib has been intialized + */ + if (!devLibInitFlag) { + status = devLibInit(); + if(status){ + return status; + } + } + + /* + * Make sure we have a valid bus address + */ + status = addrVerify (addrType, busAddr, 4); + if (status) { + return status; + } + + /* + * Call the virtual os routine to map the bus address to a CPU address + */ + status = (*pdevLibVME->pDevMapAddr) (addrType, 0, busAddr, 4, &localAddress); + if (status) { + errPrintf (status, __FILE__, __LINE__, "%s bus address =0X%X\n", + epicsAddressTypeName[addrType], (unsigned int)busAddr); + return status; + } + + /* + * Return the local CPU address if the pointer is supplied + */ + if (ppLocalAddress) { + *ppLocalAddress = localAddress; + } + + return SUCCESS; + +}/*end devBusToLocalAddr()*/ + + +/* + * devRegisterAddress() + */ +long devRegisterAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress) +{ + rangeItem *pRange; + long s; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s){ + return s; + } + } + + s = addrVerify (addrType, base, size); + if (s) { + return s; + } + + if (size == 0) { + return S_dev_lowValue; + } + +#ifdef DEBUG + printf ("Req Addr 0X%X Size 0X%X\n", base, size); +#endif + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst(&addrFree[addrType]); + while (TRUE) { + if (pRange->begin > base) { + pRange = NULL; +# ifdef DEBUG + printf ("Unable to locate a free block\n"); + devListAddressMap (&addrFree[addrType]); +# endif + break; + } + else if (base + (size - 1) <= pRange->end) { +# ifdef DEBUG + printf ("Found free block Begin 0X%X End 0X%X\n", + pRange->begin, pRange->end); +# endif + break; + } + + pRange = (rangeItem *) ellNext (&pRange->node); + } + epicsMutexUnlock(addrListLock); + + if (pRange==NULL) { + report_conflict (addrType, base, size, pOwnerName); + return S_dev_addressOverlap; + } + + s = devInstallAddr( + pRange, /* item on the free list to be split */ + pOwnerName, + addrType, + base, + size, + ppPhysicalAddress); + + return s; +} + +/* + * devReadProbe() + * + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevReadProbe) (wordSize, ptr, pValue); +} + +/* + * devWriteProbe + * + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +long devWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevWriteProbe) (wordSize, ptr, pValue); +} + +/* + * devInstallAddr() + */ +static long devInstallAddr ( + rangeItem *pRange, /* item on the free list to be split */ + const char *pOwnerName, + epicsAddressType addrType, + size_t base, + size_t size, + volatile void **ppPhysicalAddress) +{ + volatile void *pPhysicalAddress; + rangeItem *pNewRange; + size_t reqEnd = base + (size-1); + long status; + + /* + * does it start below the specified block + */ + if (base < pRange->begin) { + return S_dev_badArgument; + } + + /* + * does it end above the specified block + */ + if (reqEnd > pRange->end) { + return S_dev_badArgument; + } + + /* + * always map through the virtual os in case the memory + * management is set up there + */ + status = (*pdevLibVME->pDevMapAddr) (addrType, 0, base, + size, &pPhysicalAddress); + if (status) { + errPrintf (status, __FILE__, __LINE__, "%s base=0X%X size = 0X%X", + epicsAddressTypeName[addrType], (unsigned int)base, (unsigned int)size); + return status; + } + + /* + * set the callers variable if the pointer is supplied + */ + if (ppPhysicalAddress) { + *ppPhysicalAddress = pPhysicalAddress; + } + + /* + * does it start at the beginning of the block + */ + if (pRange->begin == base) { + if (pRange->end == reqEnd) { + epicsMutexMustLock(addrListLock); + ellDelete(&addrFree[addrType], &pRange->node); + epicsMutexUnlock(addrListLock); + free ((void *)pRange); + } + else { + pRange->begin = base + size; + } + } + /* + * does it end at the end of the block + */ + else if (pRange->end == reqEnd) { + pRange->end = base-1; + } + /* + * otherwise split the item on the free list + */ + else { + + pNewRange = (rangeItem *) calloc (1, sizeof(*pRange)); + if(!pNewRange){ + return S_dev_noMemory; + } + + pNewRange->begin = base + size; + pNewRange->end = pRange->end; + pNewRange->pOwnerName = ""; + pNewRange->pPhysical = NULL; + pRange->end = base - 1; + + /* + * add the node after the old item on the free list + * (blocks end up ordered by address) + */ + epicsMutexMustLock(addrListLock); + ellInsert(&addrFree[addrType], &pRange->node, &pNewRange->node); + epicsMutexUnlock(addrListLock); + } + + /* + * allocate a new address range entry and add it to + * the list + */ + pNewRange = (rangeItem *)calloc (1, sizeof(*pRange)); + if (!pNewRange) { + return S_dev_noMemory; + } + + pNewRange->begin = base; + pNewRange->end = reqEnd; + pNewRange->pOwnerName = pOwnerName; + pNewRange->pPhysical = pPhysicalAddress; + + devInsertAddress (&addrAlloc[addrType], pNewRange); + + return SUCCESS; +} + +/* + * report_conflict() + */ +static void report_conflict ( + epicsAddressType addrType, + size_t base, + size_t size, + const char *pOwnerName +) +{ + const rangeItem *pRange; + + errPrintf ( + S_dev_addressOverlap, + __FILE__, + __LINE__, + "%10s 0X%08X - OX%08X Requested by %s", + epicsAddressTypeName[addrType], + (unsigned int)base, + (unsigned int)(base+size-1), + pOwnerName); + + pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); + while (pRange) { + + if (pRange->begin <= base + (size-1) && pRange->end >= base) { + report_conflict_device (addrType, pRange); + } + + pRange = (rangeItem *) pRange->node.next; + } +} + +/* + * report_conflict_device() + */ +static void report_conflict_device(epicsAddressType addrType, const rangeItem *pRange) +{ + errPrintf ( + S_dev_identifyOverlap, + __FILE__, + __LINE__, + "%10s 0X%08X - 0X%08X Owned by %s", + epicsAddressTypeName[addrType], + (unsigned int)pRange->begin, + (unsigned int)pRange->end, + pRange->pOwnerName); +} + +/* + * devUnregisterAddress() + */ +long devUnregisterAddress( + epicsAddressType addrType, + size_t baseAddress, + const char *pOwnerName) +{ + rangeItem *pRange; + int s; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s) { + return s; + } + } + + s = addrVerify (addrType, baseAddress, 1); + if (s != SUCCESS) { + return s; + } + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst(&addrAlloc[addrType]); + while (pRange) { + if (pRange->begin == baseAddress) { + break; + } + if (pRange->begin > baseAddress) { + pRange = NULL; + break; + } + pRange = (rangeItem *) ellNext(&pRange->node); + } + epicsMutexUnlock(addrListLock); + + if (!pRange) { + return S_dev_addressNotFound; + } + + if (strcmp(pOwnerName,pRange->pOwnerName)) { + s = S_dev_addressOverlap; + errPrintf ( + s, + __FILE__, + __LINE__, + "unregister address for %s at 0X%X failed because %s owns it", + pOwnerName, + (unsigned int)baseAddress, + pRange->pOwnerName); + return s; + } + + epicsMutexMustLock(addrListLock); + ellDelete (&addrAlloc[addrType], &pRange->node); + epicsMutexUnlock(addrListLock); + + pRange->pOwnerName = ""; + devInsertAddress (&addrFree[addrType], pRange); + s = devCombineAdjacentBlocks (&addrFree[addrType], pRange); + if(s){ + errMessage (s, "devCombineAdjacentBlocks error"); + return s; + } + + return SUCCESS; +} + +/* + * devCombineAdjacentBlocks() + */ +static long devCombineAdjacentBlocks( + ELLLIST *pRangeList, + rangeItem *pRange) +{ + rangeItem *pBefore; + rangeItem *pAfter; + + pBefore = (rangeItem *) ellPrevious (&pRange->node); + pAfter = (rangeItem *) ellNext (&pRange->node); + + /* + * combine adjacent blocks + */ + if (pBefore) { + if (pBefore->end == pRange->begin-1) { + epicsMutexMustLock(addrListLock); + pRange->begin = pBefore->begin; + ellDelete (pRangeList, &pBefore->node); + epicsMutexUnlock(addrListLock); + free ((void *)pBefore); + } + } + + if (pAfter) { + if (pAfter->begin == pRange->end+1) { + epicsMutexMustLock(addrListLock); + pRange->end = pAfter->end; + ellDelete (pRangeList, &pAfter->node); + epicsMutexUnlock(addrListLock); + free((void *)pAfter); + } + } + + return SUCCESS; +} + +/* + * devInsertAddress() + */ +static void devInsertAddress( +ELLLIST *pRangeList, +rangeItem *pNewRange) +{ + rangeItem *pBefore; + rangeItem *pAfter; + + epicsMutexMustLock(addrListLock); + pAfter = (rangeItem *) ellFirst (pRangeList); + while (pAfter) { + if (pNewRange->end < pAfter->begin) { + break; + } + pAfter = (rangeItem *) ellNext (&pAfter->node); + } + + if (pAfter) { + pBefore = (rangeItem *) ellPrevious (&pAfter->node); + ellInsert (pRangeList, &pBefore->node, &pNewRange->node); + } + else { + ellAdd (pRangeList, &pNewRange->node); + } + epicsMutexUnlock(addrListLock); +} + +/* + * devAllocAddress() + */ +long devAllocAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t size, + unsigned alignment, /* n ls bits zero in base addr*/ + volatile void ** pLocalAddress ) +{ + int s; + rangeItem *pRange; + size_t base = 0; + + if (!devLibInitFlag) { + s = devLibInit(); + if(s){ + return s; + } + } + + s = addrVerify (addrType, 0, size); + if(s){ + return s; + } + + if (size == 0) { + return S_dev_lowValue; + } + + epicsMutexMustLock(addrListLock); + pRange = (rangeItem *) ellFirst (&addrFree[addrType]); + while (pRange) { + if ((pRange->end - pRange->begin) + 1 >= size){ + s = blockFind ( + addrType, + pRange, + size, + alignment, + &base); + if (s==SUCCESS) { + break; + } + } + pRange = (rangeItem *) pRange->node.next; + } + epicsMutexUnlock(addrListLock); + + if(!pRange){ + s = S_dev_deviceDoesNotFit; + errMessage(s, epicsAddressTypeName[addrType]); + return s; + } + + s = devInstallAddr (pRange, pOwnerName, addrType, base, + size, pLocalAddress); + + return s; +} + +/* + * addrVerify() + * + * care has been taken here not to overflow type size_t + */ +static long addrVerify(epicsAddressType addrType, size_t base, size_t size) +{ + if (addrType>=atLast) { + return S_dev_uknAddrType; + } + + if (size == 0) { + return addrFail[addrType]; + } + + if (size-1 > addrLast[addrType]) { + return addrFail[addrType]; + } + + if (base > addrLast[addrType]) { + return addrFail[addrType]; + } + + if (size - 1 > addrLast[addrType] - base) { + return addrFail[addrType]; + } + + return SUCCESS; +} + +/* + * devLibInit() + */ +static long devLibInit (void) +{ + rangeItem *pRange; + int i; + + + if(devLibInitFlag) return(SUCCESS); + if(!pdevLibVME) { + epicsPrintf ("pdevLibVME is NULL\n"); + return S_dev_internal; + } + + if (NELEMENTS(addrAlloc) != NELEMENTS(addrFree)) { + return S_dev_internal; + } + + addrListLock = epicsMutexMustCreate(); + + epicsMutexMustLock(addrListLock); + for (i=0; ipOwnerName = ""; + pRange->pPhysical = NULL; + pRange->begin = 0; + pRange->end = addrLast[i]; + ellAdd (&addrFree[i], &pRange->node); + } + epicsMutexUnlock(addrListLock); + devLibInitFlag = TRUE; + return pdevLibVME->pDevInit(); +} + +/* + * devAddressMap() + */ +long devAddressMap(void) +{ + return devListAddressMap(addrAlloc); +} + +/* + * devListAddressMap() + */ +static long devListAddressMap(ELLLIST *pRangeList) +{ + rangeItem *pri; + int i; + long s; + + if (!devLibInitFlag) { + s = devLibInit (); + if (s) { + return s; + } + } + + epicsMutexMustLock(addrListLock); + for (i=0; ibegin, + addrHexDig[i], + (unsigned long) pri->end, + pri->pPhysical, + pri->pOwnerName); + pri = (rangeItem *) ellNext (&pri->node); + } + } + epicsMutexUnlock(addrListLock); + + return SUCCESS; +} + + +/* + * + * blockFind() + * + * Find unoccupied block in a large block + * + */ +static long blockFind ( + epicsAddressType addrType, + const rangeItem *pRange, + /* size needed */ + size_t requestSize, + /* n ls bits zero in base addr */ + unsigned alignment, + /* base address found */ + size_t *pBase) +{ + int s = SUCCESS; + size_t bb; + size_t mask; + size_t newBase; + size_t newSize; + + /* + * align the block base + */ + mask = devCreateMask (alignment); + newBase = pRange->begin; + if ( mask & newBase ) { + newBase |= mask; + newBase++; + } + + if ( requestSize == 0) { + return S_dev_badRequest; + } + + /* + * align size of block + */ + newSize = requestSize; + if (mask & newSize) { + newSize |= mask; + newSize++; + } + + if (pRange->end - pRange->begin + 1 < newSize) { + return S_dev_badRequest; + } + + bb = pRange->begin; + while (bb <= (pRange->end + 1) - newSize) { + s = devNoResponseProbe (addrType, bb, newSize); + if (s==SUCCESS) { + *pBase = bb; + return SUCCESS; + } + bb += newSize; + } + + return s; +} + +/* + * devNoResponseProbe() + */ +long devNoResponseProbe (epicsAddressType addrType, + size_t base, size_t size) +{ + volatile void *pPhysical; + size_t probe; + size_t byteNo; + unsigned wordSize; + union { + char charWord; + short shortWord; + int intWord; + long longWord; + }allWordSizes; + long s; + + if (!devLibInitFlag) { + s = devLibInit(); + if (s) { + return s; + } + } + + byteNo = 0; + while (byteNo < size) { + + probe = base + byteNo; + + /* + * for all word sizes + */ + for (wordSize=1; wordSize<=sizeof(allWordSizes); wordSize <<= 1) { + /* + * only check naturally aligned words + */ + if ( (probe&(wordSize-1)) != 0 ) { + break; + } + + if (byteNo+wordSize>size) { + break; + } + + /* + * every byte in the block must + * map to a physical address + */ + s = (*pdevLibVME->pDevMapAddr) (addrType, 0, probe, wordSize, &pPhysical); + if (s!=SUCCESS) { + return s; + } + + /* + * verify that no device is present + */ + s = (*pdevLibVME->pDevReadProbe)(wordSize, pPhysical, &allWordSizes); + if (s==SUCCESS) { + return S_dev_addressOverlap; + } + } + byteNo++; + } + return SUCCESS; +} + +long devConnectInterruptVME( +unsigned vectorNumber, +void (*pFunction)(void *), +void *parameter ) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, + pFunction, parameter); +} + +long devDisconnectInterruptVME( +unsigned vectorNumber, +void (*pFunction)(void *) ) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevDisconnectInterruptVME) (vectorNumber, pFunction); +} + +int devInterruptInUseVME (unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevInterruptInUseVME) (level); +} + +long devEnableInterruptLevelVME (unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevEnableInterruptLevelVME) (level); +} + +long devDisableInterruptLevelVME (unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + return (*pdevLibVME->pDevDisableInterruptLevelVME) (level); +} + +/* + * devConnectInterrupt () + * + * !! DEPRECATED !! + */ +long devConnectInterrupt( +epicsInterruptType intType, +unsigned vectorNumber, +void (*pFunction)(void *), +void *parameter) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevConnectInterruptVME) (vectorNumber, + pFunction, parameter); + default: + return S_dev_uknIntType; + } +} + +/* + * + * devDisconnectInterrupt() + * + * !! DEPRECATED !! + */ +long devDisconnectInterrupt( +epicsInterruptType intType, +unsigned vectorNumber, +void (*pFunction)(void *) +) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevDisconnectInterruptVME) (vectorNumber, + pFunction); + default: + return S_dev_uknIntType; + } +} + +/* + * devEnableInterruptLevel() + * + * !! DEPRECATED !! + */ +long devEnableInterruptLevel( +epicsInterruptType intType, +unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevEnableInterruptLevelVME) (level); + default: + return S_dev_uknIntType; + } +} + +/* + * devDisableInterruptLevel() + * + * !! DEPRECATED !! + */ +long devDisableInterruptLevel ( +epicsInterruptType intType, +unsigned level) +{ + long status; + + if (!devLibInitFlag) { + status = devLibInit(); + if (status) { + return status; + } + } + + switch(intType){ + case intVME: + case intVXI: + return (*pdevLibVME->pDevDisableInterruptLevelVME) (level); + default: + return S_dev_uknIntType; + } +} + +/* + * locationProbe + * + * !! DEPRECATED !! + */ +long locationProbe (epicsAddressType addrType, char *pLocation) +{ + return devNoResponseProbe (addrType, (size_t) pLocation, sizeof(long)); +} + +/****************************************************************************** + * + * The follwing may, or may not be present in the BSP for the CPU in use. + * + */ +/****************************************************************************** + * + * Routines to use to allocate and free memory present in the A24 region. + * + ******************************************************************************/ + +int devLibA24Debug = 0; /* Debugging flag */ + +void *devLibA24Calloc(size_t size) +{ + void *ret; + + ret = devLibA24Malloc(size); + + if (ret == NULL) + return (NULL); + + memset(ret, 0x00, size); + return(ret); +} + +void *devLibA24Malloc(size_t size) +{ + void *ret; + + if (devLibA24Debug) + epicsPrintf ("devLibA24Malloc(%u) entered\n", (unsigned int)size); + + ret = pdevLibVME->pDevA24Malloc(size); + return(ret); +} + +void devLibA24Free(void *pBlock) +{ + if (devLibA24Debug) + epicsPrintf("devLibA24Free(%p) entered\n", pBlock); + + pdevLibVME->pDevA24Free(pBlock); +} diff --git a/src/libCom/osi/devLibVME.h b/src/libCom/osi/devLibVME.h new file mode 100644 index 000000000..6d7476098 --- /dev/null +++ b/src/libCom/osi/devLibVME.h @@ -0,0 +1,312 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLib.h */ +/* devLib.h,v 1.1.2.8 2009/07/09 15:27:43 anj Exp */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + */ + +#ifndef INCdevLibh +#define INCdevLibh 1 + +#include "dbDefs.h" +#include "osdVME.h" +#include "errMdef.h" +#include "shareLib.h" +#include "devLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * epdevAddressType & EPICStovxWorksAddrType + * devLib.c must change in unison + */ +typedef enum { + atVMEA16, + atVMEA24, + atVMEA32, + atISA, /* memory mapped ISA access (until now only on PC) */ + atVMECSR, /* VME-64 CR/CSR address space */ + atLast /* atLast must be the last enum in this list */ + } epicsAddressType; + +/* + * pointer to an array of strings for each of + * the above address types + */ +epicsShareExtern const char *epicsAddressTypeName[]; + +#ifdef __cplusplus +} +#endif + +/* + * To retain compatibility include everything by default + */ +#ifndef NO_DEVLIB_COMPAT +# include "devLibVMEImpl.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * General API + * + * This section applies to all bus types + */ + +epicsShareFunc long devAddressMap(void); /* print an address map */ + +/* + * devBusToLocalAddr() + * + * OSI routine to translate bus addresses their local CPU address mapping + */ +epicsShareFunc long devBusToLocalAddr ( + epicsAddressType addrType, + size_t busAddr, + volatile void **ppLocalAddr); +/* + * devReadProbe() + * + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +epicsShareFunc long devReadProbe ( + unsigned wordSize, volatile const void *ptr, void *pValueRead); + +/* + * devNoResponseProbe() + * + * Verifies that no devices respond at naturally aligned words + * within the specified address range. Return success if no devices + * respond. Returns an error if a device does respond or if + * a physical address for a naturally aligned word cant be mapped. + * Checks all naturally aligned word sizes between char and long for + * the entire specified range of bytes. + */ +epicsShareFunc long devNoResponseProbe( + epicsAddressType addrType, + size_t base, + size_t size +); + +/* + * devWriteProbe + * + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +epicsShareFunc long devWriteProbe ( + unsigned wordSize, volatile void *ptr, const void *pValueWritten); + +epicsShareFunc long devRegisterAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t logicalBaseAddress, + size_t size, /* bytes */ + volatile void **pPhysicalAddress); + +epicsShareFunc long devUnregisterAddress( + epicsAddressType addrType, + size_t logicalBaseAddress, + const char *pOwnerName); + +/* + * allocate and register an unoccupied address block + */ +epicsShareFunc long devAllocAddress( + const char *pOwnerName, + epicsAddressType addrType, + size_t size, + unsigned alignment, /*n ls bits zero in addr*/ + volatile void **pLocalAddress); + +/* + * VME API + * + * Functions in this section apply only to the VME bus type + */ + +/* + * connect ISR to a VME interrupt vector + */ +epicsShareFunc long devConnectInterruptVME( + unsigned vectorNumber, + void (*pFunction)(void *), + void *parameter); + +/* + * disconnect ISR from a VME interrupt vector + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from inadvertently + * removing an interrupt handler that it didn't install + */ +epicsShareFunc long devDisconnectInterruptVME( + unsigned vectorNumber, + void (*pFunction)(void *)); + +/* + * determine if a VME interrupt vector is in use + * + * returns boolean + */ +epicsShareFunc int devInterruptInUseVME (unsigned vectorNumber); + +/* + * enable VME interrupt level + */ +epicsShareFunc long devEnableInterruptLevelVME (unsigned level); + +/* + * disable VME interrupt level + */ +epicsShareFunc long devDisableInterruptLevelVME (unsigned level); + +/* + * Routines to allocate and free memory in the A24 memory region. + * + */ +epicsShareFunc void *devLibA24Malloc(size_t); +epicsShareFunc void *devLibA24Calloc(size_t); +epicsShareFunc void devLibA24Free(void *pBlock); + +/* + * ISA API + * + * Functions in this section apply only to the ISA bus type + */ + +/* + * connect ISR to an ISA interrupt level + * (not implemented) + * (API should be reviewed) + */ +epicsShareFunc long devConnectInterruptISA( + unsigned interruptLevel, + void (*pFunction)(void *), + void *parameter); + +/* + * disconnect ISR from an ISA interrupt level + * (not implemented) + * (API should be reviewed) + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from inadvertently + * removing an interrupt handler that it didn't install + */ +epicsShareFunc long devDisconnectInterruptISA( + unsigned interruptLevel, + void (*pFunction)(void *)); + +/* + * determine if an ISA interrupt level is in use + * (not implemented) + * + * returns boolean + */ +epicsShareFunc int devInterruptLevelInUseISA (unsigned interruptLevel); + +/* + * enable ISA interrupt level + */ +epicsShareFunc long devEnableInterruptLevelISA (unsigned level); + +/* + * disable ISA interrupt level + */ +epicsShareFunc long devDisableInterruptLevelISA (unsigned level); + +/* + * Deprecated interface + */ + +#ifndef NO_DEVLIB_OLD_INTERFACE + +typedef enum {intVME, intVXI, intISA} epicsInterruptType; + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devConnectInterruptVME, devConnectInterruptPCI, + * devConnectInterruptISA etc. devConnectInterrupt will be removed + * in a future release. + */ +epicsShareFunc long devConnectInterrupt( + epicsInterruptType intType, + unsigned vectorNumber, + void (*pFunction)(void *), + void *parameter); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devDisconnectInterruptVME, devDisconnectInterruptPCI, + * devDisconnectInterruptISA etc. devDisconnectInterrupt will be removed + * in a future release. + */ +epicsShareFunc long devDisconnectInterrupt( + epicsInterruptType intType, + unsigned vectorNumber, + void (*pFunction)(void *)); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devEnableInterruptLevelVME, devEnableInterruptLevelPCI, + * devEnableInterruptLevelISA etc. devEnableInterruptLevel will be removed + * in a future release. + */ +epicsShareFunc long devEnableInterruptLevel( + epicsInterruptType intType, unsigned level); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use one of devDisableInterruptLevelVME, devDisableInterruptLevelISA, + * devDisableInterruptLevelPCI etc. devDisableInterruptLevel will be removed + * in a future release. + */ +epicsShareFunc long devDisableInterruptLevel ( + epicsInterruptType intType, unsigned level); + +/* + * NOTE: this routine has been deprecated. It exists + * for backwards compatibility purposes only. + * + * Please use devNoResponseProbe(). locationProbe() will be removed + * in a future release. + */ +epicsShareFunc long locationProbe (epicsAddressType addrType, char *pLocation); + +#endif /* NO_DEVLIB_OLD_INTERFACE */ + +/* + * Some vxWorks convenience routines + */ +void bcopyLongs(char *source, char *destination, int nlongs); + +#ifdef __cplusplus +} +#endif + +#endif /* INCdevLibh.h*/ diff --git a/src/libCom/osi/devLibVMEImpl.h b/src/libCom/osi/devLibVMEImpl.h new file mode 100644 index 000000000..63d794ca3 --- /dev/null +++ b/src/libCom/osi/devLibVMEImpl.h @@ -0,0 +1,104 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* devLibImpl.h */ +/* */ + +/* + * Original Author: Marty Kraimer + * Author: Jeff Hill + * Date: 03-10-93 + */ + +#ifndef INCdevLibImplh +#define INCdevLibImplh 1 + +#include "dbDefs.h" +#include "shareLib.h" +#include "devLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * virtual OS layer for devLib.c + * + * The global virtual OS table pdevLibVME controls + * the behaviour of the functions defined in devLib.h. + * All of which call into the functions found in this table + * to perform system specific tasks. + */ +typedef struct devLibVME { + /* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ + long (*pDevMapAddr) (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + + /* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ + long (*pDevReadProbe) (unsigned wordSize, volatile const void *ptr, void *pValueRead); + + /* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ + long (*pDevWriteProbe) (unsigned wordSize, volatile void *ptr, const void *pValueWritten); + + /* + * connect ISR to a VME interrupt vector + * (required for backwards compatibility) + */ + long (*pDevConnectInterruptVME) (unsigned vectorNumber, + void (*pFunction)(void *), void *parameter); + + /* + * disconnect ISR from a VME interrupt vector + * (required for backwards compatibility) + */ + long (*pDevDisconnectInterruptVME) (unsigned vectorNumber, + void (*pFunction)(void *)); + + /* + * enable VME interrupt level + */ + long (*pDevEnableInterruptLevelVME) (unsigned level); + + /* + * disable VME interrupt level + */ + long (*pDevDisableInterruptLevelVME) (unsigned level); + /* malloc/free A24 address space */ + void *(*pDevA24Malloc)(size_t nbytes); + void (*pDevA24Free)(void *pBlock); + long (*pDevInit)(void); + + /* + * test if VME interrupt has an ISR connected + */ + int (*pDevInterruptInUseVME) (unsigned vectorNumber); +}devLibVME; + +epicsShareExtern devLibVME *pdevLibVME; + +#ifndef NO_DEVLIB_COMPAT +# define pdevLibVirtualOS pdevLibVME +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* INCdevLibImplh */ diff --git a/src/libCom/osi/epicsAssert.h b/src/libCom/osi/epicsAssert.h new file mode 100644 index 000000000..6f8285d4d --- /dev/null +++ b/src/libCom/osi/epicsAssert.h @@ -0,0 +1,58 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ +/* + * EPICS assert + * + * Author: Jeffrey O. Hill + * Date: 022795 + */ + +#ifndef INC_epicsAssert_H +#define INC_epicsAssert_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef epicsAssertAuthor +# define epicsAssertAuthor 0 +#endif + +#undef assert + +#ifdef NDEBUG +# define assert(ignore) ((void) 0) +#else /* NDEBUG */ + +epicsShareFunc void epicsAssert (const char *pFile, const unsigned line, + const char *pExp, const char *pAuthorName); + +# define assert(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +#endif /* NDEBUG */ + + +/* Compile-time checks */ +#define STATIC_JOIN(x, y) STATIC_JOIN2(x, y) +#define STATIC_JOIN2(x, y) x ## y +#define STATIC_ASSERT(expr) \ + typedef int STATIC_JOIN(static_assert_failed_at_line_, __LINE__) \ + [ (expr) ? 1 : -1 ] + + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsAssert_H */ diff --git a/src/libCom/osi/epicsEndian.h b/src/libCom/osi/epicsEndian.h new file mode 100644 index 000000000..fd662822f --- /dev/null +++ b/src/libCom/osi/epicsEndian.h @@ -0,0 +1,34 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_epicsEndian_H +#define INC_epicsEndian_H + +/* This file must be usable from both C and C++ */ + +#define EPICS_ENDIAN_LITTLE 1234 +#define EPICS_ENDIAN_BIG 4321 + + +/* The following OS Dependent file defines the macros + * EPICS_BYTE_ORDER and EPICS_FLOAT_WORD_ORDER to be + * one of the above EPICS_ENDIAN_ values. + */ + +#include "osdWireConfig.h" + +#ifndef EPICS_BYTE_ORDER +#error osdWireConfig.h didnt define EPICS_BYTE_ORDER +#endif + +#ifndef EPICS_FLOAT_WORD_ORDER +#error osdWireConfig.h didnt define EPICS_FLOAT_WORD_ORDER +#endif + +#endif /* INC_epicsEndian_H */ diff --git a/src/libCom/osi/epicsEvent.cpp b/src/libCom/osi/epicsEvent.cpp new file mode 100644 index 000000000..4d3dd4492 --- /dev/null +++ b/src/libCom/osi/epicsEvent.cpp @@ -0,0 +1,100 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* epicsMutex.c */ +/* Author: Jeff Hill */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsEvent.h" +#include "epicsStdioRedirect.h" + +// vxWorks 5.4 gcc fails during compile when I use std::exception +using namespace std; + +// exception payload +class epicsEvent::invalidSemaphore : public exception +{ + const char * what () const throw (); +}; + +const char * epicsEvent::invalidSemaphore::what () const throw () +{ + return "epicsEvent::invalidSemaphore()"; +} + +// +// Its probably preferable to not make these inline because they are in +// the sharable library interface. The use of inline or not here is probably +// not an issue because all of this ends up in the operating system in system +// calls +// + +epicsEvent::epicsEvent ( epicsEventInitialState initial ) : + id ( epicsEventCreate ( initial ) ) +{ + if ( this->id == 0 ) { + throw std::bad_alloc (); + } +} + +epicsEvent::~epicsEvent () +{ + epicsEventDestroy ( this->id ); +} + +void epicsEvent::signal () +{ + epicsEventSignal ( this->id ); +} + +void epicsEvent::wait () +{ + epicsEventWaitStatus status; + status = epicsEventWait (this->id); + if (status!=epicsEventWaitOK) { + throw invalidSemaphore (); + } +} + +bool epicsEvent::wait (double timeOut) +{ + epicsEventWaitStatus status; + status = epicsEventWaitWithTimeout (this->id, timeOut); + if (status==epicsEventWaitOK) { + return true; + } else if (status==epicsEventWaitTimeout) { + return false; + } else { + throw invalidSemaphore (); + } + return false; +} + +bool epicsEvent::tryWait () +{ + epicsEventWaitStatus status; + status = epicsEventTryWait (this->id); + if (status==epicsEventWaitOK) { + return true; + } else if (status==epicsEventWaitTimeout) { + return false; + } else { + throw invalidSemaphore (); + } + return false; +} + +void epicsEvent::show ( unsigned level ) const +{ + epicsEventShow ( this->id, level ); +} diff --git a/src/libCom/osi/epicsEvent.h b/src/libCom/osi/epicsEvent.h new file mode 100644 index 000000000..1bc98a0fd --- /dev/null +++ b/src/libCom/osi/epicsEvent.h @@ -0,0 +1,74 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef epicsEventh +#define epicsEventh + +#include "epicsAssert.h" +#include "shareLib.h" + +typedef struct epicsEventOSD *epicsEventId; + +typedef enum { + epicsEventWaitOK,epicsEventWaitTimeout,epicsEventWaitError +} epicsEventWaitStatus; + +typedef enum {epicsEventEmpty,epicsEventFull} epicsEventInitialState; + +#ifdef __cplusplus + +class epicsShareClass epicsEvent { +public: + epicsEvent ( epicsEventInitialState initial = epicsEventEmpty ); + ~epicsEvent (); + void signal (); + void wait (); /* blocks until full */ + bool wait ( double timeOut ); /* false if empty at time out */ + bool tryWait (); /* false if empty */ + void show ( unsigned level ) const; + + class invalidSemaphore; /* exception payload */ +private: + epicsEvent ( const epicsEvent & ); + epicsEvent & operator = ( const epicsEvent & ); + epicsEventId id; +}; + +#endif /*__cplusplus */ + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus */ + +epicsShareFunc epicsEventId epicsShareAPI epicsEventCreate( + epicsEventInitialState initialState); +epicsShareFunc epicsEventId epicsShareAPI epicsEventMustCreate ( + epicsEventInitialState initialState); +epicsShareFunc void epicsShareAPI epicsEventDestroy(epicsEventId id); +epicsShareFunc void epicsShareAPI epicsEventSignal(epicsEventId id); +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventWait( + epicsEventId id); +#define epicsEventMustWait(ID) { \ + epicsEventWaitStatus status = epicsEventWait(ID); \ + assert(status == epicsEventWaitOK); \ +} +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventWaitWithTimeout( + epicsEventId id, double timeOut); +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventTryWait( + epicsEventId id); +epicsShareFunc void epicsShareAPI epicsEventShow( + epicsEventId id, unsigned int level); + +#ifdef __cplusplus +} +#endif /*__cplusplus */ + +#include "osdEvent.h" + +#endif /* epicsEventh */ diff --git a/src/libCom/osi/epicsFindSymbol.h b/src/libCom/osi/epicsFindSymbol.h new file mode 100644 index 000000000..9935834da --- /dev/null +++ b/src/libCom/osi/epicsFindSymbol.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef epicsFindSymbolh +#define epicsFindSymbolh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name); +epicsShareFunc const char *epicsLoadError(void); +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* epicsFindSymbolh */ diff --git a/src/libCom/osi/epicsGeneralTime.c b/src/libCom/osi/epicsGeneralTime.c new file mode 100644 index 000000000..819220ea7 --- /dev/null +++ b/src/libCom/osi/epicsGeneralTime.c @@ -0,0 +1,497 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2008 Diamond Light Source Ltd +* Copyright (c) 2004 Oak Ridge National Laboratory +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Original Authors: David H. Thompson & Sheng Peng (ORNL) */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsTypes.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsMessageQueue.h" +#include "epicsString.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "epicsTimer.h" +#include "epicsInterrupt.h" +#include "osiSock.h" +#include "ellLib.h" +#include "errlog.h" +#include "cantProceed.h" +#include "envDefs.h" +#include "generalTimeSup.h" +#include "epicsGeneralTime.h" + + +/* Declarations */ + +typedef struct { + ELLNODE node; + char *name; + int priority; + union { + TIMECURRENTFUN Time; + TIMEEVENTFUN Event; + } get; + union { + TIMECURRENTFUN Time; + TIMEEVENTFUN Event; + } getInt; +} gtProvider; + +static struct { + epicsMutexId timeListLock; + ELLLIST timeProviders; + gtProvider *lastTimeProvider; + epicsTimeStamp lastProvidedTime; + + epicsMutexId eventListLock; + ELLLIST eventProviders; + gtProvider *lastEventProvider; + epicsTimeStamp eventTime[NUM_TIME_EVENTS]; + epicsTimeStamp lastProvidedBestTime; + + int ErrorCounts; +} gtPvt; + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + +/* Implementation */ + +static void generalTime_InitOnce(void *dummy) +{ + ellInit(>Pvt.timeProviders); + gtPvt.timeListLock = epicsMutexMustCreate(); + + ellInit(>Pvt.eventProviders); + gtPvt.eventListLock = epicsMutexMustCreate(); +} + +void generalTime_Init(void) +{ + epicsThreadOnce(&onceId, generalTime_InitOnce, NULL); +} + + +int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int *pPrio, int ignore) +{ + gtProvider *ptp; + int status = epicsTimeERROR; + + generalTime_Init(); + + epicsMutexMustLock(gtPvt.timeListLock); + for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + if (ptp->priority == ignore) + continue; + + status = ptp->get.Time(pDest); + if (status == epicsTimeOK) { + /* check time is monotonic */ + if (epicsTimeGreaterThanEqual(pDest, >Pvt.lastProvidedTime)) { + gtPvt.lastProvidedTime = *pDest; + if (ignore == 0) + gtPvt.lastTimeProvider = ptp; + if (pPrio) + *pPrio = ptp->priority; + } else { + int key; + *pDest = gtPvt.lastProvidedTime; + if (pPrio) + *pPrio = gtPvt.lastTimeProvider->priority; + key = epicsInterruptLock(); + gtPvt.ErrorCounts++; + epicsInterruptUnlock(key); + } + break; + } + } + if (status == epicsTimeERROR && + ignore == 0) + gtPvt.lastTimeProvider = NULL; + epicsMutexUnlock(gtPvt.timeListLock); + + return status; +} + +int epicsShareAPI epicsTimeGetCurrent(epicsTimeStamp *pDest) +{ + return generalTimeGetExceptPriority(pDest, NULL, 0); +} + +int epicsTimeGetCurrentInt(epicsTimeStamp *pDest) +{ + gtProvider *ptp = gtPvt.lastTimeProvider; + + if (ptp == NULL || + ptp->getInt.Time == NULL) return epicsTimeERROR; + + return ptp->getInt.Time(pDest); +} + + +static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, + int *pPrio) +{ + gtProvider *ptp; + int status = epicsTimeERROR; + + generalTime_Init(); + + if ((eventNumber < 0 || eventNumber >= NUM_TIME_EVENTS) && + (eventNumber != epicsTimeEventBestTime)) + return status; + + epicsMutexMustLock(gtPvt.eventListLock); + for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + + status = ptp->get.Event(pDest, eventNumber); + if (status != epicsTimeERROR) { + gtPvt.lastEventProvider = ptp; + if (pPrio) + *pPrio = ptp->priority; + + if (eventNumber == epicsTimeEventBestTime) { + if (epicsTimeGreaterThanEqual(pDest, + >Pvt.lastProvidedBestTime)) { + gtPvt.lastProvidedBestTime = *pDest; + } else { + int key; + *pDest = gtPvt.lastProvidedBestTime; + key = epicsInterruptLock(); + gtPvt.ErrorCounts++; + epicsInterruptUnlock(key); + } + } else { + if (epicsTimeGreaterThanEqual(pDest, + >Pvt.eventTime[eventNumber])) { + gtPvt.eventTime[eventNumber] = *pDest; + } else { + int key; + *pDest = gtPvt.eventTime[eventNumber]; + key = epicsInterruptLock(); + gtPvt.ErrorCounts++; + epicsInterruptUnlock(key); + } + } + break; + } + } + if (status == epicsTimeERROR) + gtPvt.lastEventProvider = NULL; + epicsMutexUnlock(gtPvt.eventListLock); + + return status; +} + +int epicsShareAPI epicsTimeGetEvent(epicsTimeStamp *pDest, int eventNumber) +{ + if (eventNumber == epicsTimeEventCurrentTime) { + return epicsTimeGetCurrent(pDest); + } else { + return generalTimeGetEventPriority(pDest, eventNumber, NULL); + } +} + +int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber) +{ + if (eventNumber == epicsTimeEventCurrentTime) { + return epicsTimeGetCurrentInt(pDest); + } else { + gtProvider *ptp = gtPvt.lastEventProvider; + + if (ptp == NULL || + ptp->getInt.Event == NULL) return epicsTimeERROR; + + return ptp->getInt.Event(pDest, eventNumber); + } +} + + +/* Provider Registration */ + +static void insertProvider(gtProvider *ptp, ELLLIST *plist, epicsMutexId lock) +{ + gtProvider *ptpref; + + epicsMutexMustLock(lock); + + for (ptpref = (gtProvider *)ellFirst(plist); + ptpref; ptpref = (gtProvider *)ellNext(&ptpref->node)) { + if (ptpref->priority > ptp->priority) + break; + } + + if (ptpref) { + /* Found a provider below the new one */ + ptpref = (gtProvider *)ellPrevious(&ptpref->node); + ellInsert(plist, &ptpref->node, &ptp->node); + } else { + ellAdd(plist, &ptp->node); + } + + epicsMutexUnlock(lock); +} + +static gtProvider * findProvider(ELLLIST *plist, epicsMutexId lock, + const char *name, int priority) +{ + gtProvider *ptp; + + epicsMutexMustLock(lock); + + for (ptp = (gtProvider *)ellFirst(plist); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + if (ptp->priority == ptp->priority && + !strcmp(ptp->name, name)) + break; + } + + epicsMutexUnlock(lock); + return ptp; +} + +int generalTimeRegisterEventProvider(const char *name, int priority, + TIMEEVENTFUN getEvent) +{ + gtProvider *ptp; + + generalTime_Init(); + + if (name == NULL || getEvent == NULL) + return epicsTimeERROR; + + ptp = (gtProvider *)malloc(sizeof(gtProvider)); + if (ptp == NULL) + return epicsTimeERROR; + + ptp->name = epicsStrDup(name); + ptp->priority = priority; + ptp->get.Event = getEvent; + ptp->getInt.Event = NULL; + + insertProvider(ptp, >Pvt.eventProviders, gtPvt.eventListLock); + + return epicsTimeOK; +} + +int generalTimeAddIntEventProvider(const char *name, int priority, + TIMEEVENTFUN getEvent) +{ + gtProvider *ptp = findProvider(>Pvt.eventProviders, gtPvt.eventListLock, + name, priority); + if (ptp == NULL) + return epicsTimeERROR; + + ptp->getInt.Event = getEvent; + + return epicsTimeOK; +} + +int generalTimeRegisterCurrentProvider(const char *name, int priority, + TIMECURRENTFUN getTime) +{ + gtProvider *ptp; + + generalTime_Init(); + + if (name == NULL || getTime == NULL) + return epicsTimeERROR; + + ptp = (gtProvider *)malloc(sizeof(gtProvider)); + if (ptp == NULL) + return epicsTimeERROR; + + ptp->name = epicsStrDup(name); + ptp->priority = priority; + ptp->get.Time = getTime; + ptp->getInt.Time = NULL; + + insertProvider(ptp, >Pvt.timeProviders, gtPvt.timeListLock); + + return epicsTimeOK; +} + +int generalTimeAddIntCurrentProvider(const char *name, int priority, + TIMECURRENTFUN getTime) +{ + gtProvider *ptp = findProvider(>Pvt.timeProviders, gtPvt.timeListLock, + name, priority); + if (ptp == NULL) + return epicsTimeERROR; + + ptp->getInt.Time = getTime; + + return epicsTimeOK; +} + +/* + * Provide an optional "last resort" provider for Event Time. + * + * This is deliberately optional, as it represents site policy. + * It is intended to be installed as an EventTime provider at the lowest + * priority, to return the current time for an event if there is no + * better time provider for event times. + * + * Typically, this will only be used during startup, or a time-provider + * resynchronisation, where events are being generated by the event system + * but the time provided by the system is not yet valid. + */ +static int lastResortGetEvent(epicsTimeStamp *timeStamp, int eventNumber) +{ + return epicsTimeGetCurrent(timeStamp); +} + +int installLastResortEventProvider(void) +{ + return generalTimeEventTpRegister("Last Resort Event", + LAST_RESORT_PRIORITY, lastResortGetEvent); +} + + +/* Status Report */ + +long generalTimeReport(int level) +{ + int items; + + if (onceId == EPICS_THREAD_ONCE_INIT) { + printf("General time framework not yet initialized.\n"); + return epicsTimeOK; + } + + printf("Backwards time errors prevented %u times.\n\n", + generalTimeGetErrorCounts()); + + /* Use an output buffer to avoid holding mutexes during printing */ + + printf("Current Time Providers:\n"); + epicsMutexMustLock(gtPvt.timeListLock); + if ((items = ellCount(>Pvt.timeProviders))) { + gtProvider *ptp; + char *message; /* Temporary output buffer */ + char *pout; + + message = calloc(items, 80 * 2); /* Each provider needs 2 lines */ + if (!message) { + epicsMutexUnlock(gtPvt.timeListLock); + printf("Out of memory\n"); + return epicsTimeERROR; + } + + pout = message; + + for (ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + pout += sprintf(pout, " \"%s\", priority = %d\n", + ptp->name, ptp->priority); + if (level) { + epicsTimeStamp tempTS; + if (ptp->get.Time(&tempTS) != epicsTimeERROR) { + char tempTSText[40]; + epicsTimeToStrftime(tempTSText, sizeof(tempTSText), + "%Y-%m-%d %H:%M:%S.%06f", &tempTS); + pout += sprintf(pout, "\tCurrent Time is %s.\n", + tempTSText); + } else { + pout += sprintf(pout, "\tCurrent Time not available\n"); + } + } + } + epicsMutexUnlock(gtPvt.timeListLock); + puts(message); + free(message); + } else { + epicsMutexUnlock(gtPvt.timeListLock); + printf("\tNo Providers registered.\n"); + } + + printf("Event Time Providers:\n"); + epicsMutexMustLock(gtPvt.eventListLock); + if ((items = ellCount(>Pvt.eventProviders))) + { + gtProvider *ptp; + char *message; /* Temporary output buffer */ + char *pout; + + message = calloc(items, 80); /* Each provider needs 1 line, */ + if (!message) { + epicsMutexUnlock(gtPvt.eventListLock); + printf("Out of memory\n"); + return epicsTimeERROR; + } + + pout = message; + + for (ptp = (gtProvider *)ellFirst(>Pvt.eventProviders); + ptp; ptp = (gtProvider *)ellNext(&ptp->node)) { + pout += sprintf(pout, " \"%s\", priority = %d\n", + ptp->name, ptp->priority); + } + epicsMutexUnlock(gtPvt.eventListLock); + puts(message); + free(message); + } + else + { + epicsMutexUnlock(gtPvt.eventListLock); + printf("\tNo Providers registered.\n"); + } + + return epicsTimeOK; +} + +/* + * Access to internal status values. + */ + +void generalTimeResetErrorCounts(void) +{ + int key = epicsInterruptLock(); + gtPvt.ErrorCounts = 0; + epicsInterruptUnlock(key); +} + +int generalTimeGetErrorCounts(void) +{ + int key = epicsInterruptLock(); + int errors = gtPvt.ErrorCounts; + epicsInterruptUnlock(key); + return errors; +} + +const char * generalTimeCurrentProviderName(void) +{ + if (gtPvt.lastTimeProvider) + return gtPvt.lastTimeProvider->name; + return NULL; +} + +const char * generalTimeEventProviderName(void) +{ + if (gtPvt.lastEventProvider) + return gtPvt.lastEventProvider->name; + return NULL; +} + +const char * generalTimeHighestCurrentName(void) +{ + gtProvider *ptp; + + epicsMutexMustLock(gtPvt.timeListLock); + ptp = (gtProvider *)ellFirst(>Pvt.timeProviders); + epicsMutexUnlock(gtPvt.timeListLock); + return ptp ? ptp->name : NULL; +} diff --git a/src/libCom/osi/epicsGeneralTime.h b/src/libCom/osi/epicsGeneralTime.h new file mode 100644 index 000000000..3adc23705 --- /dev/null +++ b/src/libCom/osi/epicsGeneralTime.h @@ -0,0 +1,43 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2008 Diamond Light Source Ltd +* Copyright (c) 2004 Oak Ridge National Laboratory +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_epicsGeneralTime_H +#define INC_epicsGeneralTime_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define NUM_TIME_EVENTS 256 +/* Time Events are numbered 0 through (NUM_TIME_EVENTS-1) */ + +epicsShareFunc void generalTime_Init(void); + +epicsShareFunc int installLastResortEventProvider(void); + +epicsShareFunc void generalTimeResetErrorCounts(void); +epicsShareFunc int generalTimeGetErrorCounts(void); + +epicsShareFunc const char * generalTimeCurrentProviderName(void); +epicsShareFunc const char * generalTimeEventProviderName(void); +epicsShareFunc const char * generalTimeHighestCurrentName(void); + +/* Original names, for compatibility */ +#define generalTimeCurrentTpName generalTimeCurrentProviderName +#define generalTimeEventTpName generalTimeEventProviderName + +epicsShareFunc long generalTimeReport(int interest); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsGeneralTime_H */ diff --git a/src/libCom/osi/epicsInterrupt.h b/src/libCom/osi/epicsInterrupt.h new file mode 100644 index 000000000..93b443810 --- /dev/null +++ b/src/libCom/osi/epicsInterrupt.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#ifndef epicsInterrupth +#define epicsInterrupth + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +epicsShareFunc int epicsInterruptLock(void); +epicsShareFunc void epicsInterruptUnlock(int key); +epicsShareFunc int epicsInterruptIsInterruptContext(void); +epicsShareFunc void epicsInterruptContextMessage(const char *message); + +#ifdef __cplusplus +} +#endif + +#include "osdInterrupt.h" + +#endif /* epicsInterrupth */ diff --git a/src/libCom/osi/epicsMath.cpp b/src/libCom/osi/epicsMath.cpp new file mode 100644 index 000000000..1388e658a --- /dev/null +++ b/src/libCom/osi/epicsMath.cpp @@ -0,0 +1,43 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonna LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMath.cpp */ + +#define epicsExportSharedSymbols +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4723) +#endif + +#ifndef NAN +static float makeNAN ( void ) +{ + float a = 0, b = 0; + return a / b; +} +#define NAN makeNAN() +#endif + +#ifndef INFINITY +static float makeINF ( void ) +{ + float a = 1, b = 0; + return a / b; +} +#define INFINITY makeINF() +#endif + +extern "C" { +epicsShareDef float epicsNAN = NAN; +epicsShareDef float epicsINF = INFINITY; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + diff --git a/src/libCom/osi/epicsMessageQueue.cpp b/src/libCom/osi/epicsMessageQueue.cpp new file mode 100644 index 000000000..7090aed7f --- /dev/null +++ b/src/libCom/osi/epicsMessageQueue.cpp @@ -0,0 +1,83 @@ +/*************************************************************************\ +* 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 W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsMessageQueue.h" +#include "epicsStdioRedirect.h" + +epicsMessageQueue::epicsMessageQueue(unsigned int aCapacity, + unsigned int aMaxMessageSize) + : id ( epicsMessageQueueCreate(aCapacity, aMaxMessageSize) ) +{ + if (id == NULL) + throw std::bad_alloc (); +} + +epicsMessageQueue::~epicsMessageQueue() +{ + epicsMessageQueueDestroy(id); +} + +int +epicsMessageQueue::trySend(void *message, unsigned int size) +{ + return epicsMessageQueueTrySend(id, message, size); +} + +int +epicsMessageQueue::send(void *message, unsigned int size) +{ + return epicsMessageQueueSend(id, message, size); +} + +int +epicsMessageQueue::send(void *message, unsigned int size, double timeout) +{ + return epicsMessageQueueSendWithTimeout(id, message, size, timeout); +} + +int +epicsMessageQueue::tryReceive(void *message, unsigned int size ) +{ + return epicsMessageQueueTryReceive(id, message, size); +} + +int +epicsMessageQueue::receive(void *message, unsigned int size ) +{ + return epicsMessageQueueReceive(id, message, size); +} + +int +epicsMessageQueue::receive(void *message, unsigned int size, double timeout) +{ + return epicsMessageQueueReceiveWithTimeout(id, message, size, timeout); +} + +unsigned int +epicsMessageQueue::pending() +{ + return epicsMessageQueuePending(id); +} + +void +epicsMessageQueue::show(unsigned int level) +{ + epicsMessageQueueShow(id, level); +} diff --git a/src/libCom/osi/epicsMessageQueue.h b/src/libCom/osi/epicsMessageQueue.h new file mode 100644 index 000000000..d02da4ea3 --- /dev/null +++ b/src/libCom/osi/epicsMessageQueue.h @@ -0,0 +1,102 @@ +/*************************************************************************\ +* 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 W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * Interthread message passing + */ +#ifndef epicsMessageQueueh +#define epicsMessageQueueh + +#include "epicsAssert.h" +#include "shareLib.h" + +typedef struct epicsMessageQueueOSD *epicsMessageQueueId; + +#ifdef __cplusplus + +#include "locationException.h" + +class epicsShareClass epicsMessageQueue { +public: + epicsMessageQueue ( unsigned int capacity, + unsigned int maximumMessageSize ); + ~epicsMessageQueue (); + int trySend ( void *message, unsigned int messageSize ); + int send ( void *message, unsigned int messageSize); + int send ( void *message, unsigned int messageSize, double timeout ); + int tryReceive ( void *message, unsigned int size ); + int receive ( void *message, unsigned int size ); + int receive ( void *message, unsigned int size, double timeout ); + void show ( unsigned int level = 0 ); + unsigned int pending (); + +private: /* Prevent compiler-generated member functions */ + /* default constructor, copy constructor, assignment operator */ + epicsMessageQueue(); + epicsMessageQueue(const epicsMessageQueue &); + epicsMessageQueue& operator=(const epicsMessageQueue &); + + epicsMessageQueueId id; +}; + +extern "C" { +#endif /*__cplusplus */ + +epicsShareFunc epicsMessageQueueId epicsShareAPI epicsMessageQueueCreate( + unsigned int capacity, + unsigned int maximumMessageSize); +epicsShareFunc void epicsShareAPI epicsMessageQueueDestroy( + epicsMessageQueueId id); +epicsShareFunc int epicsShareAPI epicsMessageQueueTrySend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize); +epicsShareFunc int epicsShareAPI epicsMessageQueueSend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize); +epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int messageSize, + double timeout); +epicsShareFunc int epicsShareAPI epicsMessageQueueTryReceive( + epicsMessageQueueId id, + void *message, + unsigned int size); +epicsShareFunc int epicsShareAPI epicsMessageQueueReceive( + epicsMessageQueueId id, + void *message, + unsigned int size); +epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int size, + double timeout); +epicsShareFunc int epicsShareAPI epicsMessageQueuePending( + epicsMessageQueueId id); +epicsShareFunc void epicsShareAPI epicsMessageQueueShow( + epicsMessageQueueId id, + int level); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#include "osdMessageQueue.h" + +#endif /* epicsMessageQueueh */ diff --git a/src/libCom/osi/epicsMutex.cpp b/src/libCom/osi/epicsMutex.cpp new file mode 100644 index 000000000..d2e421204 --- /dev/null +++ b/src/libCom/osi/epicsMutex.cpp @@ -0,0 +1,326 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* epicsMutex.c */ +/* Author: Marty Kraimer and Jeff Hill Date: 03APR01 */ + +/* + * NOTES: + * 1) LOG_LAST_OWNER feature is normally commented out because + * it slows down the system at run time, anfd because its not + * currently safe to convert a thread id to a thread name because + * the thread may have exited making the thread id invalid. + */ + +#include + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdioRedirect.h" +#include "epicsThread.h" +#include "ellLib.h" +#include "errlog.h" +#include "epicsMutex.h" +#include "epicsThread.h" + +static int firstTime = 1; +static ELLLIST mutexList; +static ELLLIST freeList; + +struct epicsMutexParm { + ELLNODE node; + epicsMutexOSD * id; +# ifdef LOG_LAST_OWNER + epicsThreadId lastOwner; +# endif + const char *pFileName; + int lineno; +}; + +static epicsMutexOSD * epicsMutexGlobalLock; + + +// vxWorks 5.4 gcc fails during compile when I use std::exception +using namespace std; + +// exception payload +class epicsMutex::mutexCreateFailed : public exception +{ + const char * what () const throw (); +}; + +const char * epicsMutex::mutexCreateFailed::what () const throw () +{ + return "epicsMutex::mutexCreateFailed()"; +} + +// exception payload +class epicsMutex::invalidMutex : public exception +{ + const char * what () const throw (); +}; + +const char * epicsMutex::invalidMutex::what () const throw () +{ + return "epicsMutex::invalidMutex()"; +} + + +epicsMutexId epicsShareAPI epicsMutexOsiCreate( + const char *pFileName,int lineno) +{ + epicsMutexOSD * id; + + if(firstTime) { + firstTime=0; + ellInit(&mutexList); + ellInit(&freeList); + epicsMutexGlobalLock = epicsMutexOsdCreate(); + } + id = epicsMutexOsdCreate(); + if(!id) { + return 0; + } + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + epicsMutexParm *pmutexNode = + reinterpret_cast < epicsMutexParm * > ( ellFirst(&freeList) ); + if(pmutexNode) { + ellDelete(&freeList,&pmutexNode->node); + } else { + pmutexNode = static_cast < epicsMutexParm * > ( calloc(1,sizeof(epicsMutexParm)) ); + } + pmutexNode->id = id; +# ifdef LOG_LAST_OWNER + pmutexNode->lastOwner = 0; +# endif + pmutexNode->pFileName = pFileName; + pmutexNode->lineno = lineno; + ellAdd(&mutexList,&pmutexNode->node); + epicsMutexOsdUnlock(epicsMutexGlobalLock); + return(pmutexNode); +} + +epicsMutexId epicsShareAPI epicsMutexOsiMustCreate( + const char *pFileName,int lineno) +{ + epicsMutexId id = epicsMutexOsiCreate(pFileName,lineno); + assert(id); + return(id ); +} + +void epicsShareAPI epicsMutexDestroy(epicsMutexId pmutexNode) +{ + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + ellDelete(&mutexList,&pmutexNode->node); + epicsMutexOsdDestroy(pmutexNode->id); + ellAdd(&freeList,&pmutexNode->node); + epicsMutexOsdUnlock(epicsMutexGlobalLock); +} + +void epicsShareAPI epicsMutexUnlock(epicsMutexId pmutexNode) +{ + epicsMutexOsdUnlock(pmutexNode->id); +} + +epicsMutexLockStatus epicsShareAPI epicsMutexLock( + epicsMutexId pmutexNode) +{ + epicsMutexLockStatus status = + epicsMutexOsdLock(pmutexNode->id); +# ifdef LOG_LAST_OWNER + if ( status == epicsMutexLockOK ) { + pmutexNode->lastOwner = epicsThreadGetIdSelf(); + } +# endif + return status; +} + +epicsMutexLockStatus epicsShareAPI epicsMutexTryLock( + epicsMutexId pmutexNode) +{ + epicsMutexLockStatus status = + epicsMutexOsdTryLock(pmutexNode->id); +# ifdef LOG_LAST_OWNER + if ( status == epicsMutexLockOK ) { + pmutexNode->lastOwner = epicsThreadGetIdSelf(); + } +# endif + return status; +} + +void epicsShareAPI epicsMutexShow( + epicsMutexId pmutexNode, unsigned int level) +{ +# ifdef LOG_LAST_OWNER + char threadName [255]; + if ( pmutexNode->lastOwner ) { +# error currently not safe to fetch name for stale thread + epicsThreadGetName ( pmutexNode->lastOwner, + threadName, sizeof ( threadName ) ); + } + else { + strcpy ( threadName, "" ); + } + printf("epicsMutexId %p last owner \"%s\" source %s line %d\n", + (void *)pmutexNode, threadName, + pmutexNode->pFileName, pmutexNode->lineno); +# else + printf("epicsMutexId %p source %s line %d\n", + (void *)pmutexNode, pmutexNode->pFileName, + pmutexNode->lineno); +# endif + if ( level > 0 ) { + epicsMutexOsdShow(pmutexNode->id,level-1); + } +} + +void epicsShareAPI epicsMutexShowAll(int onlyLocked,unsigned int level) +{ + epicsMutexParm *pmutexNode; + + if(firstTime) return; + printf("ellCount(&mutexList) %d ellCount(&freeList) %d\n", + ellCount(&mutexList),ellCount(&freeList)); + epicsMutexLockStatus lockStat = + epicsMutexOsdLock(epicsMutexGlobalLock); + assert ( lockStat == epicsMutexLockOK ); + pmutexNode = reinterpret_cast < epicsMutexParm * > ( ellFirst(&mutexList) ); + while(pmutexNode) { + if(onlyLocked) { + epicsMutexLockStatus status; + status = epicsMutexOsdTryLock(pmutexNode->id); + if(status==epicsMutexLockOK) { + epicsMutexOsdUnlock(pmutexNode->id); + pmutexNode = + reinterpret_cast < epicsMutexParm * > + ( ellNext(&pmutexNode->node) ); + continue; + } + } + epicsMutexShow(pmutexNode, level); + pmutexNode = + reinterpret_cast < epicsMutexParm * > ( ellNext(&pmutexNode->node) ); + } + epicsMutexOsdUnlock(epicsMutexGlobalLock); +} + +epicsMutex :: epicsMutex () : + id ( epicsMutexCreate () ) +{ + if ( this->id == 0 ) { + throw mutexCreateFailed (); + } +} + +epicsMutex ::~epicsMutex () +{ + epicsMutexDestroy ( this->id ); +} + +void epicsMutex::lock () +{ + epicsMutexLockStatus status = epicsMutexLock ( this->id ); + if ( status != epicsMutexLockOK ) { + throw invalidMutex (); + } +} + +bool epicsMutex::tryLock () // X aCC 361 +{ + epicsMutexLockStatus status = epicsMutexTryLock ( this->id ); + if ( status == epicsMutexLockOK ) { + return true; + } + else if ( status != epicsMutexLockTimeout ) { + throw invalidMutex (); + } + return false; +} + +void epicsMutex::unlock () +{ + epicsMutexUnlock ( this->id ); +} + +void epicsMutex :: show ( unsigned level ) const +{ + epicsMutexShow ( this->id, level ); +} + +static epicsThreadPrivate < epicsDeadlockDetectMutex > + * pCurrentMutexLevel = 0; + +static epicsThreadOnceId epicsDeadlockDetectMutexInit = EPICS_THREAD_ONCE_INIT; + +extern "C" +void epicsDeadlockDetectMutexInitFunc ( void * ) +{ + pCurrentMutexLevel = new epicsThreadPrivate < epicsDeadlockDetectMutex > (); +} + +epicsDeadlockDetectMutex:: + epicsDeadlockDetectMutex ( hierarchyLevel_t level ) : + hierarchyLevel ( level ), pPreviousLevel ( 0 ) +{ + epicsThreadOnce ( & epicsDeadlockDetectMutexInit, + epicsDeadlockDetectMutexInitFunc, 0 ); +} + +epicsDeadlockDetectMutex::~epicsDeadlockDetectMutex () +{ +} + +void epicsDeadlockDetectMutex::show ( unsigned level ) const +{ + this->mutex.show ( level ); +} + +void epicsDeadlockDetectMutex::lock () +{ + epicsDeadlockDetectMutex * pPrev = pCurrentMutexLevel->get(); + if ( pPrev && pPrev != this ) { + if ( pPrev->hierarchyLevel >= this->hierarchyLevel ) { + errlogPrintf ( "!!!! Deadlock Vulnerability Detected !!!! " + "at level %u and moving to level %u\n", + pPrev->hierarchyLevel, + this->hierarchyLevel ); + } + } + this->mutex.lock (); + if ( pPrev && pPrev != this ) { + pCurrentMutexLevel->set ( this ); + this->pPreviousLevel = pPrev; + } +} + +void epicsDeadlockDetectMutex::unlock () +{ + pCurrentMutexLevel->set ( this->pPreviousLevel ); + this->mutex.unlock (); +} + +bool epicsDeadlockDetectMutex::tryLock () +{ + bool success = this->mutex.tryLock (); + if ( success ) { + this->pPreviousLevel = pCurrentMutexLevel->get(); + pCurrentMutexLevel->set ( this ); + } + return success; +} + + diff --git a/src/libCom/osi/epicsMutex.h b/src/libCom/osi/epicsMutex.h new file mode 100644 index 000000000..d016cf3d4 --- /dev/null +++ b/src/libCom/osi/epicsMutex.h @@ -0,0 +1,109 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef epicsMutexh +#define epicsMutexh + +#include "epicsAssert.h" + +#include "shareLib.h" + +typedef struct epicsMutexParm *epicsMutexId; +typedef enum { + epicsMutexLockOK,epicsMutexLockTimeout,epicsMutexLockError +} epicsMutexLockStatus; + +#ifdef __cplusplus + +#include "compilerDependencies.h" + +class epicsShareClass epicsMutex { +public: + class mutexCreateFailed; /* exception payload */ + class invalidMutex; /* exception payload */ + epicsMutex (); + ~epicsMutex (); + void show ( unsigned level ) const; + void lock (); /* blocks until success */ + void unlock (); + bool tryLock (); /* true if successful */ +private: + epicsMutexId id; + epicsMutex ( const epicsMutex & ); + epicsMutex & operator = ( const epicsMutex & ); +}; + +class epicsShareClass epicsDeadlockDetectMutex { +public: + typedef unsigned hierarchyLevel_t; + epicsDeadlockDetectMutex ( unsigned hierarchyLevel_t ); + ~epicsDeadlockDetectMutex (); + void show ( unsigned level ) const; + void lock (); /* blocks until success */ + void unlock (); + bool tryLock (); /* true if successful */ +private: + epicsMutex mutex; + const hierarchyLevel_t hierarchyLevel; + class epicsDeadlockDetectMutex * pPreviousLevel; + epicsDeadlockDetectMutex ( const epicsDeadlockDetectMutex & ); + epicsDeadlockDetectMutex & operator = ( const epicsDeadlockDetectMutex & ); +}; + +#endif /*__cplusplus*/ + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +#define epicsMutexCreate() epicsMutexOsiCreate(__FILE__,__LINE__) +epicsShareFunc epicsMutexId epicsShareAPI epicsMutexOsiCreate( + const char *pFileName,int lineno); +#define epicsMutexMustCreate() epicsMutexOsiMustCreate(__FILE__,__LINE__) +epicsShareFunc epicsMutexId epicsShareAPI epicsMutexOsiMustCreate( + const char *pFileName,int lineno); +epicsShareFunc void epicsShareAPI epicsMutexDestroy(epicsMutexId id); +epicsShareFunc void epicsShareAPI epicsMutexUnlock(epicsMutexId id); +epicsShareFunc epicsMutexLockStatus epicsShareAPI epicsMutexLock( + epicsMutexId id); +#define epicsMutexMustLock(ID) { \ + epicsMutexLockStatus status = epicsMutexLock(ID); \ + assert(status == epicsMutexLockOK); \ +} +epicsShareFunc epicsMutexLockStatus epicsShareAPI epicsMutexTryLock( + epicsMutexId id); +epicsShareFunc void epicsShareAPI epicsMutexShow( + epicsMutexId id,unsigned int level); +epicsShareFunc void epicsShareAPI epicsMutexShowAll( + int onlyLocked,unsigned int level); + +/*NOTES: + epicsMutex MUST implement recursive locking + epicsMutex should implement priority inheritance and deletion safe +*/ + +/* + * The following is the interface to the OS dependent + * implementation and should NOT be called directly by + * user code + */ +struct epicsMutexOSD * epicsMutexOsdCreate(void); +void epicsMutexOsdDestroy(struct epicsMutexOSD *); +void epicsMutexOsdUnlock(struct epicsMutexOSD *); +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD *); +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD *); +void epicsMutexOsdShow(struct epicsMutexOSD *,unsigned int level); + +#ifdef __cplusplus +} +#endif + +#include "osdMutex.h" + +#endif /* epicsMutexh */ diff --git a/src/libCom/osi/epicsSignal.h b/src/libCom/osi/epicsSignal.h new file mode 100644 index 000000000..2fd0e5839 --- /dev/null +++ b/src/libCom/osi/epicsSignal.h @@ -0,0 +1,46 @@ +/*************************************************************************\ + * 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. +\*************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +/* + * The requests in this interface are typically ignored on OS that do not implement + * POSIX signals. + */ + +struct epicsThreadOSD; + +/* + * Required to avoid problems with soft IOCs getting SIGHUPs when a + * Channel Access client disconnects + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ); + +/* + * Required to avoid terminating a process which is blocking in a + * socket send() call when the SIGPIPE signal is generated by the OS. + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ); + +/* + * required only if shutdown() and close() do not interrupt a thread blocking in + * a socket system call + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ); +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * ); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libCom/osi/epicsStdio.c b/src/libCom/osi/epicsStdio.c new file mode 100644 index 000000000..50c696a2c --- /dev/null +++ b/src/libCom/osi/epicsStdio.c @@ -0,0 +1,115 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsStdio.c */ + +/* Author: Marty Kraimer */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "shareLib.h" +#include "epicsThread.h" +#include "epicsStdio.h" + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; +static epicsThreadPrivateId stdinThreadPrivateId; +static epicsThreadPrivateId stdoutThreadPrivateId; +static epicsThreadPrivateId stderrThreadPrivateId; + +static void once(void *junk) +{ + stdinThreadPrivateId = epicsThreadPrivateCreate(); + stdoutThreadPrivateId = epicsThreadPrivateCreate(); + stderrThreadPrivateId = epicsThreadPrivateCreate(); +} + +FILE * epicsShareAPI epicsGetStdin(void) +{ + FILE *fp = epicsGetThreadStdin(); + if(!fp) fp = stdin; + return fp; +} + +FILE * epicsShareAPI epicsGetStdout(void) +{ + FILE *fp = epicsGetThreadStdout(); + if(!fp) fp = stdout; + return fp; +} + +FILE * epicsShareAPI epicsGetStderr(void) +{ + FILE *fp = epicsGetThreadStderr(); + if(!fp) fp = stderr; + return fp; +} + +FILE * epicsShareAPI epicsGetThreadStdin(void) +{ + epicsThreadOnce(&onceId,once,0); + return epicsThreadPrivateGet(stdinThreadPrivateId); +} + +FILE * epicsShareAPI epicsGetThreadStdout(void) +{ + epicsThreadOnce(&onceId,once,0); + return epicsThreadPrivateGet(stdoutThreadPrivateId); +} + +FILE * epicsShareAPI epicsGetThreadStderr(void) +{ + epicsThreadOnce(&onceId,once,0); + return epicsThreadPrivateGet(stderrThreadPrivateId); +} + +void epicsShareAPI epicsSetThreadStdin(FILE *fp) +{ + epicsThreadOnce(&onceId,once,0); + epicsThreadPrivateSet(stdinThreadPrivateId,fp); +} + +void epicsShareAPI epicsSetThreadStdout(FILE *fp) +{ + epicsThreadOnce(&onceId,once,0); + epicsThreadPrivateSet(stdoutThreadPrivateId,fp); +} + +void epicsShareAPI epicsSetThreadStderr(FILE *fp) +{ + epicsThreadOnce(&onceId,once,0); + epicsThreadPrivateSet(stderrThreadPrivateId,fp); +} + +int epicsShareAPI epicsStdoutPrintf(const char *pFormat, ...) +{ + va_list pvar; + int nchar; + FILE *stream = epicsGetStdout(); + + va_start(pvar, pFormat); + nchar = vfprintf(stream, pFormat, pvar); + va_end(pvar); + return nchar; +} + +int epicsShareAPI epicsStdoutPuts(const char *str) +{ + return fprintf(epicsGetStdout(), "%s\n", str); +} + +int epicsShareAPI epicsStdoutPutchar(int c) +{ + return putc(c, epicsGetStdout()); +} diff --git a/src/libCom/osi/epicsStdio.h b/src/libCom/osi/epicsStdio.h new file mode 100644 index 000000000..7a2dc5ade --- /dev/null +++ b/src/libCom/osi/epicsStdio.h @@ -0,0 +1,68 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsStdio.h */ + +#ifndef epicsStdioh +#define epicsStdioh + +#include +#include + +#include "shareLib.h" +#include "compilerDependencies.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsShareAPI epicsSnprintf( + char *str, size_t size, const char *format, ...) EPICS_PRINTF_STYLE(3,4); +epicsShareFunc int epicsShareAPI epicsVsnprintf( + char *str, size_t size, const char *format, va_list ap); +epicsShareFunc void epicsShareAPI epicsTempName ( + char * pNameBuf, size_t nameBufLength ); +epicsShareFunc FILE * epicsShareAPI epicsTempFile (void); + +/* + * truncate to specified size (we dont use truncate() + * because it is not portable) + * + * pFileName - name (and optionally path) of file + * size - the new file size (if file is curretly larger) + * + * returns TF_OK if the file is less than size bytes + * or if it was successfully truncated. Returns + * TF_ERROR if the file could not be truncated. + */ +enum TF_RETURN {TF_OK=0, TF_ERROR=1}; +epicsShareFunc enum TF_RETURN truncateFile ( const char *pFileName, unsigned size ); + +/* The following are for redirecting stdin,stdout,stderr */ +epicsShareFunc FILE * epicsShareAPI epicsGetStdin(void); +epicsShareFunc FILE * epicsShareAPI epicsGetStdout(void); +epicsShareFunc FILE * epicsShareAPI epicsGetStderr(void); +/* These are intended for iocsh only */ +epicsShareFunc FILE * epicsShareAPI epicsGetThreadStdin(void); +epicsShareFunc FILE * epicsShareAPI epicsGetThreadStdout(void); +epicsShareFunc FILE * epicsShareAPI epicsGetThreadStderr(void); +epicsShareFunc void epicsShareAPI epicsSetThreadStdin(FILE *); +epicsShareFunc void epicsShareAPI epicsSetThreadStdout(FILE *); +epicsShareFunc void epicsShareAPI epicsSetThreadStderr(FILE *); + +epicsShareFunc int epicsShareAPI epicsStdoutPrintf( + const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); +epicsShareFunc int epicsShareAPI epicsStdoutPuts(const char *str); +epicsShareFunc int epicsShareAPI epicsStdoutPutchar(int c); + +#ifdef __cplusplus +} +#endif + +#endif /* epicsStdioh */ diff --git a/src/libCom/osi/epicsStdioRedirect.h b/src/libCom/osi/epicsStdioRedirect.h new file mode 100644 index 000000000..36edf1dbe --- /dev/null +++ b/src/libCom/osi/epicsStdioRedirect.h @@ -0,0 +1,49 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* epicsStdioRedirect.h */ + +#ifndef epicsStdioRedirecth +#define epicsStdioRedirecth + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#undef stdin +#define stdin epicsGetStdin() +#undef stdout +#define stdout epicsGetStdout() +#undef stderr +#define stderr epicsGetStderr() + +/* Make printf, puts and putchar use *our* version of stdout */ + +#ifdef printf +# undef printf +#endif /* printf */ +#define printf epicsStdoutPrintf + +#ifdef puts +# undef puts +#endif /* puts */ +#define puts epicsStdoutPuts + +#ifdef putchar +# undef putchar +#endif /* putchar */ +#define putchar epicsStdoutPutchar + +#ifdef __cplusplus +} +#endif + +#endif /* epicsStdioRedirecth */ diff --git a/src/libCom/osi/epicsThread.cpp b/src/libCom/osi/epicsThread.cpp new file mode 100644 index 000000000..ba7f4cbd8 --- /dev/null +++ b/src/libCom/osi/epicsThread.cpp @@ -0,0 +1,358 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 +// + +#include +#include + +#include +#include +#include +#include + +// The following is required for Solaris builds +#undef __EXTENSIONS__ + +#define epicsExportSharedSymbols +#include "epicsAlgorithm.h" +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsAssert.h" +#include "epicsGuard.h" +#include "errlog.h" + +using namespace std; + +epicsThreadRunable::~epicsThreadRunable () {} +void epicsThreadRunable::run () {} +void epicsThreadRunable::show ( unsigned int ) const {} + +class epicsThread :: unableToCreateThread : + public exception { +public: + const char * what () const throw (); +}; + +const char * epicsThread :: + unableToCreateThread :: what () const throw () +{ + return "unable to create thread"; +} + +void epicsThread :: printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ) +{ + char date[64]; + try { + epicsTime cur = epicsTime :: getCurrent (); + cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); + } + catch ( ... ) { + strcpy ( date, "" ); + } + char name [128]; + epicsThreadGetName ( this->id, name, sizeof ( name ) ); + errlogPrintf ( + "epicsThread: Unexpected C++ exception \"%s\" " + "with type \"%s\" in thread \"%s\" at %s\n", + pExceptionContext, pExceptionTypeName, name, date ); + errlogFlush (); + // this should behave as the C++ implementation intends when an + // exception isnt handled. If users dont like this behavior, they + // can install an application specific unexpected handler. + std::unexpected (); +} + +extern "C" void epicsThreadCallEntryPoint ( void * pPvt ) +{ + epicsThread * pThread = + static_cast ( pPvt ); + bool waitRelease = false; + try { + pThread->pWaitReleaseFlag = & waitRelease; + if ( pThread->beginWait () ) { + pThread->runable.run (); + // current thread may have run the destructor + // so must not touch the this pointer from + // here on down if waitRelease is true + } + } + catch ( const epicsThread::exitException & ) { + } + catch ( std::exception & except ) { + if ( ! waitRelease ) { + pThread->printLastChanceExceptionMessage ( + typeid ( except ).name (), except.what () ); + } + } + catch ( ... ) { + if ( ! waitRelease ) { + pThread->printLastChanceExceptionMessage ( + "catch ( ... )", "Non-standard C++ exception" ); + } + } + if ( ! waitRelease ) { + epicsGuard < epicsMutex > guard ( pThread->mutex ); + pThread->terminated = true; + pThread->exitEvent.signal (); + // once the terminated flag is set and we release the lock + // then the "this" pointer must not be touched again + } +} + +bool epicsThread::beginWait () throw () +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + while ( ! this->begin && ! this->cancel ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->event.wait (); + } + return this->begin && ! this->cancel; +} + +void epicsThread::exit () +{ + throw exitException (); +} + +void epicsThread::exitWait () throw () +{ + bool success = this->exitWait ( DBL_MAX ); + assert ( success ); +} + +bool epicsThread::exitWait ( const double delay ) throw () +{ + try { + // if destructor is running in managed thread then of + // course we will not wait for the managed thread to + // exit + if ( this->isCurrentThread() ) { + if ( this->pWaitReleaseFlag ) { + *this->pWaitReleaseFlag = true; + } + return true; + } + epicsTime exitWaitBegin = epicsTime::getCurrent (); + double exitWaitElapsed = 0.0; + epicsGuard < epicsMutex > guard ( this->mutex ); + this->cancel = true; + while ( ! this->terminated && exitWaitElapsed < delay ) { + epicsGuardRelease < epicsMutex > unguard ( guard ); + this->event.signal (); + this->exitEvent.wait ( delay - exitWaitElapsed ); + epicsTime current = epicsTime::getCurrent (); + exitWaitElapsed = current - exitWaitBegin; + } + } + catch ( std :: exception & except ) { + errlogPrintf ( + "epicsThread::exitWait(): Unexpected exception " + " \"%s\"\n", + except.what () ); + epicsThreadSleep ( epicsMin ( delay, 5.0 ) ); + } + catch ( ... ) { + errlogPrintf ( + "Non-standard unexpected exception in " + "epicsThread::exitWait()\n" ); + epicsThreadSleep ( epicsMin ( delay, 5.0 ) ); + } + return this->terminated; +} + +epicsThread::epicsThread ( + epicsThreadRunable & runableIn, const char * pName, + unsigned stackSize, unsigned priority ) : + runable ( runableIn ), id ( 0 ), pWaitReleaseFlag ( 0 ), + begin ( false ), cancel ( false ), terminated ( false ) +{ + this->id = epicsThreadCreate ( + pName, priority, stackSize, epicsThreadCallEntryPoint, + static_cast < void * > ( this ) ); + if ( ! this->id ) { + throw unableToCreateThread (); + } +} + +epicsThread::~epicsThread () throw () +{ + while ( ! this->exitWait ( 10.0 ) ) { + char nameBuf [256]; + this->getName ( nameBuf, sizeof ( nameBuf ) ); + fprintf ( stderr, + "epicsThread::~epicsThread(): " + "blocking for thread \"%s\" to exit\n", + nameBuf ); + fprintf ( stderr, + "was epicsThread object destroyed before thread exit ?\n"); + } +} + +void epicsThread::start () throw () +{ + { + epicsGuard < epicsMutex > guard ( this->mutex ); + this->begin = true; + } + this->event.signal (); +} + +bool epicsThread::isCurrentThread () const throw () +{ + return ( epicsThreadGetIdSelf () == this->id ); +} + +void epicsThread::resume () throw () +{ + epicsThreadResume ( this->id ); +} + +void epicsThread::getName ( char *name, size_t size ) const throw () +{ + epicsThreadGetName ( this->id, name, size ); +} + +epicsThreadId epicsThread::getId () const throw () +{ + return this->id; +} + +unsigned int epicsThread::getPriority () const throw () +{ + return epicsThreadGetPriority (this->id); +} + +void epicsThread::setPriority (unsigned int priority) throw () +{ + epicsThreadSetPriority (this->id, priority); +} + +bool epicsThread::priorityIsEqual (const epicsThread &otherThread) const throw () +{ + if ( epicsThreadIsEqual (this->id, otherThread.id) ) { + return true; + } + return false; +} + +bool epicsThread::isSuspended () const throw () +{ + if ( epicsThreadIsSuspended (this->id) ) { + return true; + } + return false; +} + +bool epicsThread::operator == (const epicsThread &rhs) const throw () +{ + return (this->id == rhs.id); +} + +void epicsThread::suspendSelf () throw () +{ + epicsThreadSuspendSelf (); +} + +void epicsThread::sleep (double seconds) throw () +{ + epicsThreadSleep (seconds); +} + +//epicsThread & epicsThread::getSelf () +//{ +// return * static_cast ( epicsThreadGetIdSelf () ); +//} + +const char *epicsThread::getNameSelf () throw () +{ + return epicsThreadGetNameSelf (); +} + +bool epicsThread::isOkToBlock () throw () +{ + return epicsThreadIsOkToBlock() != 0; +} + +void epicsThread::setOkToBlock(bool isOkToBlock) throw () +{ + epicsThreadSetOkToBlock(static_cast(isOkToBlock)); +} + +void epicsThreadPrivateBase::throwUnableToCreateThreadPrivate () +{ + throw epicsThreadPrivateBase::unableToCreateThreadPrivate (); +} + +void epicsThread :: show ( unsigned level ) const throw () +{ + ::printf ( "epicsThread at %p\n", this->id ); + if ( level > 0u ) { + epicsThreadShow ( this->id, level - 1 ); + if ( level > 1u ) { + ::printf ( "pWaitReleaseFlag = %p\n", this->pWaitReleaseFlag ); + ::printf ( "begin = %c, cancel = %c, terminated = %c\n", + this->begin ? 'T' : 'F', + this->cancel ? 'T' : 'F', + this->terminated ? 'T' : 'F' ); + this->runable.show ( level - 2u ); + this->mutex.show ( level - 2u ); + ::printf ( "general purpose event\n" ); + this->event.show ( level - 2u ); + ::printf ( "exit event\n" ); + this->exitEvent.show ( level - 2u ); + } + } +} + +extern "C" { + static epicsThreadOnceId okToBlockOnce = EPICS_THREAD_ONCE_INIT; + epicsThreadPrivateId okToBlockPrivate; + static const int okToBlockNo = 0; + static const int okToBlockYes = 1; + + static void epicsThreadOnceIdInit(void *) + { + okToBlockPrivate = epicsThreadPrivateCreate(); + } + + int epicsShareAPI epicsThreadIsOkToBlock(void) + { + const int *pokToBlock; + epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); + pokToBlock = (int *) epicsThreadPrivateGet(okToBlockPrivate); + return (pokToBlock ? *pokToBlock : 0); + } + + void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock) + { + const int *pokToBlock; + epicsThreadOnce(&okToBlockOnce, epicsThreadOnceIdInit, NULL); + pokToBlock = (isOkToBlock) ? &okToBlockYes : &okToBlockNo; + epicsThreadPrivateSet(okToBlockPrivate, (void *)pokToBlock); + } + + epicsThreadId epicsShareAPI epicsThreadMustCreate ( + const char *name, unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) + { + epicsThreadId id = epicsThreadCreate ( + name, priority, stackSize, funptr, parm ); + assert ( id ); + return id; + } +} // extern "C" + +// Ensure the main thread gets a unique ID +epicsThreadId epicsThreadMainId = epicsThreadGetIdSelf(); diff --git a/src/libCom/osi/epicsThread.h b/src/libCom/osi/epicsThread.h new file mode 100644 index 000000000..fa0a6bd70 --- /dev/null +++ b/src/libCom/osi/epicsThread.h @@ -0,0 +1,233 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef epicsThreadh +#define epicsThreadh + +#include + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*EPICSTHREADFUNC)(void *parm); + +#define epicsThreadPriorityMax 99 +#define epicsThreadPriorityMin 0 + +/* some generic values */ +#define epicsThreadPriorityLow 10 +#define epicsThreadPriorityMedium 50 +#define epicsThreadPriorityHigh 90 + +/* some iocCore specific values */ +#define epicsThreadPriorityCAServerLow 20 +#define epicsThreadPriorityCAServerHigh 40 +#define epicsThreadPriorityScanLow 60 +#define epicsThreadPriorityScanHigh 70 +#define epicsThreadPriorityIocsh 91 +#define epicsThreadPriorityBaseMax 91 + +/* stack sizes for each stackSizeClass are implementation and CPU dependent */ +typedef enum { + epicsThreadStackSmall, epicsThreadStackMedium, epicsThreadStackBig +} epicsThreadStackSizeClass; + +typedef enum { + epicsThreadBooleanStatusFail, epicsThreadBooleanStatusSuccess +} epicsThreadBooleanStatus; + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize( + epicsThreadStackSizeClass size); + +/* (epicsThreadId)0 is guaranteed to be an invalid thread id */ +typedef struct epicsThreadOSD *epicsThreadId; + +typedef epicsThreadId epicsThreadOnceId; +#define EPICS_THREAD_ONCE_INIT 0 + +epicsShareFunc void epicsShareAPI epicsThreadOnce( + epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg); + +epicsShareFunc void epicsShareAPI epicsThreadExitMain(void); + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate ( + const char * name, unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void * parm ); +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadMustCreate ( + const char * name, unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void * parm ); +epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void); +epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadId id); +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority( + epicsThreadId id); +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPrioritySelf(void); +epicsShareFunc void epicsShareAPI epicsThreadSetPriority( + epicsThreadId id,unsigned int priority); +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI + epicsThreadHighestPriorityLevelBelow ( + unsigned int priority, unsigned *pPriorityJustBelow); +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI + epicsThreadLowestPriorityLevelAbove ( + unsigned int priority, unsigned *pPriorityJustAbove); +epicsShareFunc int epicsShareAPI epicsThreadIsEqual( + epicsThreadId id1, epicsThreadId id2); +epicsShareFunc int epicsShareAPI epicsThreadIsSuspended(epicsThreadId id); +epicsShareFunc void epicsShareAPI epicsThreadSleep(double seconds); +epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum(void); +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void); +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId(const char *name); + +epicsShareFunc const char * epicsShareAPI epicsThreadGetNameSelf(void); + +/* For epicsThreadGetName name is guaranteed to be null terminated */ +/* size is size of buffer to hold name (including terminator) */ +/* Failure results in an empty string stored in name */ +epicsShareFunc void epicsShareAPI epicsThreadGetName( + epicsThreadId id, char *name, size_t size); + +epicsShareFunc int epicsShareAPI epicsThreadIsOkToBlock(void); +epicsShareFunc void epicsShareAPI epicsThreadSetOkToBlock(int isOkToBlock); + +epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level); +epicsShareFunc void epicsShareAPI epicsThreadShow( + epicsThreadId id,unsigned int level); + +typedef struct epicsThreadPrivateOSD * epicsThreadPrivateId; +epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void); +epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id); +epicsShareFunc void epicsShareAPI epicsThreadPrivateSet(epicsThreadPrivateId,void *); +epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet(epicsThreadPrivateId); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +#include "epicsEvent.h" +#include "epicsMutex.h" + +class epicsShareClass epicsThreadRunable { +public: + virtual ~epicsThreadRunable () = 0; + virtual void run () = 0; + virtual void show ( unsigned int level ) const; +}; + +extern "C" void epicsThreadCallEntryPoint ( void * ); + +class epicsShareClass epicsThread { +public: + epicsThread ( epicsThreadRunable &,const char *name, unsigned int stackSize, + unsigned int priority=epicsThreadPriorityLow ); + ~epicsThread () throw (); + void start () throw (); + void exitWait () throw (); + bool exitWait ( const double delay ) throw (); + static void exit (); + void resume () throw (); + void getName ( char * name, size_t size ) const throw (); + epicsThreadId getId () const throw (); + unsigned int getPriority () const throw (); + void setPriority ( unsigned int ) throw (); + bool priorityIsEqual ( const epicsThread & ) const throw (); + bool isSuspended () const throw (); + bool isCurrentThread () const throw (); + bool operator == ( const epicsThread & ) const throw (); + void show ( unsigned level ) const throw (); + /* these operate on the current thread */ + static void suspendSelf () throw (); + static void sleep (double seconds) throw (); + /* static epicsThread & getSelf (); */ + static const char * getNameSelf () throw (); + static bool isOkToBlock () throw (); + static void setOkToBlock ( bool isOkToBlock ) throw (); + + /* exceptions */ + class unableToCreateThread; +private: + epicsThreadRunable & runable; + epicsThreadId id; + epicsMutex mutex; + epicsEvent event; + epicsEvent exitEvent; + bool * pWaitReleaseFlag; + bool begin; + bool cancel; + bool terminated; + + bool beginWait () throw (); + epicsThread ( const epicsThread & ); + epicsThread & operator = ( const epicsThread & ); + friend void epicsThreadCallEntryPoint ( void * ); + void printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ); + /* exceptions */ + class exitException {}; +}; + +class epicsShareClass epicsThreadPrivateBase { +public: + class unableToCreateThreadPrivate {}; /* exception */ +protected: + static void throwUnableToCreateThreadPrivate (); +}; + +template < class T > +class epicsThreadPrivate : + private epicsThreadPrivateBase { +public: + epicsThreadPrivate (); + ~epicsThreadPrivate () throw (); + T * get () const throw (); + void set (T *) throw (); +private: + epicsThreadPrivateId id; +}; + +#endif /* __cplusplus */ + +#include "osdThread.h" + +#ifdef __cplusplus + +template +inline epicsThreadPrivate::epicsThreadPrivate () +{ + this->id = epicsThreadPrivateCreate (); + if ( this->id == 0 ) { + epicsThreadPrivateBase::throwUnableToCreateThreadPrivate (); + } +} + +template +inline epicsThreadPrivate::~epicsThreadPrivate () throw () +{ + epicsThreadPrivateDelete ( this->id ); +} + +template +inline T *epicsThreadPrivate::get () const throw () +{ + return static_cast ( epicsThreadPrivateGet (this->id) ); +} + +template +inline void epicsThreadPrivate::set (T *pIn) throw () +{ + epicsThreadPrivateSet ( this->id, static_cast (pIn) ); +} + +#endif /* ifdef __cplusplus */ + +#endif /* epicsThreadh */ diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp new file mode 100644 index 000000000..70f282e21 --- /dev/null +++ b/src/libCom/osi/epicsTime.cpp @@ -0,0 +1,1034 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsTime.cpp */ +/* Author Jeffrey O. Hill */ + +// Notes: +// 1) The epicsTime::nSec field is not public and so it could be +// changed to work more like the fractional seconds field in the NTP time +// stamp. That would significantly improve the precision of epicsTime on +// 64 bit architectures. +// + +#include + +#include +#include +#include +#include +#include +#include +#include // vxWorks 6.0 requires this include + +#define epicsExportSharedSymbols +#include "epicsStdioRedirect.h" +#include "locationException.h" +#include "epicsAssert.h" +#include "epicsVersion.h" +#include "envDefs.h" +#include "epicsTime.h" +#include "osiSock.h" /* pull in struct timeval */ +#include "epicsStdio.h" + +static const char pEpicsTimeVersion[] = + "@(#) " EPICS_VERSION_STRING ", Common Utilities Library " __DATE__; + +// +// useful public constants +// +static const unsigned mSecPerSec = 1000u; +static const unsigned uSecPerMSec = 1000u; +static const unsigned uSecPerSec = uSecPerMSec * mSecPerSec; +static const unsigned nSecPerUSec = 1000u; +static const unsigned nSecPerSec = nSecPerUSec * uSecPerSec; +static const unsigned nSecFracDigits = 9u; + + +// Timescale conversion data + +static const unsigned long NTP_TIME_AT_POSIX_EPOCH = 2208988800ul; +static const unsigned long NTP_TIME_AT_EPICS_EPOCH = + NTP_TIME_AT_POSIX_EPOCH + POSIX_TIME_AT_EPICS_EPOCH; + +// +// epicsTime (const unsigned long secIn, const unsigned long nSecIn) +// +inline epicsTime::epicsTime (const unsigned long secIn, const unsigned long nSecIn) : + secPastEpoch ( nSecIn / nSecPerSec + secIn ), nSec ( nSecIn % nSecPerSec ) {} + +// +// epicsTimeLoadTimeInit +// +class epicsTimeLoadTimeInit { +public: + epicsTimeLoadTimeInit (); + double epicsEpochOffset; // seconds + double time_tSecPerTick; // seconds (both NTP and EPICS use int sec) + unsigned long epicsEpochOffsetAsAnUnsignedLong; + bool useDiffTimeOptimization; +}; + +static const epicsTimeLoadTimeInit lti; + +// +// epicsTimeLoadTimeInit () +// +epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () +{ + // All we know about time_t is that it is an arithmetic type. + time_t t_zero = static_cast (0); + time_t t_one = static_cast (1); + this->time_tSecPerTick = difftime (t_one, t_zero); + + /* The EPICS epoch (1/1/1990 00:00:00UTC) was 631152000 seconds after + * the ANSI epoch (1/1/1970 00:00:00UTC) + * Convert this offset into time_t units, however this must not be + * calculated using local time (i.e. using mktime() or similar), since + * in the UK the ANSI Epoch had daylight saving time in effect, and + * the value calculated would be 3600 seconds wrong.*/ + this->epicsEpochOffset = + (double) POSIX_TIME_AT_EPICS_EPOCH / this->time_tSecPerTick; + + if (this->time_tSecPerTick == 1.0 && + this->epicsEpochOffset <= ULONG_MAX && + this->epicsEpochOffset >= 0) { + // We can use simpler code on Posix-compliant systems + this->useDiffTimeOptimization = true; + this->epicsEpochOffsetAsAnUnsignedLong = + static_cast(this->epicsEpochOffset); + } else { + // Forced to use the slower but correct code + this->useDiffTimeOptimization = false; + this->epicsEpochOffsetAsAnUnsignedLong = 0; + } +} + +// +// epicsTime::addNanoSec () +// +// many of the UNIX timestamp formats have nano sec stored as a long +// +inline void epicsTime::addNanoSec (long nSecAdj) +{ + double secAdj = static_cast (nSecAdj) / nSecPerSec; + *this += secAdj; +} + +// +// epicsTime (const time_t_wrapper &tv) +// +epicsTime::epicsTime ( const time_t_wrapper & ansiTimeTicks ) +{ + // + // try to directly map time_t into an unsigned long integer because this is + // faster on systems w/o hardware floating point and a simple integer type time_t. + // + if ( lti.useDiffTimeOptimization ) { + // LONG_MAX is used here and not ULONG_MAX because some systems (linux) + // still store time_t as a long. + if ( ansiTimeTicks.ts > 0 && ansiTimeTicks.ts <= LONG_MAX ) { + unsigned long ticks = static_cast < unsigned long > ( ansiTimeTicks.ts ); + if ( ticks >= lti.epicsEpochOffsetAsAnUnsignedLong ) { + this->secPastEpoch = ticks - lti.epicsEpochOffsetAsAnUnsignedLong; + } + else { + this->secPastEpoch = ( ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) + ticks; + } + this->nSec = 0; + return; + } + } + + // + // otherwise map time_t, which ANSI C and POSIX define as any arithmetic type, + // into type double + // + double sec = ansiTimeTicks.ts * lti.time_tSecPerTick - lti.epicsEpochOffset; + + // + // map into the the EPICS time stamp range (which allows rollover) + // + static double uLongMax = static_cast (ULONG_MAX); + if ( sec < 0.0 ) { + if ( sec < -uLongMax ) { + sec = sec + static_cast ( -sec / uLongMax ) * uLongMax; + } + sec += uLongMax; + } + else if ( sec > uLongMax ) { + sec = sec - static_cast ( sec / uLongMax ) * uLongMax; + } + + this->secPastEpoch = static_cast ( sec ); + this->nSec = static_cast ( ( sec-this->secPastEpoch ) * nSecPerSec ); +} + +epicsTime::epicsTime (const epicsTimeStamp &ts) +{ + if ( ts.nsec < nSecPerSec ) { + this->secPastEpoch = ts.secPastEpoch; + this->nSec = ts.nsec; + } + else { + throw std::logic_error ( + "epicsTimeStamp has overflow in nano-seconds field" ); + } +} + +epicsTime::epicsTime () : + secPastEpoch(0u), nSec(0u) {} + +epicsTime::epicsTime (const epicsTime &t) : + secPastEpoch (t.secPastEpoch), nSec (t.nSec) {} + +epicsTime epicsTime::getCurrent () +{ + epicsTimeStamp current; + int status = epicsTimeGetCurrent (¤t); + if (status) { + throwWithLocation ( unableToFetchCurrentTime () ); + } + return epicsTime ( current ); +} + +epicsTime epicsTime::getEvent (const epicsTimeEvent &event) +{ + epicsTimeStamp current; + int status = epicsTimeGetEvent (¤t, event); + if (status) { + throwWithLocation ( unableToFetchCurrentTime () ); + } + return epicsTime ( current ); +} + +// +// operator time_t_wrapper () +// +epicsTime::operator time_t_wrapper () const +{ + time_t_wrapper wrap; + + if ( lti.useDiffTimeOptimization ) { + if ( this->secPastEpoch < ULONG_MAX - lti.epicsEpochOffsetAsAnUnsignedLong ) { + wrap.ts = static_cast ( this->secPastEpoch + lti.epicsEpochOffsetAsAnUnsignedLong ); + return wrap; + } + } + + // + // map type double into time_t which ansi C defines as some arithmetic type + // + double tmp = (this->secPastEpoch + lti.epicsEpochOffset) / lti.time_tSecPerTick; + tmp += (this->nSec / lti.time_tSecPerTick) / nSecPerSec; + + wrap.ts = static_cast ( tmp ); + + return wrap; +} + +// +// convert to ANSI C struct tm (with nano seconds) adjusted for the local time zone +// +epicsTime::operator local_tm_nano_sec () const +{ + time_t_wrapper ansiTimeTicks = *this; + + local_tm_nano_sec tm; + + int status = epicsTime_localtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); + if ( status != epicsTimeOK ) { + throw std::logic_error ( "epicsTime_localtime failed" ); + } + + tm.nSec = this->nSec; + + return tm; +} + +// +// convert to ANSI C struct tm (with nano seconds) adjusted for UTC +// +epicsTime::operator gm_tm_nano_sec () const +{ + time_t_wrapper ansiTimeTicks = *this; + + gm_tm_nano_sec tm; + + int status = epicsTime_gmtime ( &ansiTimeTicks.ts, &tm.ansi_tm ); + if ( status != epicsTimeOK ) { + throw std::logic_error ( "epicsTime_gmtime failed" ); + } + + tm.nSec = this->nSec; + + return tm; +} + +// +// epicsTime (const local_tm_nano_sec &tm) +// +epicsTime::epicsTime (const local_tm_nano_sec &tm) +{ + static const time_t mktimeFailure = static_cast (-1); + time_t_wrapper ansiTimeTicks; + struct tm tmp = tm.ansi_tm; + + ansiTimeTicks.ts = mktime (&tmp); + if (ansiTimeTicks.ts == mktimeFailure) { + throwWithLocation ( formatProblemWithStructTM () ); + } + + *this = epicsTime (ansiTimeTicks); + + unsigned long nSecAdj = tm.nSec % nSecPerSec; + unsigned long secAdj = tm.nSec / nSecPerSec; + *this = epicsTime ( this->secPastEpoch+secAdj, this->nSec+nSecAdj ); +} + +// +// operator struct timespec () +// +epicsTime::operator struct timespec () const +{ + struct timespec ts; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + ts.tv_sec = ansiTimeTicks.ts; + ts.tv_nsec = static_cast (this->nSec); + return ts; +} + +// +// epicsTime (const struct timespec &ts) +// +epicsTime::epicsTime (const struct timespec &ts) +{ + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks.ts = ts.tv_sec; + *this = epicsTime (ansiTimeTicks); + this->addNanoSec (ts.tv_nsec); +} + +// +// operator struct timeval () +// +epicsTime::operator struct timeval () const +{ + struct timeval ts; + time_t_wrapper ansiTimeTicks; + + ansiTimeTicks = *this; + // On Posix systems timeval :: tv_sec is a time_t so this can be + // a direct assignement. On other systems I dont know that we can + // guarantee that time_t and timeval :: tv_sec will have the + // same epoch or have the same scaling factor to discrete seconds. + // For example, on windows time_t changed recently to a 64 bit + // quantity but timeval is still a long. That can cause problems + // on 32 bit systems. So technically, we should have an os + // dependent conversion between time_t and timeval :: tv_sec? + ts.tv_sec = ansiTimeTicks.ts; + ts.tv_usec = static_cast < long > ( this->nSec / nSecPerUSec ); + return ts; +} + +// +// epicsTime (const struct timeval &ts) +// +epicsTime::epicsTime (const struct timeval &ts) +{ + time_t_wrapper ansiTimeTicks; + // On Posix systems timeval :: tv_sec is a time_t so this can be + // a direct assignement. On other systems I dont know that we can + // guarantee that time_t and timeval :: tv_sec will have the + // same epoch or have the same scaling factor to discrete seconds. + // For example, on windows time_t changed recently to a 64 bit + // quantity but timeval is still a long. That can cause problems + // on 32 bit systems. So technically, we should have an os + // dependent conversion between time_t and timeval :: tv_sec? + ansiTimeTicks.ts = ts.tv_sec; + *this = epicsTime (ansiTimeTicks); + this->addNanoSec (ts.tv_usec * nSecPerUSec); +} + + +static const double NTP_FRACTION_DENOMINATOR = 1.0 + 0xffffffff; + +struct l_fp { /* NTP time stamp */ + epicsUInt32 l_ui; /* sec past NTP epoch */ + epicsUInt32 l_uf; /* fractional seconds */ +}; + +// +// epicsTime::l_fp () +// +epicsTime::operator l_fp () const +{ + l_fp ts; + ts.l_ui = this->secPastEpoch + NTP_TIME_AT_EPICS_EPOCH; + ts.l_uf = static_cast < unsigned long > + ( ( this->nSec * NTP_FRACTION_DENOMINATOR ) / nSecPerSec ); + return ts; +} + +// +// epicsTime::epicsTime ( const l_fp & ts ) +// +epicsTime::epicsTime ( const l_fp & ts ) +{ + this->secPastEpoch = ts.l_ui - NTP_TIME_AT_EPICS_EPOCH; + this->nSec = static_cast < unsigned long > + ( ( ts.l_uf / NTP_FRACTION_DENOMINATOR ) * nSecPerSec ); +} + +epicsTime::operator epicsTimeStamp () const +{ + if ( this->nSec >= nSecPerSec ) { + throw std::logic_error ( + "epicsTimeStamp has overflow in nano-seconds field?" ); + } + epicsTimeStamp ts; + // + // trucation by design + // ------------------- + // epicsTime::secPastEpoch is based on ulong and has much greater range + // on 64 bit hosts than the orginal epicsTimeStamp::secPastEpoch. The + // epicsTimeStamp::secPastEpoch is based on epicsUInt32 so that it will + // match the original network protocol. Of course one can anticipate + // that eventually, a epicsUInt64 based network time stamp will be + // introduced when 64 bit architectures are more ubiquitous. + // + // Truncation usually works fine here because the routines in this code + // that compute time stamp differences and compare time stamps produce + // good results when the operands are on either side of a time stamp + // rollover as long as the difference between the operands does not exceed + // 1/2 of full range. + // + ts.secPastEpoch = static_cast < epicsUInt32 > ( this->secPastEpoch ); + ts.nsec = static_cast < epicsUInt32 > ( this->nSec ); + return ts; +} + +// Break up a format string into "%0f" +// (where in an unsigned integer) +// Result: +// A) Copies a prefix which is valid for ANSI strftime into the supplied +// buffer setting the buffer to an empty string if no prefix is present. +// B) Indicates whether a valid "%0f]" is present or not and if so +// specifying its nnnn +// C) returning a pointer to the postfix (which might be passed again +// to fracFormatFind. +static const char * fracFormatFind ( + const char * const pFormat, + char * const pPrefixBuf, + const size_t prefixBufLen, + bool & fracFmtFound, + unsigned long & fracFmtWidth ) +{ + assert ( prefixBufLen > 1 ); + unsigned long width = ULONG_MAX; + bool fracFound = false; + const char * pAfter = pFormat; + const char * pFmt = pFormat; + while ( *pFmt != '\0' ) { + if ( *pFmt == '%' ) { + if ( pFmt[1] == '%' ) { + pFmt++; + } + else if ( pFmt[1] == 'f' ) { + fracFound = true; + pAfter = & pFmt[2]; + break; + } + else { + errno = 0; + char * pAfterTmp; + unsigned long result = strtoul ( pFmt + 1, & pAfterTmp, 10 ); + if ( errno == 0 && *pAfterTmp == 'f' && result > 0 ) { + width = result; + fracFound = true; + pAfter = pAfterTmp + 1; + break; + } + } + } + pFmt++; + pAfter = pFmt; + } + size_t len = pFmt - pFormat; + if ( len < prefixBufLen ) { + strncpy ( pPrefixBuf, pFormat, len ); + pPrefixBuf [ len ] = '\0'; + if ( fracFound ) { + fracFmtFound = true; + fracFmtWidth = width; + } + else { + fracFmtFound = false; + } + } + else { + strncpy ( pPrefixBuf, "", prefixBufLen - 1 ); + pPrefixBuf [ prefixBufLen - 1 ] = '\0'; + fracFmtFound = false; + pAfter = ""; + } + + return pAfter; +} + +// +// size_t epicsTime::strftime () +// +size_t epicsTime::strftime ( + char * pBuff, size_t bufLength, const char * pFormat ) const +{ + if ( bufLength == 0u ) { + return 0u; + } + + // presume that EPOCH date is an uninitialized time stamp + if ( this->secPastEpoch == 0 && this->nSec == 0u ) { + strncpy ( pBuff, "", bufLength ); + pBuff[bufLength-1] = '\0'; + return strlen ( pBuff ); + } + + char * pBufCur = pBuff; + const char * pFmt = pFormat; + size_t bufLenLeft = bufLength; + while ( *pFmt != '\0' && bufLenLeft > 1 ) { + // look for "%0f" at the end (used for fractional second formatting) + char strftimePrefixBuf [256]; + bool fracFmtFound; + unsigned long fracWid = 0; + pFmt = fracFormatFind ( + pFmt, + strftimePrefixBuf, sizeof ( strftimePrefixBuf ), + fracFmtFound, fracWid ); + + // nothing more in the string, then quit + if ( ! ( strftimePrefixBuf[0] != '\0' || fracFmtFound ) ) { + break; + } + // all but fractional seconds use strftime formatting + if ( strftimePrefixBuf[0] != '\0' ) { + local_tm_nano_sec tmns = *this; + size_t strftimeNumChar = :: strftime ( + pBufCur, bufLenLeft, strftimePrefixBuf, & tmns.ansi_tm ); + pBufCur [ strftimeNumChar ] = '\0'; + pBufCur += strftimeNumChar; + bufLenLeft -= strftimeNumChar; + } + + // fractional seconds formating + if ( fracFmtFound && bufLenLeft > 1 ) { + if ( fracWid > nSecFracDigits ) { + fracWid = nSecFracDigits; + } + // verify that there are enough chars left for the fractional seconds + if ( fracWid < bufLenLeft ) + { + local_tm_nano_sec tmns = *this; + if ( tmns.nSec < nSecPerSec ) { + // divisors for fraction (see below) + static const unsigned long div[] = { + static_cast < unsigned long > ( 1e9 ), + static_cast < unsigned long > ( 1e8 ), + static_cast < unsigned long > ( 1e7 ), + static_cast < unsigned long > ( 1e6 ), + static_cast < unsigned long > ( 1e5 ), + static_cast < unsigned long > ( 1e4 ), + static_cast < unsigned long > ( 1e3 ), + static_cast < unsigned long > ( 1e2 ), + static_cast < unsigned long > ( 1e1 ), + static_cast < unsigned long > ( 1e0 ) + }; + // round and convert nanosecs to integer of correct range + unsigned long frac = tmns.nSec + div[fracWid] / 2; + frac %= static_cast < unsigned long > ( 1e9 ); + frac /= div[fracWid]; + char fracFormat[32]; + sprintf ( fracFormat, "%%0%lulu", fracWid ); + int status = epicsSnprintf ( pBufCur, bufLenLeft, fracFormat, frac ); + if ( status > 0 ) { + unsigned long nChar = static_cast < unsigned long > ( status ); + if ( nChar >= bufLenLeft ) { + nChar = bufLenLeft - 1; + } + pBufCur[nChar] = '\0'; + pBufCur += nChar; + bufLenLeft -= nChar; + } + } + else { + static const char pOVF [] = "OVF"; + size_t tmpLen = sizeof ( pOVF ) - 1; + if ( tmpLen >= bufLenLeft ) { + tmpLen = bufLenLeft - 1; + } + strncpy ( pBufCur, pOVF, tmpLen ); + pBufCur[tmpLen] = '\0'; + pBufCur += tmpLen; + bufLenLeft -= tmpLen; + } + } + else { + static const char pDoesntFit [] = "************"; + size_t tmpLen = sizeof ( pDoesntFit ) - 1; + if ( tmpLen >= bufLenLeft ) { + tmpLen = bufLenLeft - 1; + } + strncpy ( pBufCur, pDoesntFit, tmpLen ); + pBufCur[tmpLen] = '\0'; + pBufCur += tmpLen; + bufLenLeft -= tmpLen; + break; + } + } + } + return pBufCur - pBuff; +} + +// +// epicsTime::show (unsigned) +// +void epicsTime::show ( unsigned level ) const +{ + char bigBuffer[256]; + + size_t numChar = this->strftime ( bigBuffer, sizeof ( bigBuffer ), + "%a %b %d %Y %H:%M:%S.%09f" ); + if ( numChar > 0 ) { + printf ( "epicsTime: %s\n", bigBuffer ); + } + + if ( level > 1 ) { + // this also supresses the "defined, but not used" + // warning message + printf ( "epicsTime: revision \"%s\"\n", + pEpicsTimeVersion ); + } + +} + +// +// epicsTime::operator + (const double &rhs) +// +// rhs has units seconds +// +epicsTime epicsTime::operator + (const double &rhs) const +{ + unsigned long newSec, newNSec, secOffset, nSecOffset; + double fnsec; + + if (rhs >= 0) { + secOffset = static_cast (rhs); + fnsec = rhs - secOffset; + nSecOffset = static_cast ( (fnsec * nSecPerSec) + 0.5 ); + + newSec = this->secPastEpoch + secOffset; // overflow expected + newNSec = this->nSec + nSecOffset; + if (newNSec >= nSecPerSec) { + newSec++; // overflow expected + newNSec -= nSecPerSec; + } + } + else { + secOffset = static_cast (-rhs); + fnsec = rhs + secOffset; + nSecOffset = static_cast ( (-fnsec * nSecPerSec) + 0.5 ); + + newSec = this->secPastEpoch - secOffset; // underflow expected + if (this->nSec>=nSecOffset) { + newNSec = this->nSec - nSecOffset; + } + else { + // borrow + newSec--; // underflow expected + newNSec = this->nSec + (nSecPerSec - nSecOffset); + } + } + return epicsTime (newSec, newNSec); +} + +// +// operator - +// +// To make this code robust during timestamp rollover events +// time stamp differences greater than one half full scale are +// interpreted as rollover situations: +// +// when RHS is greater than THIS: +// RHS-THIS > one half full scale => return THIS + (ULONG_MAX-RHS) +// RHS-THIS <= one half full scale => return -(RHS-THIS) +// +// when THIS is greater than or equal to RHS +// THIS-RHS > one half full scale => return -(RHS + (ULONG_MAX-THIS)) +// THIS-RHS <= one half full scale => return THIS-RHS +// +double epicsTime::operator - (const epicsTime &rhs) const +{ + double nSecRes, secRes; + + // + // first compute the difference between the nano-seconds members + // + // nano sec member is not allowed to be greater that 1/2 full scale + // so the unsigned to signed conversion is ok + // + if (this->nSec>=rhs.nSec) { + nSecRes = this->nSec - rhs.nSec; + } + else { + nSecRes = rhs.nSec - this->nSec; + nSecRes = -nSecRes; + } + + // + // next compute the difference between the seconds members + // and invert the sign of the nano seconds result if there + // is a range violation + // + if (this->secPastEpochsecPastEpoch; + if (secRes > ULONG_MAX/2) { + // + // In this situation where the difference is more than + // 68 years assume that the seconds counter has rolled + // over and compute the "wrap around" difference + // + secRes = 1 + (ULONG_MAX-secRes); + nSecRes = -nSecRes; + } + else { + secRes = -secRes; + } + } + else { + secRes = this->secPastEpoch - rhs.secPastEpoch; + if (secRes > ULONG_MAX/2) { + // + // In this situation where the difference is more than + // 68 years assume that the seconds counter has rolled + // over and compute the "wrap around" difference + // + secRes = 1 + (ULONG_MAX-secRes); + secRes = -secRes; + nSecRes = -nSecRes; + } + } + + return secRes + nSecRes/nSecPerSec; +} + +// +// operator <= +// +bool epicsTime::operator <= (const epicsTime &rhs) const +{ + bool rc; + + if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = true; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = false; + } + } + else if (this->secPastEpoch>rhs.secPastEpoch) { + if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = false; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = true; + } + } + else { + if (this->nSec<=rhs.nSec) { + rc = true; + } + else { + rc = false; + } + } + return rc; +} + +// +// operator < +// +bool epicsTime::operator < (const epicsTime &rhs) const +{ + bool rc; + + if (this->secPastEpochsecPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = true; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = false; + } + } + else if (this->secPastEpoch>rhs.secPastEpoch) { + if (this->secPastEpoch-rhs.secPastEpoch < ULONG_MAX/2) { + // + // In this situation where the difference is less than + // 69 years compute the expected result + // + rc = false; + } + else { + // + // In this situation where the difference is more than + // 69 years assume that the seconds counter has rolled + // over and compute the "wrap around" result + // + rc = true; + } + } + else { + if (this->nSec epicsTime (*pRight); + } + catch ( ... ) { + return 0; + } + } + epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual (const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight) + { + try { + return epicsTime (*pLeft) >= epicsTime (*pRight); + } + catch ( ... ) { + return 0; + } + } + epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime (char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS) + { + try { + return epicsTime(*pTS).strftime (pBuff, bufLength, pFormat); + } + catch ( ... ) { + return 0; + } + } + epicsShareFunc void epicsShareAPI epicsTimeShow (const epicsTimeStamp *pTS, unsigned interestLevel) + { + try { + epicsTime(*pTS).show (interestLevel); + } + catch ( ... ) { + printf ( "Invalid epicsTimeStamp\n" ); + } + } +} + diff --git a/src/libCom/osi/epicsTime.h b/src/libCom/osi/epicsTime.h new file mode 100644 index 000000000..a36d6afe3 --- /dev/null +++ b/src/libCom/osi/epicsTime.h @@ -0,0 +1,341 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsTime.h */ +/* Author Jeffrey O. Hill */ + +#ifndef epicsTimehInclude +#define epicsTimehInclude + +#include + +#include "shareLib.h" +#include "epicsTypes.h" +#include "osdTime.h" + +/* The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC */ +#define POSIX_TIME_AT_EPICS_EPOCH 631152000u + +/* epics time stamp for C interface*/ +typedef struct epicsTimeStamp { + epicsUInt32 secPastEpoch; /* seconds since 0000 Jan 1, 1990 */ + epicsUInt32 nsec; /* nanoseconds within second */ +} epicsTimeStamp; + +/*TS_STAMP is deprecated */ +#define TS_STAMP epicsTimeStamp + + +struct timespec; /* POSIX real time */ +struct timeval; /* BSD */ +struct l_fp; /* NTP timestamp */ + +#ifdef __cplusplus + +/* + * extend ANSI C RTL "struct tm" to include nano seconds within a second + * and a struct tm that is adjusted for the local timezone + */ +struct local_tm_nano_sec { + struct tm ansi_tm; /* ANSI C time details */ + unsigned long nSec; /* nano seconds extension */ +}; + +/* + * extend ANSI C RTL "struct tm" to includes nano seconds within a second + * and a struct tm that is adjusted for GMT (UTC) + */ +struct gm_tm_nano_sec { + struct tm ansi_tm; /* ANSI C time details */ + unsigned long nSec; /* nano seconds extension */ +}; + +/* + * wrapping this in a struct allows conversion to and + * from ANSI time_t but does not allow unexpected + * conversions to occur + */ +struct time_t_wrapper { + time_t ts; +}; + +class epicsShareClass epicsTimeEvent +{ +public: + epicsTimeEvent (const int &number); + operator int () const; +private: + int eventNumber; +}; + +class epicsShareClass epicsTime +{ +public: + /* exceptions */ + class unableToFetchCurrentTime {}; + class formatProblemWithStructTM {}; + + epicsTime (); + epicsTime ( const epicsTime & t ); + + static epicsTime getEvent ( const epicsTimeEvent & ); + static epicsTime getCurrent (); + + /* convert to and from EPICS epicsTimeStamp format */ + operator epicsTimeStamp () const; + epicsTime ( const epicsTimeStamp & ts ); + epicsTime & operator = ( const epicsTimeStamp & ); + + /* convert to and from ANSI time_t */ + operator time_t_wrapper () const; + epicsTime ( const time_t_wrapper & ); + epicsTime & operator = ( const time_t_wrapper & ); + + /* + * convert to and from ANSI Cs "struct tm" (with nano seconds) + * adjusted for the local time zone + */ + operator local_tm_nano_sec () const; + epicsTime ( const local_tm_nano_sec & ); + epicsTime & operator = ( const local_tm_nano_sec & ); + + /* + * convert to ANSI Cs "struct tm" (with nano seconds) + * adjusted for GM time (UTC) + */ + operator gm_tm_nano_sec () const; + + /* convert to and from POSIX RTs "struct timespec" */ + operator struct timespec () const; + epicsTime ( const struct timespec & ); + epicsTime & operator = ( const struct timespec & ); + + /* convert to and from BSDs "struct timeval" */ + operator struct timeval () const; + epicsTime ( const struct timeval & ); + epicsTime & operator = ( const struct timeval & ); + + /* convert to and from NTP timestamp format */ + operator l_fp () const; + epicsTime ( const l_fp & ); + epicsTime & operator = ( const l_fp & ); + + /* convert to and from WIN32s FILETIME (implemented only on WIN32) */ + operator struct _FILETIME () const; + epicsTime ( const struct _FILETIME & ); + epicsTime & operator = ( const struct _FILETIME & ); + + /* arithmetic operators */ + double operator- ( const epicsTime & ) const; /* returns seconds */ + epicsTime operator+ ( const double & ) const; /* add rhs seconds */ + epicsTime operator- ( const double & ) const; /* subtract rhs seconds */ + epicsTime operator+= ( const double & ); /* add rhs seconds */ + epicsTime operator-= ( const double & ); /* subtract rhs seconds */ + + /* comparison operators */ + bool operator == ( const epicsTime & ) const; + bool operator != ( const epicsTime & ) const; + bool operator <= ( const epicsTime & ) const; + bool operator < ( const epicsTime & ) const; + bool operator >= ( const epicsTime & ) const; + bool operator > ( const epicsTime & ) const; + + /* convert current state to user-specified string */ + size_t strftime ( char * pBuff, size_t bufLength, const char * pFormat ) const; + + /* dump current state to standard out */ + void show ( unsigned interestLevel ) const; + +private: + /* + * private because: + * a) application does not break when EPICS epoch is changed + * b) no assumptions about internal storage or internal precision + * in the application + * c) it would be easy to forget which argument is nanoseconds + * and which argument is seconds (no help from compiler) + */ + epicsTime ( const unsigned long secPastEpoch, const unsigned long nSec ); + void addNanoSec ( long nanoSecAdjust ); + + unsigned long secPastEpoch; /* seconds since O000 Jan 1, 1990 */ + unsigned long nSec; /* nanoseconds within second */ +}; + +extern "C" { +#endif /* __cplusplus */ + +/* All epicsTime routines return (-1,0) for (failure,success) */ +#define epicsTimeOK 0 +#define epicsTimeERROR (-1) + +/*Some special values for eventNumber*/ +#define epicsTimeEventCurrentTime 0 +#define epicsTimeEventBestTime -1 +#define epicsTimeEventDeviceTime -2 + +/* These are implemented in the "generalTime" framework */ +epicsShareFunc int epicsShareAPI epicsTimeGetCurrent ( epicsTimeStamp * pDest ); +epicsShareFunc int epicsShareAPI epicsTimeGetEvent ( + epicsTimeStamp *pDest, int eventNumber); + +/* These are callable from an Interrupt Service Routine */ +epicsShareFunc int epicsTimeGetCurrentInt(epicsTimeStamp *pDest); +epicsShareFunc int epicsTimeGetEventInt(epicsTimeStamp *pDest, int eventNumber); + +/* convert to and from ANSI C's "time_t" */ +epicsShareFunc int epicsShareAPI epicsTimeToTime_t ( + time_t * pDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTime_t ( + epicsTimeStamp * pDest, time_t src ); + +/*convert to and from ANSI C's "struct tm" with nano seconds */ +epicsShareFunc int epicsShareAPI epicsTimeToTM ( + struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeToGMTM ( + struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTM ( + epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc ); + +/* convert to and from POSIX RT's "struct timespec" */ +epicsShareFunc int epicsShareAPI epicsTimeToTimespec ( + struct timespec * pDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTimespec ( + epicsTimeStamp * pDest, const struct timespec * pSrc ); + +/* convert to and from BSD's "struct timeval" */ +epicsShareFunc int epicsShareAPI epicsTimeToTimeval ( + struct timeval * pDest, const epicsTimeStamp * pSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromTimeval ( + epicsTimeStamp * pDest, const struct timeval * pSrc ); + +/*arithmetic operations */ +epicsShareFunc double epicsShareAPI epicsTimeDiffInSeconds ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight );/* left - right */ +epicsShareFunc void epicsShareAPI epicsTimeAddSeconds ( + epicsTimeStamp * pDest, double secondsToAdd ); /* adds seconds to *pDest */ + +/*comparison operations: returns (0,1) if (false,true) */ +epicsShareFunc int epicsShareAPI epicsTimeEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); +epicsShareFunc int epicsShareAPI epicsTimeNotEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); +epicsShareFunc int epicsShareAPI epicsTimeLessThan ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left < right */ +epicsShareFunc int epicsShareAPI epicsTimeLessThanEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left <= right) */ +epicsShareFunc int epicsShareAPI epicsTimeGreaterThan ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left > right */ +epicsShareFunc int epicsShareAPI epicsTimeGreaterThanEqual ( + const epicsTimeStamp * pLeft, const epicsTimeStamp * pRight); /*true if left >= right */ + +/*convert to ASCII string */ +epicsShareFunc size_t epicsShareAPI epicsTimeToStrftime ( + char * pBuff, size_t bufLength, const char * pFormat, const epicsTimeStamp * pTS ); + +/* dump current state to standard out */ +epicsShareFunc void epicsShareAPI epicsTimeShow ( + const epicsTimeStamp *, unsigned interestLevel ); + +/* OS dependent reentrant versions of the ANSI C interface because */ +/* vxWorks gmtime_r interface does not match POSIX standards */ +epicsShareFunc int epicsShareAPI epicsTime_localtime ( const time_t * clock, struct tm * result ); +epicsShareFunc int epicsShareAPI epicsTime_gmtime ( const time_t * clock, struct tm * result ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* inline member functions ,*/ +#ifdef __cplusplus + +/* epicsTimeEvent */ + +inline epicsTimeEvent::epicsTimeEvent (const int &number) : + eventNumber(number) {} + +inline epicsTimeEvent::operator int () const +{ + return this->eventNumber; +} + + +/* epicsTime */ + +inline epicsTime epicsTime::operator - ( const double & rhs ) const +{ + return epicsTime::operator + ( -rhs ); +} + +inline epicsTime epicsTime::operator += ( const double & rhs ) +{ + *this = epicsTime::operator + ( rhs ); + return *this; +} + +inline epicsTime epicsTime::operator -= ( const double & rhs ) +{ + *this = epicsTime::operator + ( -rhs ); + return *this; +} + +inline bool epicsTime::operator == ( const epicsTime & rhs ) const +{ + if ( this->secPastEpoch == rhs.secPastEpoch && this->nSec == rhs.nSec ) { + return true; + } + return false; +} + +inline bool epicsTime::operator != ( const epicsTime & rhs ) const +{ + return !epicsTime::operator == ( rhs ); +} + +inline bool epicsTime::operator >= ( const epicsTime & rhs ) const +{ + return ! ( *this < rhs ); +} + +inline bool epicsTime::operator > ( const epicsTime & rhs ) const +{ + return ! ( *this <= rhs ); +} + +inline epicsTime & epicsTime::operator = ( const local_tm_nano_sec & rhs ) +{ + return *this = epicsTime ( rhs ); +} + +inline epicsTime & epicsTime::operator = ( const struct timespec & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} + +inline epicsTime & epicsTime::operator = ( const epicsTimeStamp & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} + +inline epicsTime & epicsTime::operator = ( const l_fp & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} + +inline epicsTime & epicsTime::operator = ( const time_t_wrapper & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} +#endif /* __cplusplus */ + +#endif /* epicsTimehInclude */ + diff --git a/src/libCom/osi/generalTimeSup.h b/src/libCom/osi/generalTimeSup.h new file mode 100644 index 000000000..bd6627ef6 --- /dev/null +++ b/src/libCom/osi/generalTimeSup.h @@ -0,0 +1,47 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2008 Diamond Light Source Ltd +* Copyright (c) 2004 Oak Ridge National Laboratory +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_generalTimeSup_H +#define INC_generalTimeSup_H + +#include "epicsTime.h" +#include "epicsTimer.h" +#include "shareLib.h" + +#define LAST_RESORT_PRIORITY 999 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*TIMECURRENTFUN)(epicsTimeStamp *pDest); +typedef int (*TIMEEVENTFUN)(epicsTimeStamp *pDest, int event); + +epicsShareFunc int generalTimeRegisterCurrentProvider(const char *name, + int priority, TIMECURRENTFUN getTime); +epicsShareFunc int generalTimeRegisterEventProvider(const char *name, + int priority, TIMEEVENTFUN getEvent); + +/* Original names, for compatibility */ +#define generalTimeCurrentTpRegister generalTimeRegisterCurrentProvider +#define generalTimeEventTpRegister generalTimeRegisterEventProvider + +epicsShareFunc int generalTimeAddIntCurrentProvider(const char *name, + int priority, TIMECURRENTFUN getTime); +epicsShareFunc int generalTimeAddIntEventProvider(const char *name, + int priority, TIMEEVENTFUN getEvent); + +epicsShareFunc int generalTimeGetExceptPriority(epicsTimeStamp *pDest, + int *pPrio, int ignorePrio); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_generalTimeSup_H */ diff --git a/src/libCom/osi/os/Darwin/epicsMath.h b/src/libCom/osi/os/Darwin/epicsMath.h new file mode 100644 index 000000000..d540ba5d3 --- /dev/null +++ b/src/libCom/osi/os/Darwin/epicsMath.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include + +#define finite(x) isfinite(x) + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/src/libCom/osi/os/Darwin/osdEnv.c b/src/libCom/osi/os/Darwin/osdEnv.c new file mode 100644 index 000000000..9e3a4003c --- /dev/null +++ b/src/libCom/osi/os/Darwin/osdEnv.c @@ -0,0 +1,84 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +#include +#include +#include +#include +#include + +/* + * Starting in Mac OS X 10.5 (Leopard) shared libraries and + * bundles don't have direct access to environ (man environ). + */ +# include +# define environ (*_NSGetEnviron()) + +#define epicsExportSharedSymbols +#include +#include +#include +#include +#include +#include "epicsFindSymbol.h" + +/* + * Set the value of an environment variable + * Leaks memory, but the assumption is that this routine won't be + * called often enough for the leak to be a problem. + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + char *cp; + + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); + strcpy (cp, name); + strcat (cp, "="); + strcat (cp, value); + if (putenv (cp) < 0) { + errPrintf( + -1L, + __FILE__, + __LINE__, + "Failed to set environment parameter \"%s\" to \"%s\": %s\n", + name, + value, + strerror (errno)); + free (cp); + } +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/src/libCom/osi/os/Darwin/osdFindSymbol.c b/src/libCom/osi/os/Darwin/osdFindSymbol.c new file mode 100644 index 000000000..967c220cd --- /dev/null +++ b/src/libCom/osi/os/Darwin/osdFindSymbol.c @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/default/epicsFindSymbol.c */ + +#include + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + return dlopen(name, RTLD_LAZY | RTLD_GLOBAL); +} + +epicsShareFunc const char *epicsLoadError(void) +{ + return dlerror(); +} + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ + return dlsym(0, name); +} diff --git a/src/libCom/osi/os/Darwin/osdSock.h b/src/libCom/osi/os/Darwin/osdSock.h new file mode 100644 index 000000000..fc281c3d5 --- /dev/null +++ b/src/libCom/osi/os/Darwin/osdSock.h @@ -0,0 +1,82 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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: Eric Norum + */ + +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +/*#include +#include */ +#include +#include +#include +#include +#include +#include /* close() and others */ + + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof((pifreq)->ifr_name)) + +#endif /*osdSockH*/ diff --git a/src/libCom/osi/os/Darwin/osdSockAddrReuse.cpp b/src/libCom/osi/os/Darwin/osdSockAddrReuse.cpp new file mode 100644 index 000000000..12cb6fb94 --- /dev/null +++ b/src/libCom/osi/os/Darwin/osdSockAddrReuse.cpp @@ -0,0 +1,50 @@ + +/*************************************************************************\ +* 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 + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} + +/* + * SO_REUSEPORT is not in POSIX + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEPORT, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEPORT?\n"); + } +} diff --git a/src/libCom/osi/os/Darwin/osdTime.h b/src/libCom/osi/os/Darwin/osdTime.h new file mode 100644 index 000000000..7ec259607 --- /dev/null +++ b/src/libCom/osi/os/Darwin/osdTime.h @@ -0,0 +1,29 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is 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: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void convertDoubleToWakeTime(double timeout, + struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/src/libCom/osi/os/Darwin/osiFileName.h b/src/libCom/osi/os/Darwin/osiFileName.h new file mode 100644 index 000000000..646ba7f7e --- /dev/null +++ b/src/libCom/osi/os/Darwin/osiFileName.h @@ -0,0 +1,18 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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: Eric Norum + */ + +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/Linux/osdFindSymbol.c b/src/libCom/osi/os/Linux/osdFindSymbol.c new file mode 100644 index 000000000..967c220cd --- /dev/null +++ b/src/libCom/osi/os/Linux/osdFindSymbol.c @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/default/epicsFindSymbol.c */ + +#include + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + return dlopen(name, RTLD_LAZY | RTLD_GLOBAL); +} + +epicsShareFunc const char *epicsLoadError(void) +{ + return dlerror(); +} + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ + return dlsym(0, name); +} diff --git a/src/libCom/osi/os/Linux/osdSock.h b/src/libCom/osi/os/Linux/osdSock.h new file mode 100644 index 000000000..8c1f65b70 --- /dev/null +++ b/src/libCom/osi/os/Linux/osdSock.h @@ -0,0 +1,96 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * Linux specific socket include + * + * Under Linux if we dont define _POSIX_C_SOURCE or _XOPEN_SOURCE + * then none of the POSIX stuff (such as signals) can be used + * with cc -v. However if one of _POSIX_C_SOURCE or _XOPEN_SOURCE + * are defined then we cant use the socket library. Therefore I + * have been adding the following in order to use POSIX signals + * and also sockets on Linux with cc -v. What a pain.... + * + * #ifdef linux + * #define __EXTENSIONS__ + * #endif + */ + +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +/*#include +#include */ +#include +#include +#include +#include +#include +#include /* close() and others */ + + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_name)) + +#endif /*osdSockH*/ + diff --git a/src/libCom/osi/os/Linux/osdTime.h b/src/libCom/osi/os/Linux/osdTime.h new file mode 100644 index 000000000..614b35442 --- /dev/null +++ b/src/libCom/osi/os/Linux/osdTime.h @@ -0,0 +1,34 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +/* + * Linux needs this include file since the POSIX version + * causes `struct timespec' to be defined in more than one place. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsShareAPI + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/src/libCom/osi/os/Linux/osiFileName.h b/src/libCom/osi/os/Linux/osiFileName.h new file mode 100644 index 000000000..b79203992 --- /dev/null +++ b/src/libCom/osi/os/Linux/osiFileName.h @@ -0,0 +1,21 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/Linux/osiUnistd.h b/src/libCom/osi/os/Linux/osiUnistd.h new file mode 100644 index 000000000..b3ccbb735 --- /dev/null +++ b/src/libCom/osi/os/Linux/osiUnistd.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +/* + * Some systems fail to provide prototypes of these functions. + * Others provide different prototypes. + * There seems to be no way to handle this automatically, so + * if you get compile errors, just make the appropriate changes here. + */ diff --git a/src/libCom/osi/os/RTEMS/devLibVMEOSD.c b/src/libCom/osi/os/RTEMS/devLibVMEOSD.c new file mode 100644 index 000000000..82f08d95a --- /dev/null +++ b/src/libCom/osi/os/RTEMS/devLibVMEOSD.c @@ -0,0 +1,366 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* RTEMS port by Till Straumann, + * 3/2002 + * + */ + +#include +#include +#include +#include +#include "devLibVME.h" +#include + +#if defined(__PPC__) || defined(__mcf528x__) + +#if defined(__PPC__) +#include +#include +#endif + + +typedef void myISR (void *pParam); + +static myISR *isrFetch(unsigned vectorNumber, void **parg); + +/* + * this routine needs to be in the symbol table + * for this code to work correctly + */ +static void unsolicitedHandlerEPICS(int vectorNumber); + +static myISR *defaultHandlerAddr[]={ + (myISR*)unsolicitedHandlerEPICS, +}; + +/* + * Make sure that the CR/CSR addressing mode is defined. + * (it may not be in some BSPs). + */ +#ifndef VME_AM_CSR +# define VME_AM_CSR (0x2f) +#endif + +/* + * we use a translation between an EPICS encoding + * and a vxWorks encoding here + * to reduce dependency of drivers on vxWorks + * + * we assume that the BSP are configured to use these + * address modes by default + */ +#define EPICSAddrTypeNoConvert -1 +int EPICStovxWorksAddrType[] + = { + VME_AM_SUP_SHORT_IO, + VME_AM_STD_SUP_DATA, + VME_AM_EXT_SUP_DATA, + EPICSAddrTypeNoConvert, + VME_AM_CSR + }; + +/* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ +static long rtemsDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long rtemsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long rtemsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); + +static long rtemsDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter); + +static long rtemsDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +); + +static long rtemsDevEnableInterruptLevelVME (unsigned level); + +static long rtemsDevDisableInterruptLevelVME (unsigned level); + +static int rtemsDevInterruptInUseVME (unsigned vectorNumber); + +/* RTEMS specific init */ + +/*devA24Malloc and devA24Free are not implemented*/ +static void *devA24Malloc(size_t size) { return 0;} +static void devA24Free(void *pBlock) {}; +static long rtemsDevInit(void); + +/* + * used by bind in devLib.c + */ +static devLibVME rtemsVirtualOS = { + rtemsDevMapAddr, rtemsDevReadProbe, rtemsDevWriteProbe, + rtemsDevConnectInterruptVME, rtemsDevDisconnectInterruptVME, + rtemsDevEnableInterruptLevelVME, rtemsDevDisableInterruptLevelVME, + devA24Malloc,devA24Free,rtemsDevInit,rtemsDevInterruptInUseVME +}; +devLibVME *pdevLibVME = &rtemsVirtualOS; + +/* RTEMS specific initialization */ +static long +rtemsDevInit(void) +{ + /* assume the vme bridge has been initialized by bsp */ + /* init BSP extensions [memProbe etc.] */ + return bspExtInit(); +} + +/* + * devConnectInterruptVME + * + * wrapper to minimize driver dependency on OS + */ +static long rtemsDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter) +{ + int status; + + + if (devInterruptInUseVME(vectorNumber)) { + return S_dev_vectorInUse; + } + status = BSP_installVME_isr( + vectorNumber, + pFunction, + parameter); + if (status) { + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * + * devDisconnectInterruptVME() + * + * wrapper to minimize driver dependency on OS + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from removing + * an interrupt handler that was installed by another driver + * + */ +static long rtemsDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +) +{ + void (*psub)(); + void *arg; + int status; + + /* + * If pFunction not connected to this vector + * then they are probably disconnecting from the wrong vector + */ + psub = isrFetch(vectorNumber, &arg); + if(psub != pFunction){ + return S_dev_vectorNotInUse; + } + + status = BSP_removeVME_isr( + vectorNumber, + psub, + arg) || + BSP_installVME_isr( + vectorNumber, + (BSP_VME_ISR_t)unsolicitedHandlerEPICS, + (void*)vectorNumber); + if(status){ + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * enable VME interrupt level + */ +static long rtemsDevEnableInterruptLevelVME (unsigned level) +{ + return BSP_enableVME_int_lvl(level); +} + +/* + * disable VME interrupt level + */ +static long rtemsDevDisableInterruptLevelVME (unsigned level) +{ + return BSP_disableVME_int_lvl(level); +} + +/* + * rtemsDevMapAddr () + */ +static long rtemsDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) +{ + long status; + + if (ppPhysicalAddress==NULL) { + return S_dev_badArgument; + } + + if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) + { + *ppPhysicalAddress = (void *) logicalAddress; + } + else + { + status = BSP_vme2local_adrs(EPICStovxWorksAddrType[addrType], + logicalAddress, (unsigned long *)ppPhysicalAddress); + if (status) { + return S_dev_addrMapFail; + } + } + + return 0; +} + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +rtems_status_code bspExtMemProbe(void *addr, int write, int size, void *pval); +static long rtemsDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = bspExtMemProbe ((void*)ptr, 0/*read*/, wordSize, pValue); + if (status!=RTEMS_SUCCESSFUL) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long rtemsDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + /* + * this global variable exists in the nivxi library + */ + status = bspExtMemProbe ((void*)ptr, 1/*write*/, wordSize, (void*)pValue); + if (status!=RTEMS_SUCCESSFUL) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * isrFetch() + */ +static myISR *isrFetch(unsigned vectorNumber, void **parg) +{ + /* + * fetch the handler or C stub attached at this vector + */ + return (myISR *) BSP_getVME_isr(vectorNumber,parg); +} + +/* + * determine if a VME interrupt vector is in use + */ +static int rtemsDevInterruptInUseVME (unsigned vectorNumber) +{ + int i; + myISR *psub; + void *arg; + + psub = isrFetch (vectorNumber,&arg); + + if (!psub) + return FALSE; + + /* + * its a C routine. Does it match a default handler? + */ + for (i=0; i +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/src/libCom/osi/os/RTEMS/osdEvent.c b/src/libCom/osi/os/RTEMS/osdEvent.c new file mode 100644 index 000000000..5513636a2 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdEvent.c @@ -0,0 +1,206 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osdEvent.c + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* + * We want to access information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include +#include +#include + +#include "epicsEvent.h" +#include "epicsThread.h" +#include "errlog.h" + +/* #define EPICS_RTEMS_SEMAPHORE_STATS */ +/* + * Some performance tuning instrumentation + */ +#ifdef EPICS_RTEMS_SEMAPHORE_STATS +unsigned long semEstat[4]; +#define SEMSTAT(i) semEstat[i]++; +#else +#define SEMSTAT(i) +#endif + +/* + * Create a simple binary semaphore + */ +epicsEventId +epicsEventCreate(epicsEventInitialState initialState) +{ + rtems_status_code sc; + rtems_id sid; + rtems_interrupt_level level; + static char c1 = 'a'; + static char c2 = 'a'; + static char c3 = 'a'; + + sc = rtems_semaphore_create (rtems_build_name ('B', c3, c2, c1), + initialState, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL, + 0, + &sid); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't create binary semaphore: %s\n", rtems_status_text (sc)); + return NULL; + } + rtems_interrupt_disable (level); + if (c1 == 'z') { + if (c2 == 'z') { + if (c3 == 'z') { + c3 = 'a'; + } + else { + c3++; + } + c2 = 'a'; + } + else { + c2++; + } + c1 = 'a'; + } + else { + c1++; + } + rtems_interrupt_enable (level); + return (epicsEventId)sid; +} + +epicsEventId epicsEventMustCreate(epicsEventInitialState initialState) +{ + epicsEventId id = epicsEventCreate (initialState); + assert (id); + return id; +} + +void +epicsEventDestroy(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + sc = rtems_semaphore_delete (sid); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf ("Can't destroy semaphore: %s\n", rtems_status_text (sc)); +} + +void +epicsEventSignal(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + sc = rtems_semaphore_release (sid); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf ("Can't release semaphore: %s\n", rtems_status_text (sc)); +} + +epicsEventWaitStatus +epicsEventWait(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + SEMSTAT(0) + sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + return epicsEventWaitError; + return epicsEventWaitOK; +} + +epicsEventWaitStatus +epicsEventWaitWithTimeout(epicsEventId id, double timeOut) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + rtems_interval delay; + extern double rtemsTicksPerSecond_double; + + if (timeOut <= 0.0) + return epicsEventTryWait(id); + SEMSTAT(1) + delay = timeOut * rtemsTicksPerSecond_double; + if (delay == 0) + delay++; + sc = rtems_semaphore_obtain (sid, RTEMS_WAIT, delay); + if (sc == RTEMS_SUCCESSFUL) + return epicsEventWaitOK; + else if (sc == RTEMS_TIMEOUT) + return epicsEventWaitTimeout; + else + return epicsEventWaitError; +} + +epicsEventWaitStatus +epicsEventTryWait(epicsEventId id) +{ + rtems_id sid = (rtems_id)id; + rtems_status_code sc; + + SEMSTAT(2) + sc = rtems_semaphore_obtain (sid, RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT); + if (sc == RTEMS_SUCCESSFUL) + return epicsEventWaitOK; + else if (sc == RTEMS_UNSATISFIED) + return epicsEventWaitTimeout; + else + return epicsEventWaitError; +} + +void +epicsEventShow(epicsEventId id, unsigned int level) +{ +#if __RTEMS_VIOLATE_KERNEL_VISIBILITY__ + rtems_id sid = (rtems_id)id; + Semaphore_Control *the_semaphore; + Semaphore_Control semaphore; + Objects_Locations location; + + the_semaphore = _Semaphore_Get (sid, &location); + if (location != OBJECTS_LOCAL) + return; + /* + * Yes, there's a race condition here since an interrupt might + * change things while the copy is in progress, but the information + * is only for display, so it's not that critical. + */ + semaphore = *the_semaphore; + _Thread_Enable_dispatch(); + printf (" %8.8x ", (int)sid); + if (_Attributes_Is_counting_semaphore (semaphore.attribute_set)) { + printf ("Count: %d", (int)semaphore.Core_control.semaphore.count); + } + else { + if (_CORE_mutex_Is_locked(&semaphore.Core_control.mutex)) { + char name[30]; + epicsThreadGetName ((epicsThreadId)semaphore.Core_control.mutex.holder_id, name, sizeof name); + printf ("Held by:%8.8x (%s) Nest count:%d", + (unsigned int)semaphore.Core_control.mutex.holder_id, + name, + (int)semaphore.Core_control.mutex.nest_count); + } + else { + printf ("Not Held"); + } + } + printf ("\n"); +#endif +} diff --git a/src/libCom/osi/os/RTEMS/osdEvent.h b/src/libCom/osi/os/RTEMS/osdEvent.h new file mode 100644 index 000000000..1763e6b08 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdEvent.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osdEvent.h + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* osdEvent.h not needed */ diff --git a/src/libCom/osi/os/RTEMS/osdInterrupt.c b/src/libCom/osi/os/RTEMS/osdInterrupt.c new file mode 100644 index 000000000..1bdc8b3f7 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdInterrupt.c @@ -0,0 +1,95 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osdInterrupt.c + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +#include +#include +#include +#include "errlog.h" +#include "epicsInterrupt.h" +#include "epicsThread.h" + +#define INTERRUPT_CONTEXT_MESSAGE_QUEUE_COUNT 100 + +static rtems_id interruptContextMessageQueue; + +int +epicsInterruptLock (void) +{ + rtems_interrupt_level level; + + rtems_interrupt_disable (level); + return level; +} + +void +epicsInterruptUnlock (int key) +{ + rtems_interrupt_level level = key; + + rtems_interrupt_enable (level); +} + +int +epicsInterruptIsInterruptContext (void) +{ + return rtems_interrupt_is_in_progress (); +} + +/* + * Pass a message from an interrupt context. + * Note that this passes a pointer to the message, not the message itself. + * This implies that the message must remain valid after the + * interrupt context is no longer active. + */ +void +epicsInterruptContextMessage (const char *message) +{ + rtems_message_queue_send (interruptContextMessageQueue, &message, sizeof message); +} + +/* + * Daemon to process interrupt context messages + */ +void +InterruptContextMessageDaemon (void *unused) +{ + const char *message; + size_t size; + rtems_status_code sc; + + sc = rtems_message_queue_create (rtems_build_name ('I', 'C', 'M', 'Q'), + INTERRUPT_CONTEXT_MESSAGE_QUEUE_COUNT, + sizeof message, + RTEMS_FIFO | RTEMS_LOCAL, + &interruptContextMessageQueue); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't create interrupt context message queue: %s\n", rtems_status_text (sc)); + epicsThreadSuspendSelf (); + } + for (;;) { + sc = rtems_message_queue_receive (interruptContextMessageQueue, + &message, + &size, + RTEMS_WAIT, + RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't receive message from interrupt context: %s\n", rtems_status_text (sc)); + epicsThreadSuspendSelf (); + } + if (size == sizeof message) + syslog (LOG_ERR, "%s", message); + else + errlogPrintf ("Received %u-byte message from interrupt context", (unsigned int)size); + } +} diff --git a/src/libCom/osi/os/RTEMS/osdInterrupt.h b/src/libCom/osi/os/RTEMS/osdInterrupt.h new file mode 100644 index 000000000..2f3a2c596 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdInterrupt.h @@ -0,0 +1,13 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Daemon to soak up and report messages from interrupt contexts + */ +extern void InterruptContextMessageDaemon (void *); diff --git a/src/libCom/osi/os/RTEMS/osdMessageQueue.c b/src/libCom/osi/os/RTEMS/osdMessageQueue.c new file mode 100644 index 000000000..09141aaf8 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdMessageQueue.c @@ -0,0 +1,249 @@ +/*************************************************************************\ +* 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 W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * We want to access information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#define epicsExportSharedSymbols +#include +#include +#include +#include +#include +#include +#include "epicsMessageQueue.h" +#include "errlog.h" + +epicsShareFunc epicsMessageQueueId epicsShareAPI +epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize) +{ + rtems_status_code sc; + epicsMessageQueueId id = (epicsMessageQueueId)callocMustSucceed(1, sizeof(*id), "epicsMessageQueueCreate"); + rtems_interrupt_level level; + static char c1 = 'a'; + static char c2 = 'a'; + static char c3 = 'a'; + + sc = rtems_message_queue_create (rtems_build_name ('Q', c3, c2, c1), + capacity, + maximumMessageSize, + RTEMS_FIFO|RTEMS_LOCAL, + &id->id); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't create message queue: %s\n", rtems_status_text (sc)); + return NULL; + } + id->maxSize = maximumMessageSize; + id->localBuf = NULL; + rtems_interrupt_disable (level); + if (c1 == 'z') { + if (c2 == 'z') { + if (c3 == 'z') { + c3 = 'a'; + } + else { + c3++; + } + c2 = 'a'; + } + else { + c2++; + } + c1 = 'a'; + } + else { + c1++; + } + rtems_interrupt_enable (level); + return id; +} + +static rtems_status_code rtems_message_queue_send_timeout( + rtems_id id, + void *buffer, + uint32_t size, + rtems_interval timeout) +{ + Message_queue_Control *the_message_queue; + Objects_Locations location; + CORE_message_queue_Status msg_status; + + the_message_queue = _Message_queue_Get( id, &location ); + switch ( location ) + { + case OBJECTS_ERROR: + return RTEMS_INVALID_ID; + + case OBJECTS_LOCAL: + msg_status = _CORE_message_queue_Send( + &the_message_queue->message_queue, + buffer, + size, + id, + NULL, + 1, + timeout + ); + + _Thread_Enable_dispatch(); + + /* + * If we had to block, then this is where the task returns + * after it wakes up. The returned status is correct for + * non-blocking operations but if we blocked, then we need + * to look at the status in our TCB. + */ + + if ( msg_status == CORE_MESSAGE_QUEUE_STATUS_UNSATISFIED_WAIT ) + msg_status = _Thread_Executing->Wait.return_code; + return _Message_queue_Translate_core_message_queue_return_code( msg_status ); + } + return RTEMS_INTERNAL_ERROR; /* unreached - only to remove warnings */ +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueSend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize) +{ + if (rtems_message_queue_send_timeout(id->id, message, messageSize, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL) + return 0; + else + return -1; +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int messageSize, + double timeout) +{ + rtems_interval delay; + extern double rtemsTicksPerSecond_double; + + /* + * Convert time to ticks + */ + if (timeout <= 0.0) + return epicsMessageQueueTrySend(id, message, messageSize); + delay = (int)(timeout * rtemsTicksPerSecond_double); + if (delay == 0) + delay++; + if (rtems_message_queue_send_timeout(id->id, message, messageSize, delay) == RTEMS_SUCCESSFUL) + return 0; + else + return -1; +} + +static int receiveMessage( + epicsMessageQueueId id, + void *buffer, + uint32_t size, + uint32_t wait, + rtems_interval delay) +{ + size_t rsize; + rtems_status_code sc; + + if (size < id->maxSize) { + if (id->localBuf == NULL) { + id->localBuf = malloc(id->maxSize); + if (id->localBuf == NULL) + return -1; + } + rsize = receiveMessage(id, id->localBuf, id->maxSize, wait, delay); + if ((rsize < 0) || (rsize > size)) + return -1; + memcpy(buffer, id->localBuf, rsize); + } + else { + sc = rtems_message_queue_receive(id->id, buffer, &rsize, wait, delay); + if (sc != RTEMS_SUCCESSFUL) + return -1; + } + return rsize; +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueTryReceive( + epicsMessageQueueId id, + void *message, + unsigned int size) +{ + return receiveMessage(id, message, size, RTEMS_NO_WAIT, 0); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueReceive( + epicsMessageQueueId id, + void *message, + unsigned int size) +{ + return receiveMessage(id, message, size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int size, + double timeout) +{ + rtems_interval delay; + uint32_t wait; + extern double rtemsTicksPerSecond_double; + + /* + * Convert time to ticks + */ + if (timeout <= 0.0) { + wait = RTEMS_NO_WAIT; + delay = 0; + } + else { + wait = RTEMS_WAIT; + delay = (int)(timeout * rtemsTicksPerSecond_double); + if (delay == 0) + delay++; + } + return receiveMessage(id, message, size, wait, delay); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueuePending( + epicsMessageQueueId id) +{ + uint32_t count; + rtems_status_code sc; + + sc = rtems_message_queue_get_number_pending(id->id, &count); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf("Message queue %x get number pending failed: %s\n", + (unsigned int)id, + rtems_status_text(sc)); + return -1; + } + return count; +} + +epicsShareFunc void epicsShareAPI epicsMessageQueueShow( + epicsMessageQueueId id, + int level) +{ + int pending = epicsMessageQueuePending(id); + if (pending >= 0) + printf ("Message queue %lx -- Pending: %d\n", (unsigned long)id, pending); +} diff --git a/src/libCom/osi/os/RTEMS/osdMessageQueue.h b/src/libCom/osi/os/RTEMS/osdMessageQueue.h new file mode 100644 index 000000000..489ec9e6e --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdMessageQueue.h @@ -0,0 +1,31 @@ +/*************************************************************************\ +* 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 W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * Very thin shims around RTEMS routines + */ +#include + +struct epicsMessageQueueOSD { + rtems_id id; + unsigned int maxSize; + void *localBuf; + +}; +#define epicsMessageQueueDestroy(q) (rtems_message_queue_delete((q)->id)) + +#define epicsMessageQueueTrySend(q,m,l) (rtems_message_queue_send((q)->id, (m), (l)) == RTEMS_SUCCESSFUL ? 0 : -1) diff --git a/src/libCom/osi/os/RTEMS/osdMutex.c b/src/libCom/osi/os/RTEMS/osdMutex.c new file mode 100644 index 000000000..8c51a4de9 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdMutex.c @@ -0,0 +1,196 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osdMutex.c + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* + * We want to access information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include +#include +#include + +#include "epicsStdioRedirect.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "errlog.h" + +#define RTEMS_FAST_MUTEX +/* #define EPICS_RTEMS_SEMAPHORE_STATS */ +/* + * Some performance tuning instrumentation + */ +#ifdef EPICS_RTEMS_SEMAPHORE_STATS +unsigned long semMstat[4]; +#define SEMSTAT(i) semMstat[i]++; +#else +#define SEMSTAT(i) +#endif + +struct epicsMutexOSD * +epicsMutexOsdCreate(void) +{ + rtems_status_code sc; + rtems_id sid; + rtems_interrupt_level level; + static char c1 = 'a'; + static char c2 = 'a'; + static char c3 = 'a'; + + sc = rtems_semaphore_create (rtems_build_name ('M', c3, c2, c1), + 1, + RTEMS_PRIORITY|RTEMS_BINARY_SEMAPHORE|RTEMS_INHERIT_PRIORITY|RTEMS_NO_PRIORITY_CEILING|RTEMS_LOCAL, + 0, + &sid); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("Can't create mutex semaphore: %s\n", rtems_status_text (sc)); + return NULL; + } + rtems_interrupt_disable (level); + if (c1 == 'z') { + if (c2 == 'z') { + if (c3 == 'z') { + c3 = 'a'; + } + else { + c3++; + } + c2 = 'a'; + } + else { + c2++; + } + c1 = 'a'; + } + else { + c1++; + } + rtems_interrupt_enable (level); +#ifdef RTEMS_FAST_MUTEX + { + Semaphore_Control *the_semaphore; + Objects_Locations location; + + the_semaphore = _Semaphore_Get( sid, &location ); + _Thread_Enable_dispatch(); + + return (struct epicsMutexOSD *)the_semaphore; + } +#endif + return (struct epicsMutexOSD *)sid; +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * id) +{ + rtems_status_code sc; + rtems_id sid; +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + sid = the_semaphore->Object.id; +#else + sid = (rtems_id)id; +#endif + sc = rtems_semaphore_delete (sid); + if (sc == RTEMS_RESOURCE_IN_USE) { + rtems_semaphore_release (sid); + sc = rtems_semaphore_delete (sid); + } + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf ("Can't destroy semaphore %p (%lx): %s\n", id, (unsigned long)sid, rtems_status_text (sc)); +} + +void epicsMutexOsdUnlock(struct epicsMutexOSD * id) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + _Thread_Disable_dispatch(); + _CORE_mutex_Surrender ( + &the_semaphore->Core_control.mutex, + the_semaphore->Object.id, + NULL + ); + _Thread_Enable_dispatch(); +#else + epicsEventSignal (id); +#endif + +} + +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * id) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + ISR_Level level; + SEMSTAT(0) + _ISR_Disable( level ); + _CORE_mutex_Seize( + &the_semaphore->Core_control.mutex, + the_semaphore->Object.id, + 1, /* TRUE or FALSE */ + 0, /* same as passed to obtain -- ticks */ + level + ); + if (_Thread_Executing->Wait.return_code == 0) + return epicsMutexLockOK; + else + return epicsMutexLockError; +#else + SEMSTAT(0) + return((epicsEventWait (id) == epicsEventWaitOK) + ?epicsMutexLockOK : epicsMutexLockError); +#endif +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + ISR_Level level; + SEMSTAT(2) + _ISR_Disable( level ); + _CORE_mutex_Seize( + &the_semaphore->Core_control.mutex, + the_semaphore->Object.id, + 0, /* TRUE or FALSE */ + 0, /* same as passed to obtain -- ticks */ + level + ); + if (_Thread_Executing->Wait.return_code == CORE_MUTEX_STATUS_SUCCESSFUL) + return epicsMutexLockOK; + else if (_Thread_Executing->Wait.return_code == CORE_MUTEX_STATUS_UNSATISFIED_NOWAIT) + return epicsMutexLockTimeout; + else + return epicsMutexLockError; +#else + epicsEventWaitStatus status; + SEMSTAT(2) + status = epicsEventTryWait(id); + return((status==epicsEventWaitOK + ? epicsMutexLockOK + : (status==epicsEventWaitTimeout) + ? epicsMutexLockTimeout + : epicsMutexLockError)); +#endif +} + +epicsShareFunc void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level) +{ +#ifdef RTEMS_FAST_MUTEX + Semaphore_Control *the_semaphore = (Semaphore_Control *)id; + id = (struct epicsMutexOSD *)the_semaphore->Object.id; +#endif + epicsEventShow ((epicsEventId)id,level); +} diff --git a/src/libCom/osi/os/RTEMS/osdMutex.h b/src/libCom/osi/os/RTEMS/osdMutex.h new file mode 100644 index 000000000..90434b8dc --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdMutex.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osdMutex.h + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* osdSem.h not needed */ diff --git a/src/libCom/osi/os/RTEMS/osdPoolStatus.c b/src/libCom/osi/os/RTEMS/osdPoolStatus.c new file mode 100644 index 000000000..b2f401190 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdPoolStatus.c @@ -0,0 +1,26 @@ +/*************************************************************************\ +* 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 + +#define epicsExportSharedSymbols +#include "osiPoolStatus.h" + +/* + * osiSufficentSpaceInPool () + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + rtems_malloc_statistics_t s; + unsigned long n; + + malloc_get_statistics(&s); + n = s.space_available - (unsigned long)(s.lifetime_allocated - s.lifetime_freed); + return (n > (50000 + contiguousBlockSize)); +} diff --git a/src/libCom/osi/os/RTEMS/osdProcess.c b/src/libCom/osi/os/RTEMS/osdProcess.c new file mode 100644 index 000000000..10e32b731 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdProcess.c @@ -0,0 +1,52 @@ +/*************************************************************************\ +* 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 + * + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + const char *pName = "rtems"; + unsigned uiLength; + size_t len; + + len = strlen (pName); + + if ( len>UINT_MAX || len<=0 ) { + return osiGetUserNameFail; + } + uiLength = (unsigned) len; + + if ( uiLength + 1 >= bufSizeIn ) { + return osiGetUserNameFail; + } + + strncpy ( pBuf, pName, (size_t) bufSizeIn ); + + return osiGetUserNameSuccess; +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName) +{ + return osiSpawnDetachedProcessNoSupport; +} diff --git a/src/libCom/osi/os/RTEMS/osdSignal.cpp b/src/libCom/osi/os/RTEMS/osdSignal.cpp new file mode 100644 index 000000000..08dfa023c --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdSignal.cpp @@ -0,0 +1,21 @@ +/*************************************************************************\ + * 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. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * All NOOPs if the os isnt POSIX + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} + diff --git a/src/libCom/osi/os/RTEMS/osdSock.h b/src/libCom/osi/os/RTEMS/osdSock.h new file mode 100644 index 000000000..17125ff62 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdSock.h @@ -0,0 +1,101 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osdSock.h + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; + +#define FD_IN_FDSET(FD) ((FD) +#include +#include + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK (u_long)0x7F000001 +#endif + +#ifndef INADDR_NONE +# define INADDR_NONE (0xffffffff) +#endif + +/* + * For shutdown() + */ +#ifndef SHUT_RD +# define SHUT_RD 0 +#endif + +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +/* + * Ensure that we get the right network code in default/osdNetIntf.c. + */ +#define ifreq_size(pifreq) (pifreq->ifr_addr.sa_len + sizeof(pifreq->ifr_name)) + +#endif /*osdSockH*/ diff --git a/src/libCom/osi/os/RTEMS/osdStrtod.h b/src/libCom/osi/os/RTEMS/osdStrtod.h new file mode 100644 index 000000000..39fda698d --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdStrtod.h @@ -0,0 +1,11 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ + +/* + * epicsStrtod() for systems with working strtod() routine + */ +#define epicsStrtod strtod diff --git a/src/libCom/osi/os/RTEMS/osdThread.c b/src/libCom/osi/os/RTEMS/osdThread.c new file mode 100644 index 000000000..5cdb15441 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdThread.c @@ -0,0 +1,700 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osdThread.c + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ + +/* + * We want to print out some task information which is + * normally hidden from application programs. + */ +#define __RTEMS_VIOLATE_KERNEL_VISIBILITY__ 1 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "epicsStdio.h" +#include "errlog.h" +#include "epicsMutex.h" +#include "epicsString.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "osiUnistd.h" +#include "osdInterrupt.h" +#include "epicsExit.h" + +/* + * Per-task variables + */ +struct taskVar { + struct taskVar *forw; + struct taskVar *back; + char *name; + rtems_id id; + EPICSTHREADFUNC funptr; + void *parm; + unsigned int threadVariableCapacity; + void **threadVariables; +}; +static epicsMutexId taskVarMutex; +static struct taskVar *taskVarHead; +#define RTEMS_NOTEPAD_TASKVAR 11 + +/* + * Support for `once-only' execution + */ +static int initialized = 0; +static epicsMutexId onceMutex; + +/* + * Just map osi 0 to 99 into RTEMS 199 to 100 + * For RTEMS lower number means higher priority + * RTEMS = 100 + (99 - osi) + * = 199 - osi + * osi = 199 - RTEMS + */ +int epicsThreadGetOsiPriorityValue(int ossPriority) +{ + if (ossPriority < 100) { + return epicsThreadPriorityMax; + } + else if (ossPriority > 199) { + return epicsThreadPriorityMin; + } + else { + return (199u - (unsigned int)ossPriority); + } +} + +int epicsThreadGetOssPriorityValue(unsigned int osiPriority) +{ + if (osiPriority > 99) { + return 100; + } + else { + return (199 - (signed int)osiPriority); + } +} + +/* + * epicsThreadLowestPriorityLevelAbove () + */ +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove + (unsigned int priority, unsigned *pPriorityJustAbove) +{ + unsigned newPriority = priority + 1; + + newPriority = priority + 1; + if (newPriority <= 99) { + *pPriorityJustAbove = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +/* + * epicsThreadHighestPriorityLevelBelow () + */ +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow + (unsigned int priority, unsigned *pPriorityJustBelow) +{ + unsigned newPriority = priority - 1; + + if (newPriority <= 99) { + *pPriorityJustBelow = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +unsigned int +epicsThreadGetStackSize (epicsThreadStackSizeClass size) +{ + unsigned int stackSize = 11000; + switch(size) { + case epicsThreadStackSmall: stackSize = 5000; break; + case epicsThreadStackMedium: stackSize = 8000; break; + case epicsThreadStackBig: break; + default: + errlogPrintf("epicsThreadGetStackSize illegal argument"); + break; + } + if (stackSize < RTEMS_MINIMUM_STACK_SIZE) + stackSize = RTEMS_MINIMUM_STACK_SIZE; + return stackSize; +} + +/* + * Ensure integrity of task variable list + */ +static void +taskVarLock (void) +{ + epicsMutexLock (taskVarMutex); +} + +static void +taskVarUnlock (void) +{ + epicsMutexUnlock (taskVarMutex); +} + +/* + * EPICS threads destroy themselves by returning from the thread entry function. + * This simple wrapper provides the same semantics on RTEMS. + */ +static rtems_task +threadWrapper (rtems_task_argument arg) +{ + struct taskVar *v = (struct taskVar *)arg; + + (*v->funptr)(v->parm); + epicsExitCallAtThreadExits (); + taskVarLock (); + if (v->back) + v->back->forw = v->forw; + else + taskVarHead = v->forw; + if (v->forw) + v->forw->back = v->back; + taskVarUnlock (); + free (v->threadVariables); + free (v->name); + free (v); + rtems_task_delete (RTEMS_SELF); +} + +/* + * The task wrapper takes care of cleanup + */ +void epicsThreadExitMain (void) +{ +} + +static void +setThreadInfo (rtems_id tid, const char *name, EPICSTHREADFUNC funptr,void *parm) +{ + struct taskVar *v; + uint32_t note; + rtems_status_code sc; + + v = mallocMustSucceed (sizeof *v, "epicsThreadCreate_vars"); + v->name = epicsStrDup(name); + v->id = tid; + v->funptr = funptr; + v->parm = parm; + v->threadVariableCapacity = 0; + v->threadVariables = NULL; + note = (uint32_t)v; + rtems_task_set_note (tid, RTEMS_NOTEPAD_TASKVAR, note); + taskVarLock (); + v->forw = taskVarHead; + v->back = NULL; + if (v->forw) + v->forw->back = v; + taskVarHead = v; + taskVarUnlock (); + if (funptr) { + sc = rtems_task_start (tid, threadWrapper, (rtems_task_argument)v); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf ("setThreadInfo: Can't start %s: %s\n",name, rtems_status_text (sc)); + } +} + +/* + * OS-dependent initialization + * No need to worry about making this thread-safe since + * it must be called before epicsThreadCreate creates + * any new threads. + */ +static void +epicsThreadInit (void) +{ + if (!initialized) { + rtems_id tid; + rtems_task_priority old; + + rtems_task_set_priority (RTEMS_SELF, epicsThreadGetOssPriorityValue(99), &old); + onceMutex = epicsMutexMustCreate(); + taskVarMutex = epicsMutexMustCreate (); + rtems_task_ident (RTEMS_SELF, 0, &tid); + setThreadInfo (tid, "_main_", NULL, NULL); + initialized = 1; + epicsThreadCreate ("ImsgDaemon", 99, + epicsThreadGetStackSize (epicsThreadStackSmall), + InterruptContextMessageDaemon, NULL); + } +} + +/* + * Create and start a new thread + */ +epicsThreadId +epicsThreadCreate (const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + rtems_id tid; + rtems_status_code sc; + char c[4]; + + if (!initialized) epicsThreadInit(); + if (stackSize < RTEMS_MINIMUM_STACK_SIZE) { + errlogPrintf ("Warning: epicsThreadCreate %s illegal stackSize %d\n",name,stackSize); + stackSize = RTEMS_MINIMUM_STACK_SIZE; + } + strncpy (c, name, sizeof c); + sc = rtems_task_create (rtems_build_name (c[0], c[1], c[2], c[3]), + epicsThreadGetOssPriorityValue (priority), + stackSize, + RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0), + RTEMS_FLOATING_POINT|RTEMS_LOCAL, + &tid); + if (sc != RTEMS_SUCCESSFUL) { + errlogPrintf ("epicsThreadCreate create failure for %s: %s\n",name, rtems_status_text (sc)); + return 0; + } + setThreadInfo (tid, name, funptr,parm); + return (epicsThreadId)tid; +} + +epicsThreadId +threadMustCreate (const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + epicsThreadId tid; + + if ((tid = epicsThreadCreate (name, priority, stackSize, funptr, parm)) == NULL) + cantProceed (0); + return tid; +} + +void +epicsThreadSuspendSelf (void) +{ + rtems_status_code sc; + + sc = rtems_task_suspend (RTEMS_SELF); + if(sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadSuspendSelf failed: %s\n", rtems_status_text (sc)); +} + +void epicsThreadResume(epicsThreadId id) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + + sc = rtems_task_resume (tid); + if(sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadResume failed: %s\n", rtems_status_text (sc)); +} + +unsigned int epicsThreadGetPriority(epicsThreadId id) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + rtems_task_priority pri; + + sc = rtems_task_set_priority (tid, RTEMS_CURRENT_PRIORITY, &pri); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadGetPriority failed: %s\n", rtems_status_text (sc)); + return epicsThreadGetOsiPriorityValue (pri); +} + +unsigned int epicsThreadGetPrioritySelf(void) +{ + return epicsThreadGetPriority((epicsThreadId)RTEMS_SELF); +} + +void +epicsThreadSetPriority (epicsThreadId id,unsigned int osip) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + rtems_task_priority pri = epicsThreadGetOssPriorityValue(osip); + + sc = rtems_task_set_priority (tid, pri, &pri); + if (sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadSetPriority failed: %s\n", rtems_status_text (sc)); +} + +int +epicsThreadIsEqual (epicsThreadId id1, epicsThreadId id2) +{ + return (id1 == id2); +} + +int +epicsThreadIsSuspended (epicsThreadId id) +{ + rtems_id tid = (rtems_id)id; + rtems_status_code sc; + + switch (sc = rtems_task_is_suspended (tid)) { + case RTEMS_SUCCESSFUL: + return 0; + + case RTEMS_ALREADY_SUSPENDED: + return 1; + + default: + return 1; + } +} + +void +epicsThreadSleep (double seconds) +{ + rtems_status_code sc; + rtems_interval delay; + extern double rtemsTicksPerTwoSeconds_double; + + if (seconds <= 0.0) { + delay = 0; + } + else { + delay = seconds * rtemsTicksPerTwoSeconds_double; + delay = (delay + 1) / 2; + if (delay == 0) + delay++; + } + sc = rtems_task_wake_after (delay); + if(sc != RTEMS_SUCCESSFUL) + errlogPrintf("epicsThreadSleep: %s\n", rtems_status_text (sc)); +} + +epicsThreadId +epicsThreadGetIdSelf (void) +{ + rtems_id tid; + + rtems_task_ident (RTEMS_SELF, 0, &tid); + return (epicsThreadId)tid; +} + +const char *epicsThreadGetNameSelf(void) +{ + uint32_t note; + struct taskVar *v; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (void *)note; + return v->name; +} + +void epicsThreadGetName (epicsThreadId id, char *name, size_t size) +{ + rtems_id tid = (rtems_id)id; + struct taskVar *v; + int haveName = 0; + + if (size <= 0) + return; + taskVarLock (); + for (v=taskVarHead ; v != NULL ; v=v->forw) { + if (v->id == tid) { + strncpy(name, v->name, size); + haveName = 1; + break; + } + } + taskVarUnlock (); + if (!haveName) { +#if (__RTEMS_MAJOR__>4 || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>8) || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==8 && __RTEMS_REVISION__>=99)) + if (_Objects_Get_name_as_string((rtems_id)id, size, name) != NULL) + haveName = 1; +#else + /* + * Try to get the RTEMS task name + */ + Thread_Control *thr; + Objects_Locations l; + if ((thr=_Thread_Get(tid, &l)) != NULL) { + if (OBJECTS_LOCAL == l) { + int length; + Objects_Information *oi = _Objects_Get_information(tid); + if (oi->name_length >= size) + length = size - 1; + else + length = oi->name_length; + if (oi->is_string) + strncpy(name, thr->Object.name, length); + else + _Objects_Copy_name_raw( &thr->Object.name, name, length); + name[length] = '\0'; + haveName = 1; + } + _Thread_Enable_dispatch(); + } +#endif + } + if (!haveName) + snprintf(name, size, "0x%lx", (long)tid); + name[size-1] = '\0'; +} + +epicsThreadId epicsThreadGetId (const char *name) +{ + struct taskVar *v; + rtems_id tid = 0; + + /* + * Linear search is good enough since this routine + * is invoked only by command-line calls. + */ + taskVarLock (); + for (v = taskVarHead ; v != NULL ; v = v->forw) { + if (strcmp (name, v->name) == 0) { + tid = v->id; + break; + } + } + taskVarUnlock (); + return (epicsThreadId)tid; +} + +/* + * Ensure func() is run only once. + */ +void epicsThreadOnce(epicsThreadOnceId *id, void(*func)(void *), void *arg) +{ + #define EPICS_THREAD_ONCE_DONE (epicsThreadId) 1 + + if (!initialized) epicsThreadInit(); + epicsMutexMustLock(onceMutex); + if (*id != EPICS_THREAD_ONCE_DONE) { + if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + epicsMutexUnlock(onceMutex); + func(arg); + epicsMutexMustLock(onceMutex); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if (*id == epicsThreadGetIdSelf()) { + epicsMutexUnlock(onceMutex); + cantProceed("Recursive epicsThreadOnce() initialization\n"); + } else + while (*id != EPICS_THREAD_ONCE_DONE) { + /* Another thread is in the above func(arg) call. */ + epicsMutexUnlock(onceMutex); + epicsThreadSleep(epicsThreadSleepQuantum()); + epicsMutexMustLock(onceMutex); + } + } + epicsMutexUnlock(onceMutex); +} + +/* + * Thread private storage implementation based on the vxWorks + * implementation by Andrew Johnson APS/ASD. + */ +epicsThreadPrivateId epicsThreadPrivateCreate () +{ + unsigned int taskVarIndex; + static volatile unsigned int threadVariableCount = 0; + + if (!initialized) epicsThreadInit (); + taskVarLock (); + taskVarIndex = ++threadVariableCount; + taskVarUnlock (); + return (epicsThreadPrivateId)taskVarIndex; +} + +void epicsThreadPrivateDelete (epicsThreadPrivateId id) +{ + /* empty */ +} + +void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt) +{ + unsigned int varIndex = (unsigned int)id; + uint32_t note; + struct taskVar *v; + int i; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (struct taskVar *)note; + if (varIndex >= v->threadVariableCapacity) { + v->threadVariables = realloc (v->threadVariables, (varIndex + 1) * sizeof (void *)); + if (v->threadVariables == NULL) + cantProceed("epicsThreadPrivateSet realloc failed\n"); + for (i = v->threadVariableCapacity ; i < varIndex ; i++) + v->threadVariables[i] = NULL; + v->threadVariableCapacity = varIndex + 1; + } + v->threadVariables[varIndex] = pvt; +} + +void * epicsThreadPrivateGet (epicsThreadPrivateId id) +{ + unsigned int varIndex = (unsigned int)id; + uint32_t note; + struct taskVar *v; + + rtems_task_get_note (RTEMS_SELF, RTEMS_NOTEPAD_TASKVAR, ¬e); + v = (struct taskVar *)note; + if (varIndex >= v->threadVariableCapacity) + return NULL; + return v->threadVariables[varIndex]; +} + +/* + * Show task info + */ +struct bitmap { + char *msg; + unsigned long mask; + unsigned long state; +}; + +static void +showBitmap (char *cbuf, unsigned long bits, const struct bitmap *bp) +{ + for ( ; bp->msg != NULL ; bp++) { + if ((bp->mask & bits) == bp->state) { + strcpy (cbuf, bp->msg); + cbuf += strlen (bp->msg); + } + } +} + +static void +showInternalTaskInfo (rtems_id tid) +{ +#ifdef __RTEMS_VIOLATE_KERNEL_VISIBILITY__ + int epicsPri; + Thread_Control *the_thread; + Objects_Locations location; + static Thread_Control thread; + static char bitbuf[120]; + static const struct bitmap taskState[] = { + { "RUN", STATES_ALL_SET, STATES_READY }, + { "DORM", STATES_DORMANT, STATES_DORMANT }, + { "SUSP", STATES_SUSPENDED, STATES_SUSPENDED }, + { "TRANS", STATES_TRANSIENT, STATES_TRANSIENT }, + { "Delay", STATES_DELAYING, STATES_DELAYING }, + { "Wtime", STATES_WAITING_FOR_TIME, STATES_WAITING_FOR_TIME }, + { "Wbuf", STATES_WAITING_FOR_BUFFER, STATES_WAITING_FOR_BUFFER }, + { "Wseg", STATES_WAITING_FOR_SEGMENT, STATES_WAITING_FOR_SEGMENT }, + { "Wmsg" , STATES_WAITING_FOR_MESSAGE, STATES_WAITING_FOR_MESSAGE }, + { "Wevnt", STATES_WAITING_FOR_EVENT, STATES_WAITING_FOR_EVENT }, + { "Wsem", STATES_WAITING_FOR_SEMAPHORE,STATES_WAITING_FOR_SEMAPHORE }, + { "Wmtx", STATES_WAITING_FOR_MUTEX, STATES_WAITING_FOR_MUTEX }, + { "Wjoin", STATES_WAITING_FOR_JOIN_AT_EXIT,STATES_WAITING_FOR_JOIN_AT_EXIT }, + { "Wrpc", STATES_WAITING_FOR_RPC_REPLY,STATES_WAITING_FOR_RPC_REPLY }, + { "Wrate", STATES_WAITING_FOR_PERIOD, STATES_WAITING_FOR_PERIOD }, + { "Wsig", STATES_WAITING_FOR_SIGNAL, STATES_WAITING_FOR_SIGNAL }, + { NULL, 0, 0 }, + }; + + the_thread = _Thread_Get (tid, &location); + if (location != OBJECTS_LOCAL) { + fprintf(epicsGetStdout(),"%-30s", " *** RTEMS task gone! ***"); + return; + } + thread = *the_thread; + _Thread_Enable_dispatch(); + /* + * Show both real and current priorities if they differ. + * Note that the epicsThreadGetOsiPriorityValue routine is not used here. + * If a thread has a priority outside the normal EPICS range then + * that priority should be displayed, not the value truncated to + * the EPICS range. + */ + epicsPri = 199-thread.real_priority; + if (epicsPri < 0) + fprintf(epicsGetStdout()," <0"); + else if (epicsPri > 99) + fprintf(epicsGetStdout()," >99"); + else + fprintf(epicsGetStdout()," %4d", epicsPri); + if (thread.current_priority == thread.real_priority) + fprintf(epicsGetStdout(),"%4d ", (int)thread.current_priority); + else + fprintf(epicsGetStdout(),"%4d/%-3d", (int)thread.real_priority, (int)thread.current_priority); + showBitmap (bitbuf, thread.current_state, taskState); + fprintf(epicsGetStdout(),"%8.8s", bitbuf); + if (thread.current_state & (STATES_WAITING_FOR_SEMAPHORE | + STATES_WAITING_FOR_MUTEX | + STATES_WAITING_FOR_MESSAGE)) + fprintf(epicsGetStdout()," %8.8x", (int)thread.Wait.id); + else + fprintf(epicsGetStdout()," %8.8s", ""); +#endif +} + +static void +epicsThreadShowHeader (void) +{ + fprintf(epicsGetStdout()," PRIORITY\n"); + fprintf(epicsGetStdout()," ID EPICS RTEMS STATE WAIT NAME\n"); + fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n"); +} + +static void +epicsThreadShowInfo (struct taskVar *v, unsigned int level) +{ + fprintf(epicsGetStdout(),"%9.8x", (int)v->id); + showInternalTaskInfo (v->id); + fprintf(epicsGetStdout()," %s\n", v->name); +} + +void epicsThreadShow (epicsThreadId id, unsigned int level) +{ + struct taskVar *v; + + if (!id) { + epicsThreadShowHeader (); + return; + } + taskVarLock (); + for (v = taskVarHead ; v != NULL ; v = v->forw) { + if ((rtems_id)id == v->id) { + epicsThreadShowInfo (v, level); + return; + } + } + taskVarUnlock (); + fprintf(epicsGetStdout(),"*** Thread %x does not exist.\n", (unsigned int)id); +} + +void epicsThreadShowAll (unsigned int level) +{ + struct taskVar *v; + + epicsThreadShowHeader (); + taskVarLock (); + /* + * Show tasks in the order of creation (backwards through list) + */ + for (v = taskVarHead ; v != NULL && v->forw != NULL ; v = v->forw) + continue; + while (v) { + epicsThreadShowInfo (v, level); + v = v->back; + } + taskVarUnlock (); +} + +double epicsThreadSleepQuantum ( void ) +{ + extern double rtemsTicksPerSecond_double; + + return 1.0 / rtemsTicksPerSecond_double; +} diff --git a/src/libCom/osi/os/RTEMS/osdThread.h b/src/libCom/osi/os/RTEMS/osdThread.h new file mode 100644 index 000000000..4451f845a --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdThread.h @@ -0,0 +1,12 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +int epicsThreadGetOssPriorityValue(unsigned int osiPriority); + diff --git a/src/libCom/osi/os/RTEMS/osdTime.cpp b/src/libCom/osi/os/RTEMS/osdTime.cpp new file mode 100644 index 000000000..e7a419d60 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdTime.cpp @@ -0,0 +1,133 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Revision-Id: ralph.lange@bessy.de-20101020143551-76qdww31y8v01l24 + * + * Author: W. Eric Norum + */ + +#include +#include +#include +#include +#include +#include +#include +#include "epicsTime.h" +#include "osdTime.h" +#include "osiNTPTime.h" +#include "osiClockTime.h" +#include "generalTimeSup.h" + +extern "C" { + +extern rtems_interval rtemsTicksPerSecond; +int rtems_bsdnet_get_ntp(int, int(*)(), struct timespec *); +static int ntpSocket = -1; + +void osdTimeRegister(void) +{ + /* Init NTP first so it can be used to sync ClockTime */ + NTPTime_Init(100); + ClockTime_Init(CLOCKTIME_SYNC); +} + +int osdNTPGet(struct timespec *ts) +{ + if (ntpSocket < 0) + return -1; + return rtems_bsdnet_get_ntp(ntpSocket, NULL, ts); +} + +void osdNTPInit(void) +{ + struct sockaddr_in myAddr; + + ntpSocket = socket (AF_INET, SOCK_DGRAM, 0); + if (ntpSocket < 0) { + printf("osdNTPInit() Can't create socket: %s\n", strerror (errno)); + return; + } + memset (&myAddr, 0, sizeof myAddr); + myAddr.sin_family = AF_INET; + myAddr.sin_port = htons (0); + myAddr.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (ntpSocket, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) { + printf("osdNTPInit() Can't bind socket: %s\n", strerror (errno)); + close (ntpSocket); + ntpSocket = -1; + } +} + +void osdNTPReport(void) +{ +} + +int osdTickGet(void) +{ + rtems_interval t; + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &t); + return t; +} + +int osdTickRateGet(void) +{ + return rtemsTicksPerSecond; +} + +/* + * Use reentrant versions of time access + */ +int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) +{ + struct tm * pRet = gmtime_r ( pAnsiTime, pTM ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return epicsTimeERROR; + } +} + +int epicsTime_localtime ( const time_t *clock, struct tm *result ) +{ + struct tm * pRet = localtime_r ( clock, result ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return epicsTimeERROR; + } +} + +rtems_interval rtemsTicksPerSecond; +double rtemsTicksPerSecond_double, rtemsTicksPerTwoSeconds_double; + +} // extern "C" + +/* + * Static constructors are run too early in a standalone binary + * to be able to initialize the NTP time provider (the network + * is not available yet), so the RTEMS standalone startup code + * explicitly calls osdTimeRegister() at the appropriate time. + * However if we are loaded dynamically we *do* register our + * standard time providers at static constructor time; in this + * case the tick rate will have been set already. + */ +static int staticTimeRegister(void) +{ + if (rtemsTicksPerSecond != 0) + osdTimeRegister(); + + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &rtemsTicksPerSecond); + rtemsTicksPerSecond_double = rtemsTicksPerSecond; + rtemsTicksPerTwoSeconds_double = rtemsTicksPerSecond_double * 2.0; + + return 1; +} +static int done = staticTimeRegister(); diff --git a/src/libCom/osi/os/RTEMS/osdTime.h b/src/libCom/osi/os/RTEMS/osdTime.h new file mode 100644 index 000000000..5d80a0166 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdTime.h @@ -0,0 +1,35 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + */ + +#ifndef INC_osdTime_H +#define INC_osdTime_H + +#ifdef __cplusplus +extern "C" { +#endif + +void osdTimeRegister(void); + +void osdNTPInit(void); +int osdNTPGet(struct timespec *); +void osdNTPReport(void); + +int osdTickRateGet(void); +int osdTickGet(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_osdTime_H */ diff --git a/src/libCom/osi/os/RTEMS/osdVME.h b/src/libCom/osi/os/RTEMS/osdVME.h new file mode 100644 index 000000000..2de190683 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osdVME.h @@ -0,0 +1,21 @@ +/*************************************************************************\ +* 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 */ + +/* + * OS-dependent VME support + */ +#ifndef __i386__ +#ifndef __mc68000 +#ifndef __arm__ +#include +#endif +#endif +#endif diff --git a/src/libCom/osi/os/RTEMS/osiFileName.h b/src/libCom/osi/os/RTEMS/osiFileName.h new file mode 100644 index 000000000..ecbed8e86 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osiFileName.h @@ -0,0 +1,19 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS osiFileName.h + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * eric@cls.usask.ca + * (306) 966-6055 + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/RTEMS/osiUnistd.h b/src/libCom/osi/os/RTEMS/osiUnistd.h new file mode 100644 index 000000000..1b997b985 --- /dev/null +++ b/src/libCom/osi/os/RTEMS/osiUnistd.h @@ -0,0 +1,51 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +/* + * Some systems fail to provide prototypes of these functions. + * Others provide different prototypes. + * There seems to be no way to handle this automatically, so + * if you get compile errors, just make the appropriate changes here. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +int putenv (char *); +char *strdup (const char *); +char *strtok_r(char*, const char*, char**); + +int snprintf(char *str, size_t size, const char *format, ...); +#include +int vsnprintf(char *str, size_t size, const char *format, va_list ap); + + +#ifdef __cplusplus +} +#endif diff --git a/src/libCom/osi/os/WIN32/epicsGetopt.c b/src/libCom/osi/os/WIN32/epicsGetopt.c new file mode 100644 index 000000000..97bb11f47 --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsGetopt.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include + +#define _getprogname() nargv[0] + +epicsShareDef +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +int optreset; /* reset getopt */ + +epicsShareDef +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':' && optopt != BADCH) + (void)fprintf(stderr, "%s: illegal option -- %c\n", + _getprogname(), optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + _getprogname(), optopt); + return (BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} diff --git a/src/libCom/osi/os/WIN32/epicsGetopt.h b/src/libCom/osi/os/WIN32/epicsGetopt.h new file mode 100644 index 000000000..aa3bc7422 --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsGetopt.h @@ -0,0 +1,31 @@ +/*************************************************************************\ +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* 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. +\*************************************************************************/ +#ifndef _EPICS_GETOPT_H +#define _EPICS_GETOPT_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int getopt(int argc, char * const argv[], const char *optstring); + +epicsShareExtern char *optarg; + +epicsShareExtern int optind, opterr, optopt; + +#ifdef __cplusplus +} +#endif + +#endif /* _EPICS_GETOPT_H */ diff --git a/src/libCom/osi/os/WIN32/epicsMath.h b/src/libCom/osi/os/WIN32/epicsMath.h new file mode 100644 index 000000000..33eb9dddd --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsMath.h @@ -0,0 +1,40 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include +#include + +#ifndef finite +#define finite(D) _finite(D) +#endif + +#ifndef isnan +#define isnan(D) _isnan(D) +#endif + +#ifndef isinf +#define isinf(D) ( !_finite(D) && !_isnan(D) ) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/src/libCom/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp b/src/libCom/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp new file mode 100644 index 000000000..2ec387c0c --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsSocketConvertErrnoToString.cpp @@ -0,0 +1,47 @@ + +/*************************************************************************\ +* 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 + */ + +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "epicsStdio.h" + +/* + * epicsSocketConvertErrnoToString () + */ +void epicsSocketConvertErrnoToString ( + char * pBuf, unsigned bufSize ) +{ + if ( bufSize ) { + /* + * this does not work on systems prior to W2K + */ + int theSockError = SOCKERRNO; + DWORD success = FormatMessage ( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, theSockError, + MAKELANGID ( LANG_NEUTRAL, SUBLANG_DEFAULT ), /* Default language */ + pBuf, bufSize, NULL ); + if ( ! success ) { + int status = epicsSnprintf ( + pBuf, bufSize, "WINSOCK Error %d", theSockError ); + if ( status <= 0 ) { + strncpy ( pBuf, "WINSOCK Error", bufSize ); + pBuf [bufSize - 0] = '\0'; + } + } + } +} diff --git a/src/libCom/osi/os/WIN32/epicsTempFile.cpp b/src/libCom/osi/os/WIN32/epicsTempFile.cpp new file mode 100644 index 000000000..12f374281 --- /dev/null +++ b/src/libCom/osi/os/WIN32/epicsTempFile.cpp @@ -0,0 +1,94 @@ +/*************************************************************************\ +* 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 + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" + +// +// epicsTempName +// +// allow the teporary file directory to be set with the +// TMP environment varianble +// +extern "C" +epicsShareFunc void epicsShareAPI epicsTempName ( + char * pNameBuf, size_t nameBufLength ) +{ + if ( nameBufLength ) { + pNameBuf[0] = '\0'; + char * pName = _tempnam ( "c:\\tmp", "epics" ); + if ( pName ) { + if ( nameBufLength > strlen ( pName ) ) { + strncpy ( pNameBuf, pName, nameBufLength ); + } + free ( pName ); + } + } +} + +// +// epicsTmpFile +// +// allow the teporary file directory to be set with the +// TMP environment varianble +// +extern "C" +epicsShareFunc FILE * epicsShareAPI epicsTempFile () +{ + char * pName = _tempnam ( "c:\\tmp", "epics" ); + if( ! pName ) { + return 0; + } + // We use open followed by fdopen so that the _O_EXCL + // flag can be used. This causes detection of a race + // condition where two programs end up receiving the + // same temporary file name. + // + // _O_CREAT create if non-existant + // _O_EXCL file must not exist + // _O_RDWR read and write the file + // _O_TEMPORARY delete file on close + // _O_BINARY no translation + // _O_SHORT_LIVED avoid flush to disk + // + // (Borland does not supply _O_SHORT_LIVED and _O_TEMPORARY) +# ifndef _O_SHORT_LIVED +# define _O_SHORT_LIVED 0x1000 +# endif +# ifndef _O_TEMPORARY +# define _O_TEMPORARY 0x0040 +# endif + const int openFlag = _O_CREAT | _O_EXCL | _O_RDWR | + _O_SHORT_LIVED | _O_BINARY | _O_TEMPORARY; + int fd = open ( pName, openFlag, _S_IWRITE ); + FILE * pNewFile = 0; + if ( fd >=0 ) { + pNewFile = _fdopen ( fd, "w+b" ); + } + else { + printf ( + "Temporary file \"%s\" open failed because " + "\"%s\"\n", pName, strerror ( errno ) ); + } + free ( pName ); + return pNewFile; +} + diff --git a/src/libCom/osi/os/WIN32/forceBadAllocException.cpp b/src/libCom/osi/os/WIN32/forceBadAllocException.cpp new file mode 100644 index 000000000..985ec988e --- /dev/null +++ b/src/libCom/osi/os/WIN32/forceBadAllocException.cpp @@ -0,0 +1,56 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +// +// On older version of ms visual c++ operator new +// does not throw a bad_alloc exception +// when it fails. It simply returns a null pointer. +// This behavior is not in conformance with the +// ANSI / ISO C++. +// +#if _MSC_VER > 1000 && _MSC_VER < 1400 + +#include +#include + +// instruct loader to call this gllobal object +// constructor before user global object constructors +#pragma warning (disable: 4073) +#pragma init_seg(lib) +#pragma warning (default: 4073) + +static int my_new_handler (size_t size) +{ + throw std::bad_alloc(); + return 0; +} + +class my_new_handler_obj +{ +public: + my_new_handler_obj() + { + //_old_new_mode = _set_new_mode(1); // cause malloc to throw like new + _old_new_handler = _set_new_handler(my_new_handler); + } + + ~my_new_handler_obj() + { + _set_new_handler(_old_new_handler); + //_set_new_mode(_old_new_mode); + } + +private: + _PNH _old_new_handler; + //int _old_new_mode; +}; + +static my_new_handler_obj _g_new_handler_obj; + +#endif diff --git a/src/libCom/osi/os/WIN32/osdEvent.c b/src/libCom/osi/os/WIN32/osdEvent.c new file mode 100644 index 000000000..a9b2eee14 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdEvent.c @@ -0,0 +1,159 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osdEvent.c */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + */ + +#include + +#define VC_EXTRALEAN +#define STRICT +#include + +#define epicsExportSharedSymbols +#include "shareLib.h" +#include "epicsEvent.h" +#include "epicsAssert.h" + +typedef struct epicsEventOSD { + HANDLE handle; +} epicsEventOSD; + +/* + * epicsEventCreate () + */ +epicsShareFunc epicsEventId epicsShareAPI epicsEventCreate ( + epicsEventInitialState initialState ) +{ + epicsEventOSD *pSem; + + pSem = malloc ( sizeof ( *pSem ) ); + if ( pSem ) { + pSem->handle = CreateEvent ( NULL, FALSE, initialState?TRUE:FALSE, NULL ); + if ( pSem->handle == 0 ) { + free ( pSem ); + pSem = 0; + } + } + + return pSem; +} + +/* + * epicsEventMustCreate () + */ +epicsShareFunc epicsEventId epicsShareAPI epicsEventMustCreate ( + epicsEventInitialState initialState ) +{ + epicsEventId id = epicsEventCreate ( initialState ); + assert ( id ); + return id; +} + +/* + * epicsEventDestroy () + */ +epicsShareFunc void epicsShareAPI epicsEventDestroy ( epicsEventId pSem ) +{ + CloseHandle ( pSem->handle ); + free ( pSem ); +} + +/* + * epicsEventSignal () + */ +epicsShareFunc void epicsShareAPI epicsEventSignal ( epicsEventId pSem ) +{ + BOOL status; + status = SetEvent ( pSem->handle ); + assert ( status ); +} + +/* + * epicsEventWait () + */ +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventWait ( epicsEventId pSem ) +{ + DWORD status; + status = WaitForSingleObject (pSem->handle, INFINITE); + if ( status == WAIT_OBJECT_0 ) { + return epicsEventWaitOK; + } + else { + return epicsEventWaitError; + } +} + +/* + * epicsEventWaitWithTimeout () + */ +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventWaitWithTimeout ( + epicsEventId pSem, double timeOut ) +{ + static const unsigned mSecPerSec = 1000; + DWORD status; + DWORD tmo; + + if ( timeOut <= 0.0 ) { + tmo = 0u; + } + else if ( timeOut >= INFINITE / mSecPerSec ) { + tmo = INFINITE - 1; + } + else { + tmo = ( DWORD ) ( ( timeOut * mSecPerSec ) + 0.5 ); + if ( tmo == 0 ) { + tmo = 1; + } + } + status = WaitForSingleObject ( pSem->handle, tmo ); + if ( status == WAIT_OBJECT_0 ) { + return epicsEventWaitOK; + } + else if ( status == WAIT_TIMEOUT ) { + return epicsEventWaitTimeout; + } + else { + return epicsEventWaitError; + } +} + +/* + * epicsEventTryWait () + */ +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventTryWait ( epicsEventId pSem ) +{ + DWORD status; + + status = WaitForSingleObject ( pSem->handle, 0 ); + if ( status == WAIT_OBJECT_0 ) { + return epicsEventWaitOK; + } + else if ( status == WAIT_TIMEOUT ) { + return epicsEventWaitTimeout; + } + else { + return epicsEventWaitError; + } +} + +/* + * epicsEventShow () + */ +epicsShareFunc void epicsShareAPI epicsEventShow ( epicsEventId id, unsigned level ) +{ +} diff --git a/src/libCom/osi/os/WIN32/osdEvent.h b/src/libCom/osi/os/WIN32/osdEvent.h new file mode 100644 index 000000000..a5cefab64 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdEvent.h @@ -0,0 +1,20 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osdEvent.c */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +/* osdEvent.h not needed */ diff --git a/src/libCom/osi/os/WIN32/osdMutex.c b/src/libCom/osi/os/WIN32/osdMutex.c new file mode 100644 index 000000000..19314b556 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdMutex.c @@ -0,0 +1,178 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osdMutex.c */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + */ + +#include +#include + +#define VC_EXTRALEAN +#define STRICT +/* + * Defining this allows the *much* faster critical + * section mutex primitive to be used. Unfortunately, + * using certain of these functions drops support for W95\W98\WME + * unless we specify "delay loading" when we link with the + * DLL so that DLL entry points are not resolved until they + * are used. The code does have run time switches so + * that the more advanced calls are not called unless + * they are available in the windows OS, but this feature + * isnt going to be very useful unless we specify "delay + * loading" when we link with the DLL. + * + * It appears that the only entry point used here that causes + * portability problems with W95\W98\WME is TryEnterCriticalSection. + */ +#define _WIN32_WINNT 0x0400 +#include + +#define epicsExportSharedSymbols +#include "shareLib.h" +#include "epicsMutex.h" +#include "epicsAssert.h" +#include "epicsStdioRedirect.h" + +typedef struct epicsMutexOSD { + union { + HANDLE mutex; + CRITICAL_SECTION criticalSection; + } os; +} epicsMutexOSD; + +static BOOL thisIsNT = FALSE; +static LONG weHaveInitialized = 0; + +/* + * epicsMutexCreate () + */ +epicsMutexOSD * epicsMutexOsdCreate ( void ) +{ + epicsMutexOSD * pSem; + + if ( ! weHaveInitialized ) { + BOOL status; + OSVERSIONINFO osInfo; + osInfo.dwOSVersionInfoSize = sizeof ( OSVERSIONINFO ); + status = GetVersionEx ( & osInfo ); + thisIsNT = status && ( osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ); + weHaveInitialized = 1; + } + + pSem = malloc ( sizeof (*pSem) ); + if ( pSem ) { + if ( thisIsNT ) { + InitializeCriticalSection ( &pSem->os.criticalSection ); + } + else { + pSem->os.mutex = CreateMutex ( NULL, FALSE, NULL ); + if ( pSem->os.mutex == 0 ) { + free ( pSem ); + pSem = 0; + } + } + } + return pSem; +} + +/* + * epicsMutexOsdDestroy () + */ +void epicsMutexOsdDestroy ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + DeleteCriticalSection ( &pSem->os.criticalSection ); + } + else { + CloseHandle ( pSem->os.mutex ); + } + free ( pSem ); +} + +/* + * epicsMutexOsdUnlock () + */ +void epicsMutexOsdUnlock ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + LeaveCriticalSection ( &pSem->os.criticalSection ); + } + else { + BOOL success = ReleaseMutex ( pSem->os.mutex ); + assert ( success ); + } +} + +/* + * epicsMutexOsdLock () + */ +epicsMutexLockStatus epicsMutexOsdLock ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + EnterCriticalSection ( &pSem->os.criticalSection ); + } + else { + DWORD status = WaitForSingleObject ( pSem->os.mutex, INFINITE ); + if ( status != WAIT_OBJECT_0 ) { + return epicsMutexLockError; + } + } + return epicsMutexLockOK; +} + +/* + * epicsMutexOsdTryLock () + */ +epicsMutexLockStatus epicsMutexOsdTryLock ( epicsMutexOSD * pSem ) +{ + if ( thisIsNT ) { + if ( TryEnterCriticalSection ( &pSem->os.criticalSection ) ) { + return epicsMutexLockOK; + } + else { + return epicsMutexLockTimeout; + } + } + else { + DWORD status = WaitForSingleObject ( pSem->os.mutex, 0 ); + if ( status != WAIT_OBJECT_0 ) { + if (status == WAIT_TIMEOUT) { + return epicsMutexLockTimeout; + } + else { + return epicsMutexLockError; + } + } + } + return epicsMutexLockOK; +} + +/* + * epicsMutexOsdShow () + */ +void epicsMutexOsdShow ( epicsMutexOSD * pSem, unsigned level ) +{ + if ( thisIsNT ) { + printf ("epicsMutex: win32 critical section at %p\n", + (void * ) & pSem->os.criticalSection ); + } + else { + printf ( "epicsMutex: win32 mutex at %p\n", + ( void * ) pSem->os.mutex ); + } +} + diff --git a/src/libCom/osi/os/WIN32/osdMutex.h b/src/libCom/osi/os/WIN32/osdMutex.h new file mode 100644 index 000000000..663cffe33 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdMutex.h @@ -0,0 +1,24 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* osdMutex.h */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * WIN32 version + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef osdMutexh +#define osdMutexh + +#endif /* osdMutexh */ diff --git a/src/libCom/osi/os/WIN32/osdNetIntf.c b/src/libCom/osi/os/WIN32/osdNetIntf.c new file mode 100644 index 000000000..e65b1e224 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdNetIntf.c @@ -0,0 +1,242 @@ +/*************************************************************************\ +* 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 + * + * WIN32 specific initialisation for bsd sockets, + * based on Chris Timossi's base/src/ca/windows_depend.c, + * and also further additions by Kay Kasemir when this was in + * dllmain.cc + * + * 7-1-97 -joh- + * + */ + +/* + * ANSI C + */ +#include +#include + +/* + * WIN32 specific + */ +#define VC_EXTRALEAN +#define STRICT +#include +#include + +/* + * EPICS + */ +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" +#include "epicsVersion.h" + +/* + * osiLocalAddr () + */ +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr ( SOCKET socket ) +{ + static osiSockAddr addr; + static char init; + int status; + INTERFACE_INFO *pIfinfo; + INTERFACE_INFO *pIfinfoList; + unsigned nelem; + DWORD numifs; + DWORD cbBytesReturned; + + if (init) { + return addr; + } + + init = 1; + addr.sa.sa_family = AF_UNSPEC; + + /* only valid for winsock 2 and above */ + if ( wsaMajorVersion() < 2 ) { + return addr; + } + + nelem = 10; + pIfinfoList = (INTERFACE_INFO *) calloc ( nelem, sizeof (INTERFACE_INFO) ); + if (!pIfinfoList) { + errlogPrintf ("calloc failed\n"); + return addr; + } + + status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, NULL, 0, + (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), + &cbBytesReturned, NULL, NULL); + + if (status != 0 || cbBytesReturned == 0) { + errlogPrintf ("WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); + free (pIfinfoList); + return addr; + } + + numifs = cbBytesReturned / sizeof(INTERFACE_INFO); + for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ + + /* + * dont use interfaces that have been disabled + */ + if (!(pIfinfo->iiFlags & IFF_UP)) { + continue; + } + + /* + * dont use the loop back interface + */ + if (pIfinfo->iiFlags & IFF_LOOPBACK) { + continue; + } + + addr.sa = pIfinfo->iiAddress.Address; + + /* Work around MS Winsock2 bugs */ + if (addr.sa.sa_family == 0) { + addr.sa.sa_family = AF_INET; + } + + free (pIfinfoList); + return addr; + } + + free (pIfinfoList); + return addr; +} + +/* + * osiSockDiscoverBroadcastAddresses () + */ +epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses + (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr) +{ + int status; + INTERFACE_INFO *pIfinfo; + INTERFACE_INFO *pIfinfoList; + unsigned nelem; + int numifs; + DWORD cbBytesReturned; + osiSockAddrNode *pNewNode; + + if ( pMatchAddr->sa.sa_family == AF_INET ) { + if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) { + pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); + if ( pNewNode == NULL ) { + return; + } + pNewNode->addr.ia.sin_family = AF_INET; + pNewNode->addr.ia.sin_port = htons ( 0 ); + pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + ellAdd ( pList, &pNewNode->node ); + return; + } + } + + /* only valid for winsock 2 and above */ + if (wsaMajorVersion() < 2 ) { + fprintf(stderr, "Need to set EPICS_CA_AUTO_ADDR_LIST=NO for winsock 1\n"); + return; + } + + nelem = 10; + pIfinfoList = (INTERFACE_INFO *) calloc(nelem, sizeof(INTERFACE_INFO)); + if(!pIfinfoList){ + return; + } + + status = WSAIoctl (socket, SIO_GET_INTERFACE_LIST, + NULL, 0, + (LPVOID)pIfinfoList, nelem*sizeof(INTERFACE_INFO), + &cbBytesReturned, NULL, NULL); + + if (status != 0 || cbBytesReturned == 0) { + fprintf(stderr, "WSAIoctl SIO_GET_INTERFACE_LIST failed %d\n",WSAGetLastError()); + free(pIfinfoList); + return; + } + + numifs = cbBytesReturned/sizeof(INTERFACE_INFO); + for (pIfinfo = pIfinfoList; pIfinfo < (pIfinfoList+numifs); pIfinfo++){ + + /* + * dont bother with interfaces that have been disabled + */ + if (!(pIfinfo->iiFlags & IFF_UP)) { + continue; + } + + if (pIfinfo->iiFlags & IFF_LOOPBACK) { + continue; + } + + /* + * work around WS2 bug + */ + if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { + if (pIfinfo->iiAddress.Address.sa_family == 0) { + pIfinfo->iiAddress.Address.sa_family = AF_INET; + } + } + + /* + * if it isnt a wildcarded interface then look for + * an exact match + */ + if (pMatchAddr->sa.sa_family != AF_UNSPEC) { + if (pIfinfo->iiAddress.Address.sa_family != pMatchAddr->sa.sa_family) { + continue; + } + if (pIfinfo->iiAddress.Address.sa_family != AF_INET) { + continue; + } + if (pMatchAddr->sa.sa_family != AF_INET) { + continue; + } + if (pMatchAddr->ia.sin_addr.s_addr != htonl(INADDR_ANY)) { + if (pIfinfo->iiAddress.AddressIn.sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr) { + continue; + } + } + } + + pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode)); + if (pNewNode==NULL) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n"); + return; + } + + if (pIfinfo->iiAddress.Address.sa_family == AF_INET && + pIfinfo->iiFlags & IFF_BROADCAST) { + const unsigned mask = pIfinfo->iiNetmask.AddressIn.sin_addr.s_addr; + const unsigned bcast = pIfinfo->iiBroadcastAddress.AddressIn.sin_addr.s_addr; + const unsigned addr = pIfinfo->iiAddress.AddressIn.sin_addr.s_addr; + unsigned result = (addr & mask) | (bcast &~mask); + pNewNode->addr.ia.sin_family = AF_INET; + pNewNode->addr.ia.sin_addr.s_addr = result; + pNewNode->addr.ia.sin_port = htons ( 0 ); + } + else { + pNewNode->addr.sa = pIfinfo->iiBroadcastAddress.Address; + } + + /* + * LOCK applied externally + */ + ellAdd (pList, &pNewNode->node); + } + + free (pIfinfoList); +} diff --git a/src/libCom/osi/os/WIN32/osdPoolStatus.c b/src/libCom/osi/os/WIN32/osdPoolStatus.c new file mode 100644 index 000000000..bb764b2ad --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdPoolStatus.c @@ -0,0 +1,23 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "osiPoolStatus.h" + +/* + * osiSufficentSpaceInPool () + * + * @@@@@ not implemented @@@@@ + * + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + return 1; +} diff --git a/src/libCom/osi/os/WIN32/osdPoolStatus.h b/src/libCom/osi/os/WIN32/osdPoolStatus.h new file mode 100644 index 000000000..178434b11 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdPoolStatus.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifdef osdPoolStatush +#define osdPoolStatush + +#endif /* osdPoolStatush */ diff --git a/src/libCom/osi/os/WIN32/osdProcess.c b/src/libCom/osi/os/WIN32/osdProcess.c new file mode 100644 index 000000000..4200f8ccc --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdProcess.c @@ -0,0 +1,153 @@ +/*************************************************************************\ +* 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 + * + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +#ifndef _WIN32 +#error This source is specific to WIN32 +#endif + +#include + +#define STRICT +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" +#include "errlog.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + DWORD bufsize; + + if ( bufSizeIn > 0xffffffff ) { + return osiGetUserNameFail; + } + + bufsize = (DWORD) bufSizeIn; + + if ( ! GetUserName (pBuf, &bufsize) ) { + return osiGetUserNameFail; + } + + if ( *pBuf == '\0' ) { + return osiGetUserNameFail; + } + + return osiGetUserNameSuccess; +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + ( const char *pProcessName, const char *pBaseExecutableName ) +{ + BOOL status; + STARTUPINFO startupInfo; + PROCESS_INFORMATION processInfo; + + GetStartupInfo ( &startupInfo ); + startupInfo.lpReserved = NULL; + startupInfo.lpTitle = (char *) pProcessName; + startupInfo.dwFlags = STARTF_USESHOWWINDOW; + startupInfo.wShowWindow = SW_SHOWMINNOACTIVE; + + status = CreateProcess ( + NULL, /* pointer to name of executable module (not required if command line is specified) */ + (char *) pBaseExecutableName, /* pointer to command line string */ + NULL, /* pointer to process security attributes */ + NULL, /* pointer to thread security attributes */ + FALSE, /* handle inheritance flag */ + CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS, /* creation flags */ + NULL, /* pointer to new environment block (defaults to caller's environement) */ + NULL, /* pointer to current directory name (defaults to caller's current directory) */ + &startupInfo, /* pointer to STARTUPINFO */ + &processInfo /* pointer to PROCESS_INFORMATION */ + ); + if ( status == 0 ) { + DWORD W32status; + LPVOID errStrMsgBuf; + LPVOID complteMsgBuf; + + W32status = FormatMessage ( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError (), + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &errStrMsgBuf, + 0, + NULL + ); + + if ( W32status ) { + char *pFmtArgs[6]; + pFmtArgs[0] = "Failed to start executable -"; + pFmtArgs[1] = (char *) pBaseExecutableName; + pFmtArgs[2] = errStrMsgBuf; + pFmtArgs[3] = "Changes may be required in your \"path\" environment variable."; + pFmtArgs[4] = "PATH = "; + pFmtArgs[5] = getenv ("path"); + if ( pFmtArgs[5] == NULL ) { + pFmtArgs[5] = ""; + } + + W32status = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ARGUMENT_ARRAY | 80, + "%1 \"%2\". %3 %4 %5 \"%6\"", + 0, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &complteMsgBuf, + 0, + pFmtArgs + ); + if (W32status) { + /* Display the string. */ + MessageBox (NULL, complteMsgBuf, "Configuration Problem", + MB_OK | MB_ICONINFORMATION); + LocalFree (complteMsgBuf); + } + else { + /* Display the string. */ + MessageBox (NULL, errStrMsgBuf, "Failed to start executable", + MB_OK | MB_ICONINFORMATION); + } + + /* Free the buffer. */ + LocalFree (errStrMsgBuf); + } + else { + errlogPrintf ("!!WARNING!!\n"); + errlogPrintf ("Unable to locate executable \"%s\".\n", pBaseExecutableName); + errlogPrintf ("You may need to modify your environment.\n"); + } + return osiSpawnDetachedProcessFail; + } + + return osiSpawnDetachedProcessSuccess; + + /* + use of spawn here causes problems when the ca repeater + inherits open files (and sockets) from the spawning + process + + status = _spawnlp (_P_DETACH, pBaseExecutableName, pBaseExecutableName, NULL); + if (status<0) { + errlogPrintf ("!!WARNING!!\n"); + errlogPrintf ("Unable to locate the EPICS executable \"%s\".\n", + pBaseExecutableName); + errlogPrintf ("You may need to modify your environment.\n"); + } + */ +} diff --git a/src/libCom/osi/os/WIN32/osdSignal.cpp b/src/libCom/osi/os/WIN32/osdSignal.cpp new file mode 100644 index 000000000..5f799273a --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdSignal.cpp @@ -0,0 +1,21 @@ +/*************************************************************************\ + * 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. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * NOOP + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadid */ ) {} + diff --git a/src/libCom/osi/os/WIN32/osdSock.c b/src/libCom/osi/os/WIN32/osdSock.c new file mode 100644 index 000000000..2621f71c8 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdSock.c @@ -0,0 +1,192 @@ +/*************************************************************************\ +* 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 + * + * WIN32 specific initialisation for bsd sockets, + * based on Chris Timossi's base/src/ca/windows_depend.c, + * and also further additions by Kay Kasemir when this was in + * dllmain.cc + * + * 7-1-97 -joh- + * + */ + +/* + * ANSI C + */ +#include +#include + +/* + * WIN32 specific + */ +#define VC_EXTRALEAN +#define STRICT +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" +#include "epicsVersion.h" + +static unsigned nAttached = 0; +static WSADATA WsaData; /* version of winsock */ + +epicsShareFunc unsigned epicsShareAPI wsaMajorVersion () +{ + return (unsigned) LOBYTE( WsaData.wVersion ); +} + +/* + * osiSockAttach() + */ +epicsShareFunc int epicsShareAPI osiSockAttach() +{ + int status; + + if (nAttached) { + nAttached++; + return TRUE; + } + +#if _DEBUG + /* for gui applications, setup console for error messages */ + if (AllocConsole()) + { + char title[256]; + DWORD titleLength = GetConsoleTitle(title, sizeof(title)); + if (titleLength) { + titleLength = strlen (title); + strncat (title, " " EPICS_VERSION_STRING, sizeof(title)); + } + else { + strncpy(title, EPICS_VERSION_STRING, sizeof(title)); + } + title[sizeof(title)-1]= '\0'; + SetConsoleTitle(title); + freopen( "CONOUT$", "a", stderr ); + } +#endif + + /* + * attach to win sock + */ + status = WSAStartup(MAKEWORD(/*major*/2,/*minor*/2), &WsaData); + if (status != 0) { + fprintf(stderr, + "Unable to attach to windows sockets version 2. error=%d\n", status); + fprintf(stderr, + "A Windows Sockets II update for windows 95 is available at\n"); + fprintf(stderr, + "http://www.microsoft.com/windows95/info/ws2.htm"); + return FALSE; + } + +# if defined ( _DEBUG ) && 0 + fprintf(stderr, "EPICS attached to winsock version %s\n", WsaData.szDescription); +# endif + + nAttached = 1u; + + return TRUE; +} + +/* + * osiSockRelease() + */ +epicsShareFunc void epicsShareAPI osiSockRelease() +{ + if (nAttached) { + if (--nAttached==0u) { + WSACleanup(); +# if defined ( _DEBUG ) && 0 + fprintf(stderr, "EPICS released winsock version %s\n", WsaData.szDescription); +# endif + memset (&WsaData, '\0', sizeof(WsaData)); + } + } +} + +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ) +{ + return socket ( domain, type, protocol ); +} + +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) +{ + return accept ( sock, pAddr, addrlen ); +} + +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) +{ + int status = closesocket ( s ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketDestroy: failed to " + "close a socket because \"%s\"\n", buf ); + } +} + +/* + * ipAddrToHostName + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName + (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + struct hostent *ent; + + if (bufSize<1) { + return 0; + } + + ent = gethostbyaddr((char *) pAddr, sizeof (*pAddr), AF_INET); + if (ent) { + strncpy (pBuf, ent->h_name, bufSize); + pBuf[bufSize-1] = '\0'; + return strlen (pBuf); + } + return 0; +} + +/* + * hostToIPAddr () + */ +epicsShareFunc int epicsShareAPI hostToIPAddr + (const char *pHostName, struct in_addr *pIPA) +{ + struct hostent *phe; + + phe = gethostbyname (pHostName); + if (phe && phe->h_addr_list[0]) { + if (phe->h_addrtype==AF_INET && phe->h_length<=sizeof(struct in_addr)) { + struct in_addr *pInAddrIn = (struct in_addr *) phe->h_addr_list[0]; + + *pIPA = *pInAddrIn; + + /* + * success + */ + return 0; + } + } + + /* + * return indicating an error + */ + return -1; +} + + diff --git a/src/libCom/osi/os/WIN32/osdSock.h b/src/libCom/osi/os/WIN32/osdSock.h new file mode 100644 index 000000000..f362ed8b3 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdSock.h @@ -0,0 +1,81 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* + * winsock2.h changes the structure alignment to 4 if + * WIN32 isnt set which can be a source of confusion + */ +#ifndef WIN32 +# define WIN32 +#endif +#include + +#ifdef __cplusplus +} +#endif + +#define SOCKERRNO WSAGetLastError() + +#define socket_ioctl(A,B,C) ioctlsocket(A,B,C) +typedef u_long FAR osiSockIoctl_t; +typedef int osiSocklen_t; + +#ifndef SHUT_RD +# define SHUT_RD SD_RECEIVE +#endif + +#ifndef SHUT_WR +# define SHUT_WR SD_SEND +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR SD_BOTH +#endif + +#define MAXHOSTNAMELEN 75 +#define IPPORT_USERRESERVED 5000U + +#define SOCK_EWOULDBLOCK WSAEWOULDBLOCK +#define SOCK_ENOBUFS WSAENOBUFS +#define SOCK_ECONNRESET WSAECONNRESET +#define SOCK_ETIMEDOUT WSAETIMEDOUT +#define SOCK_EADDRINUSE WSAEADDRINUSE +#define SOCK_ECONNREFUSED WSAECONNREFUSED +#define SOCK_ECONNABORTED WSAECONNABORTED +#define SOCK_EINPROGRESS WSAEINPROGRESS +#define SOCK_EISCONN WSAEISCONN +#define SOCK_EALREADY WSAEALREADY +#define SOCK_EINVAL WSAEINVAL +#define SOCK_EINTR WSAEINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE WSAEMFILE +#define SOCK_SHUTDOWN WSAESHUTDOWN +#define SOCK_ENOTSOCK WSAENOTSOCK +#define SOCK_EBADF WSAENOTSOCK + +/* + * Under WIN32, FD_SETSIZE is the max. number of sockets, + * not the max. fd value that you use in select(). + * + * Therefore, it is difficult to detemine if any given + * fd can be used with FD_SET(), FD_CLR(), and FD_ISSET(). + */ +#define FD_IN_FDSET(FD) (1) + +epicsShareFunc unsigned epicsShareAPI wsaMajorVersion (); + diff --git a/src/libCom/osi/os/WIN32/osdSockAddrReuse.cpp b/src/libCom/osi/os/WIN32/osdSockAddrReuse.cpp new file mode 100644 index 000000000..a2ac8cd1b --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdSockAddrReuse.cpp @@ -0,0 +1,45 @@ + +/*************************************************************************\ +* 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 + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +/* + * Note: WINSOCK appears to assign a different functionality for + * SO_REUSEADDR compared to other OS. With WINSOCK SO_REUSEADDR indicates + * that simultaneously servers can bind to the same TCP port on the same host! + * Also, servers are always enabled to reuse a port immediately after + * they exit ( even if SO_REUSEADDR isnt set ). + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ +} + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} diff --git a/src/libCom/osi/os/WIN32/osdStdio.c b/src/libCom/osi/os/WIN32/osdStdio.c new file mode 100644 index 000000000..602651f8b --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdStdio.c @@ -0,0 +1,54 @@ +/* osdStdio.c */ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" + +int epicsShareAPI epicsVsnprintf(char *str, size_t len, + const char *fmt, va_list ap) +{ + int retval = _vsnprintf(str, len, fmt, ap); + +#ifdef _MSC_VER + int needed = _vscprintf(fmt, ap); + + if ((int) len < needed + 1) { + str[len - 1] = 0; + return needed; + } +#else + /* Unfortunately MINGW doesn't provide _vscprintf and their + * _vsnprintf follows MS' broken return value semantics. + */ + if (retval == -1) { + if (len) + str[len - 1] = 0; + return len; + } else if (retval == (int) len) { + str[--retval] = 0; + } +#endif + + return retval; +} + +int epicsShareAPI epicsSnprintf (char *str, size_t len, const char *fmt, ...) +{ + int rtn; + va_list pvar; + + va_start (pvar, fmt); + rtn = epicsVsnprintf (str, len, fmt, pvar); + va_end (pvar); + return (rtn); +} diff --git a/src/libCom/osi/os/WIN32/osdStrtod.h b/src/libCom/osi/os/WIN32/osdStrtod.h new file mode 100644 index 000000000..28fd36b9d --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdStrtod.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +epicsShareFunc double epicsStrtod(const char *str, char **endp); diff --git a/src/libCom/osi/os/WIN32/osdThread.c b/src/libCom/osi/os/WIN32/osdThread.c new file mode 100644 index 000000000..64b245867 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdThread.c @@ -0,0 +1,1090 @@ +/*************************************************************************\ +* 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 + * + * + */ + +#include +#include +#include +#include + +#define VC_EXTRALEAN +#define STRICT +#if _WIN64 +# define _WIN32_WINNT 0x400 /* defining this drops support for W95 */ +#endif +#include +#include /* for _endthread() etc */ + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "shareLib.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsAssert.h" +#include "ellLib.h" +#include "epicsExit.h" + +void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ); +static void threadCleanupWIN32 ( void ); + +typedef struct win32ThreadGlobal { + CRITICAL_SECTION mutex; + ELLLIST threadList; + DWORD tlsIndexThreadLibraryEPICS; +} win32ThreadGlobal; + +typedef struct epicsThreadOSD { + ELLNODE node; + HANDLE handle; + EPICSTHREADFUNC funptr; + void * parm; + char * pName; + DWORD id; + unsigned epicsPriority; + char isSuspended; +} win32ThreadParam; + +typedef struct epicsThreadPrivateOSD { + DWORD key; +} epicsThreadPrivateOSD; + +#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION +# define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 +#endif + +#define osdOrdinaryPriorityStateCount 5u +static const int osdOrdinaryPriorityList [osdOrdinaryPriorityStateCount] = +{ + THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */ + THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */ + THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_HIGHEST /* 2 on >= W2K ??? on W95 */ +}; + +# define osdRealtimePriorityStateCount 14u +static const int osdRealtimePriorityList [osdRealtimePriorityStateCount] = +{ + -7, /* allowed on >= W2k, but no #define supplied */ + -6, /* allowed on >= W2k, but no #define supplied */ + -5, /* allowed on >= W2k, but no #define supplied */ + -4, /* allowed on >= W2k, but no #define supplied */ + -3, /* allowed on >= W2k, but no #define supplied */ + THREAD_PRIORITY_LOWEST, /* -2 on >= W2K ??? on W95 */ + THREAD_PRIORITY_BELOW_NORMAL, /* -1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_NORMAL, /* 0 on >= W2K ??? on W95 */ + THREAD_PRIORITY_ABOVE_NORMAL, /* 1 on >= W2K ??? on W95 */ + THREAD_PRIORITY_HIGHEST, /* 2 on >= W2K ??? on W95 */ + 3, /* allowed on >= W2k, but no #define supplied */ + 4, /* allowed on >= W2k, but no #define supplied */ + 5, /* allowed on >= W2k, but no #define supplied */ + 6 /* allowed on >= W2k, but no #define supplied */ +}; + +#if !defined(EPICS_DLL_NO) +BOOL WINAPI DllMain ( + HANDLE hModule, DWORD dwReason, LPVOID lpReserved ) +{ + static DWORD dllHandleIndex; + HMODULE dllHandle = 0; + BOOL success = TRUE; + + switch ( dwReason ) + { + case DLL_PROCESS_ATTACH: + dllHandleIndex = TlsAlloc (); + if ( dllHandleIndex == TLS_OUT_OF_INDEXES ) { + success = FALSE; + } + break; + + case DLL_PROCESS_DETACH: + success = TlsFree ( dllHandleIndex ); + break; + + case DLL_THREAD_ATTACH: + /* + * Dont allow user's explicitly calling FreeLibrary for Com.dll to yank + * the carpet out from under EPICS threads that are still using Com.dll + */ +#if _WIN32_WINNT >= 0x0501 + /* + * Only in WXP + * Thats a shame becaus ethis is probably much faster + */ + success = GetModuleHandleEx ( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + ( LPCTSTR ) DllMain, & dllHandle ); +#else + { + char name[256]; + DWORD nChar = GetModuleFileName ( + hModule, name, sizeof ( name ) ); + if ( nChar && nChar < sizeof ( name ) ) { + dllHandle = LoadLibrary ( name ); + if ( ! dllHandle ) { + success = FALSE; + } + } + else { + success = FALSE; + } + } +#endif + if ( success ) { + success = TlsSetValue ( dllHandleIndex, dllHandle ); + } + break; + case DLL_THREAD_DETACH: + /* + * Thread is exiting, release Com.dll. I am assuming that windows is + * smart enough to postpone the unload until this function returns. + */ + dllHandle = TlsGetValue ( dllHandleIndex ); + if ( dllHandle ) { + success = FreeLibrary ( dllHandle ); + } + break; + } + + return success; +} +#endif + +/* + * fetchWin32ThreadGlobal () + * Search for "Synchronization and Multiprocessor Issues" in ms doc + * to understand why this is necessary and why this works on smp systems. + */ +static win32ThreadGlobal * fetchWin32ThreadGlobal ( void ) +{ + static win32ThreadGlobal * pWin32ThreadGlobal = 0; + static LONG initStarted = 0; + static LONG initCompleted = 0; + int crtlStatus; + LONG started; + LONG done; + + done = InterlockedCompareExchange ( & initCompleted, 0, 0 ); + if ( done ) { + return pWin32ThreadGlobal; + } + + started = InterlockedCompareExchange ( & initStarted, 0, 1 ); + if ( started ) { + unsigned tries = 0u; + while ( ! InterlockedCompareExchange ( & initCompleted, 0, 0 ) ) { + /* + * I am not fond of busy loops, but since this will + * collide very infrequently and this is the lowest + * level init then perhaps this is ok + */ + Sleep ( 1 ); + if ( tries++ > 1000 ) { + return 0; + } + } + return pWin32ThreadGlobal; + } + + pWin32ThreadGlobal = ( win32ThreadGlobal * ) + calloc ( 1, sizeof ( * pWin32ThreadGlobal ) ); + if ( ! pWin32ThreadGlobal ) { + InterlockedExchange ( & initStarted, 0 ); + return 0; + } + + InitializeCriticalSection ( & pWin32ThreadGlobal->mutex ); + ellInit ( & pWin32ThreadGlobal->threadList ); + pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS = TlsAlloc(); + if ( pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS == 0xFFFFFFFF ) { + DeleteCriticalSection ( & pWin32ThreadGlobal->mutex ); + free ( pWin32ThreadGlobal ); + pWin32ThreadGlobal = 0; + return 0; + } + + crtlStatus = atexit ( threadCleanupWIN32 ); + if ( crtlStatus ) { + TlsFree ( pWin32ThreadGlobal->tlsIndexThreadLibraryEPICS ); + DeleteCriticalSection ( & pWin32ThreadGlobal->mutex ); + free ( pWin32ThreadGlobal ); + pWin32ThreadGlobal = 0; + return 0; + } + + InterlockedExchange ( & initCompleted, 1 ); + + return pWin32ThreadGlobal; +} + +static void epicsParmCleanupWIN32 ( win32ThreadParam * pParm ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + if ( ! pGbl ) { + fprintf ( stderr, "epicsParmCleanupWIN32: unable to find ctx\n" ); + return; + } + + if ( pParm ) { + /* fprintf ( stderr, "thread %s is exiting\n", pParm->pName ); */ + EnterCriticalSection ( & pGbl->mutex ); + ellDelete ( & pGbl->threadList, & pParm->node ); + LeaveCriticalSection ( & pGbl->mutex ); + + /* close the handle if its an implicit thread id */ + if ( ! pParm->funptr ) { + CloseHandle ( pParm->handle ); + } + free ( pParm ); + TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, 0 ); + } +} + +/* + * threadCleanupWIN32 () + */ +static void threadCleanupWIN32 ( void ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + fprintf ( stderr, "threadCleanupWIN32: unable to find ctx\n" ); + return; + } + + while ( ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ) ) ) { + epicsParmCleanupWIN32 ( pParm ); + } + + TlsFree ( pGbl->tlsIndexThreadLibraryEPICS ); + + DeleteCriticalSection ( & pGbl->mutex ); +} + +/* + * epicsThreadExitMain () + */ +epicsShareFunc void epicsShareAPI epicsThreadExitMain ( void ) +{ + _endthread (); +} + +/* + * osdPriorityMagFromPriorityOSI () + */ +static unsigned osdPriorityMagFromPriorityOSI ( unsigned osiPriority, unsigned priorityStateCount ) +{ + unsigned magnitude; + + /* optimizer will remove this one if epicsThreadPriorityMin is zero */ + /* and osiPriority is unsigned */ + if ( osiPriority < epicsThreadPriorityMin ) { + osiPriority = epicsThreadPriorityMin; + } + + if ( osiPriority > epicsThreadPriorityMax ) { + osiPriority = epicsThreadPriorityMax; + } + + magnitude = osiPriority * priorityStateCount; + magnitude /= ( epicsThreadPriorityMax - epicsThreadPriorityMin ) + 1; + + return magnitude; +} + +/* + * epicsThreadGetOsdPriorityValue () + */ +static int epicsThreadGetOsdPriorityValue ( unsigned osiPriority ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + const int * pStateList; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + pStateList = osdRealtimePriorityList; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + pStateList = osdOrdinaryPriorityList; + } + + magnitude = osdPriorityMagFromPriorityOSI ( osiPriority, stateCount ); + return pStateList[magnitude]; +} + +/* + * osiPriorityMagFromMagnitueOSD () + */ +static unsigned osiPriorityMagFromMagnitueOSD ( unsigned magnitude, unsigned osdPriorityStateCount ) +{ + unsigned osiPriority; + + osiPriority = magnitude * ( epicsThreadPriorityMax - epicsThreadPriorityMin ); + osiPriority /= osdPriorityStateCount - 1u; + osiPriority += epicsThreadPriorityMin; + + return osiPriority; +} + + +/* + * epicsThreadGetOsiPriorityValue () + */ +static unsigned epicsThreadGetOsiPriorityValue ( int osdPriority ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + const int * pStateList; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + pStateList = osdRealtimePriorityList; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + pStateList = osdOrdinaryPriorityList; + } + + for ( magnitude = 0u; magnitude < stateCount; magnitude++ ) { + if ( osdPriority == pStateList[magnitude] ) { + break; + } + } + + if ( magnitude >= stateCount ) { + fprintf ( stderr, + "Unrecognized WIN32 thread priority level %d.\n", + osdPriority ); + fprintf ( stderr, + "Mapping to EPICS thread priority level epicsThreadPriorityMin.\n" ); + return epicsThreadPriorityMin; + } + + return osiPriorityMagFromMagnitueOSD ( magnitude, stateCount ); +} + +/* + * epicsThreadLowestPriorityLevelAbove () + */ +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove + ( unsigned int priority, unsigned * pPriorityJustAbove ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + epicsThreadBooleanStatus status; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + } + + magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount ); + + if ( magnitude < ( stateCount - 1 ) ) { + *pPriorityJustAbove = osiPriorityMagFromMagnitueOSD ( magnitude + 1u, stateCount ); + status = epicsThreadBooleanStatusSuccess; + } + else { + status = epicsThreadBooleanStatusFail; + } + return status; +} + +/* + * epicsThreadHighestPriorityLevelBelow () + */ +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow + ( unsigned int priority, unsigned * pPriorityJustBelow ) +{ + const DWORD priorityClass = GetPriorityClass ( GetCurrentProcess () ); + epicsThreadBooleanStatus status; + unsigned stateCount; + unsigned magnitude; + + if ( priorityClass == REALTIME_PRIORITY_CLASS ) { + stateCount = osdRealtimePriorityStateCount; + } + else { + stateCount = osdOrdinaryPriorityStateCount; + } + + magnitude = osdPriorityMagFromPriorityOSI ( priority, stateCount ); + + if ( magnitude > 0u ) { + *pPriorityJustBelow = osiPriorityMagFromMagnitueOSD ( magnitude - 1u, stateCount ); + status = epicsThreadBooleanStatusSuccess; + } + else { + status = epicsThreadBooleanStatusFail; + } + return status; +} + +/* + * epicsThreadGetStackSize () + */ +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize ( epicsThreadStackSizeClass stackSizeClass ) +{ + static const unsigned stackSizeTable[epicsThreadStackBig+1] = {4000, 6000, 11000}; + + if (stackSizeClassepicsThreadStackBig) { + fprintf ( stderr, + "epicsThreadGetStackSize illegal argument (too large)"); + return stackSizeTable[epicsThreadStackBig]; + } + + return stackSizeTable[stackSizeClass]; +} + +void epicsThreadCleanupWIN32 () +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + fprintf ( stderr, "epicsThreadCleanupWIN32: unable to find ctx\n" ); + return; + } + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + epicsParmCleanupWIN32 ( pParm ); +} + +/* + * epicsWin32ThreadEntry() + */ +static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm = ( win32ThreadParam * ) lpParameter; + unsigned retStat = 0u; + BOOL success; + + if ( pGbl ) { + setThreadName ( pParm->id, pParm->pName ); + + success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); + if ( success ) { + /* printf ( "starting thread %d\n", pParm->id ); */ + ( *pParm->funptr ) ( pParm->parm ); + /* printf ( "terminating thread %d\n", pParm->id ); */ + retStat = 1; + } + else { + fprintf ( stderr, "epicsWin32ThreadEntry: unable to set private\n" ); + } + } + else { + fprintf ( stderr, "epicsWin32ThreadEntry: unable to find ctx\n" ); + } + + epicsExitCallAtThreadExits (); + + /* + * CAUTION: !!!! the thread id might continue to be used after this thread exits !!!! + */ + epicsParmCleanupWIN32 ( pParm ); + + return retStat; /* this indirectly closes the thread handle */ +} + +static win32ThreadParam * epicsThreadParmCreate ( const char *pName ) +{ + win32ThreadParam *pParmWIN32; + + pParmWIN32 = calloc ( 1, sizeof ( *pParmWIN32 ) + strlen ( pName ) + 1 ); + if ( pParmWIN32 ) { + if ( pName ) { + pParmWIN32->pName = (char *) ( pParmWIN32 + 1 ); + strcpy ( pParmWIN32->pName, pName ); + } + else { + pParmWIN32->pName = 0; + } + pParmWIN32->isSuspended = 0; + } + return pParmWIN32; +} + +static win32ThreadParam * epicsThreadImplicitCreate ( void ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + DWORD id = GetCurrentThreadId (); + win32ThreadParam * pParm; + char name[64]; + HANDLE handle; + BOOL success; + + if ( ! pGbl ) { + fprintf ( stderr, "epicsThreadImplicitCreate: unable to find ctx\n" ); + return 0; + } + + success = DuplicateHandle ( GetCurrentProcess (), GetCurrentThread (), + GetCurrentProcess (), & handle, 0, FALSE, DUPLICATE_SAME_ACCESS ); + if ( ! success ) { + return 0; + } + { + unsigned long idForFormat = id; + sprintf ( name, "win%lx", idForFormat ); + } + pParm = epicsThreadParmCreate ( name ); + if ( pParm ) { + int win32ThreadPriority; + + pParm->handle = handle; + pParm->id = id; + win32ThreadPriority = GetThreadPriority ( pParm->handle ); + assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN ); + pParm->epicsPriority = epicsThreadGetOsiPriorityValue ( win32ThreadPriority ); + success = TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, pParm ); + if ( ! success ) { + epicsParmCleanupWIN32 ( pParm ); + pParm = 0; + } + else { + EnterCriticalSection ( & pGbl->mutex ); + ellAdd ( & pGbl->threadList, & pParm->node ); + LeaveCriticalSection ( & pGbl->mutex ); + } + } + return pParm; +} + +/* + * epicsThreadCreate () + */ +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate (const char *pName, + unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC pFunc,void *pParm) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParmWIN32; + int osdPriority; + DWORD wstat; + BOOL bstat; + + if ( ! pGbl ) { + return NULL; + } + + pParmWIN32 = epicsThreadParmCreate ( pName ); + if ( pParmWIN32 == 0 ) { + return ( epicsThreadId ) pParmWIN32; + } + pParmWIN32->funptr = pFunc; + pParmWIN32->parm = pParm; + pParmWIN32->epicsPriority = priority; + + { + unsigned threadId; + pParmWIN32->handle = (HANDLE) _beginthreadex ( + 0, stackSize, epicsWin32ThreadEntry, + pParmWIN32, + CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, + & threadId ); + if ( pParmWIN32->handle == 0 ) { + free ( pParmWIN32 ); + return NULL; + } + /* weird win32 interface threadId parameter inconsistency */ + pParmWIN32->id = ( DWORD ) threadId ; + } + + osdPriority = epicsThreadGetOsdPriorityValue (priority); + bstat = SetThreadPriority ( pParmWIN32->handle, osdPriority ); + if (!bstat) { + CloseHandle ( pParmWIN32->handle ); + free ( pParmWIN32 ); + return NULL; + } + + wstat = ResumeThread ( pParmWIN32->handle ); + if (wstat==0xFFFFFFFF) { + CloseHandle ( pParmWIN32->handle ); + free ( pParmWIN32 ); + return NULL; + } + + EnterCriticalSection ( & pGbl->mutex ); + ellAdd ( & pGbl->threadList, & pParmWIN32->node ); + LeaveCriticalSection ( & pGbl->mutex ); + + return ( epicsThreadId ) pParmWIN32; +} + +/* + * epicsThreadSuspendSelf () + */ +epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf () +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + DWORD stat; + + assert ( pGbl ); + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + } + if ( pParm ) { + EnterCriticalSection ( & pGbl->mutex ); + pParm->isSuspended = 1; + LeaveCriticalSection ( & pGbl->mutex ); + } + stat = SuspendThread ( GetCurrentThread () ); + assert ( stat != 0xFFFFFFFF ); +} + +/* + * epicsThreadResume () + */ +epicsShareFunc void epicsShareAPI epicsThreadResume ( epicsThreadId id ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + DWORD stat; + + assert ( pGbl ); + + EnterCriticalSection ( & pGbl->mutex ); + + stat = ResumeThread ( pParm->handle ); + pParm->isSuspended = 0; + + LeaveCriticalSection ( & pGbl->mutex ); + + assert ( stat != 0xFFFFFFFF ); +} + +/* + * epicsThreadGetPriority () + */ +epicsShareFunc unsigned epicsShareAPI epicsThreadGetPriority (epicsThreadId id) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + return pParm->epicsPriority; +} + +/* + * epicsThreadGetPrioritySelf () + */ +epicsShareFunc unsigned epicsShareAPI epicsThreadGetPrioritySelf () +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + assert ( pGbl ); + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + } + if ( pParm ) { + return pParm->epicsPriority; + } + else { + int win32ThreadPriority = + GetThreadPriority ( GetCurrentThread () ); + assert ( win32ThreadPriority != THREAD_PRIORITY_ERROR_RETURN ); + return epicsThreadGetOsiPriorityValue ( win32ThreadPriority ); + } +} + +/* + * epicsThreadSetPriority () + */ +epicsShareFunc void epicsShareAPI epicsThreadSetPriority ( epicsThreadId id, unsigned priority ) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + BOOL stat = SetThreadPriority ( pParm->handle, epicsThreadGetOsdPriorityValue (priority) ); + assert (stat); +} + +/* + * epicsThreadIsEqual () + */ +epicsShareFunc int epicsShareAPI epicsThreadIsEqual ( epicsThreadId id1, epicsThreadId id2 ) +{ + win32ThreadParam * pParm1 = ( win32ThreadParam * ) id1; + win32ThreadParam * pParm2 = ( win32ThreadParam * ) id2; + return ( id1 == id2 && pParm1->id == pParm2->id ); +} + +/* + * epicsThreadIsSuspended () + */ +epicsShareFunc int epicsShareAPI epicsThreadIsSuspended ( epicsThreadId id ) +{ + win32ThreadParam *pParm = ( win32ThreadParam * ) id; + DWORD exitCode; + BOOL stat; + + stat = GetExitCodeThread ( pParm->handle, & exitCode ); + if ( stat ) { + if ( exitCode != STILL_ACTIVE ) { + return 1; + } + else { + return pParm->isSuspended; + } + } + else { + return 1; + } +} + +/* + * epicsThreadSleep () + */ +epicsShareFunc void epicsShareAPI epicsThreadSleep ( double seconds ) +{ + static const unsigned mSecPerSec = 1000; + DWORD milliSecDelay; + + if ( seconds <= 0.0 ) { + milliSecDelay = 0u; + } + else if ( seconds >= INFINITE / mSecPerSec ) { + milliSecDelay = INFINITE - 1; + } + else { + milliSecDelay = ( DWORD ) ( ( seconds * mSecPerSec ) + 0.5 ); + if ( milliSecDelay == 0 ) { + milliSecDelay = 1; + } + } + Sleep ( milliSecDelay ); +} + +/* + * epicsThreadSleepQuantum () + */ +double epicsShareAPI epicsThreadSleepQuantum () +{ + /* + * Its worth noting here that the sleep quantum on windows can + * mysteriously get better. I eventually tracked this down to + * codes that call timeBeginPeriod(1). Calling timeBeginPeriod() + * specifying a better timer resolution also increases the interrupt + * load. This appears to be related to java applet activity. + * The function timeGetDevCaps can tell us the range of periods + * that can be specified to timeBeginPeriod, but alas there + * appears to be no way to find out what the value of the global + * minimum of all timeBeginPeriod calls for all processes is. + */ + static const double secPerTick = 100e-9; + DWORD adjustment; + DWORD delay; + BOOL disabled; + BOOL success; + + success = GetSystemTimeAdjustment ( + & adjustment, & delay, & disabled ); + if ( success ) { + return delay * secPerTick; + } + else { + return 0.0; + } +} + +/* + * epicsThreadGetIdSelf () + */ +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf (void) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + assert ( pGbl ); + + pParm = ( win32ThreadParam * ) TlsGetValue ( + pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + assert ( pParm ); /* very dangerous to allow non-unique thread id into use */ + } + return ( epicsThreadId ) pParm; +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId ( const char * pName ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + return 0; + } + + EnterCriticalSection ( & pGbl->mutex ); + + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); + pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { + if ( pParm->pName ) { + if ( strcmp ( pParm->pName, pName ) == 0 ) { + break; + } + } + } + + LeaveCriticalSection ( & pGbl->mutex ); + + /* !!!! warning - the thread parm could vanish at any time !!!! */ + + return ( epicsThreadId ) pParm; +} + + +/* + * epicsThreadGetNameSelf () + */ +epicsShareFunc const char * epicsShareAPI epicsThreadGetNameSelf (void) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + return "thread library not initialized"; + } + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + if ( ! pParm ) { + pParm = epicsThreadImplicitCreate (); + } + + if ( pParm ) { + if ( pParm->pName ) { + return pParm->pName; + } + } + return "anonymous"; +} + +/* + * epicsThreadGetName () + */ +epicsShareFunc void epicsShareAPI epicsThreadGetName ( + epicsThreadId id, char * pName, size_t size ) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + + if ( size ) { + size_t sizeMinusOne = size-1; + strncpy ( pName, pParm->pName, sizeMinusOne ); + pName [sizeMinusOne] = '\0'; + } +} + +/* + * epics_GetThreadPriorityAsString () + */ +static const char * epics_GetThreadPriorityAsString ( HANDLE thr ) +{ + const char * pPriName = "?????"; + switch ( GetThreadPriority ( thr ) ) { + case THREAD_PRIORITY_TIME_CRITICAL: + pPriName = "tm-crit"; + break; + case THREAD_PRIORITY_HIGHEST: + pPriName = "high"; + break; + case THREAD_PRIORITY_ABOVE_NORMAL: + pPriName = "normal+"; + break; + case THREAD_PRIORITY_NORMAL: + pPriName = "normal"; + break; + case THREAD_PRIORITY_BELOW_NORMAL: + pPriName = "normal-"; + break; + case THREAD_PRIORITY_LOWEST: + pPriName = "low"; + break; + case THREAD_PRIORITY_IDLE: + pPriName = "idle"; + break; + } + return pPriName; +} + +/* + * epicsThreadShowPrivate () + */ +static void epicsThreadShowPrivate ( epicsThreadId id, unsigned level ) +{ + win32ThreadParam * pParm = ( win32ThreadParam * ) id; + + if ( pParm ) { + unsigned long idForFormat = pParm->id; + fprintf ( epicsGetStdout(), "%-15s %-8p %-8lx %-9u %-9s %-7s", pParm->pName, + (void *) pParm, idForFormat, pParm->epicsPriority, + epics_GetThreadPriorityAsString ( pParm->handle ), + epicsThreadIsSuspended ( id ) ? "suspend" : "ok" ); + } + else { + fprintf (epicsGetStdout(), + "NAME EPICS-ID WIN32-ID EPICS-PRI WIN32-PRI STATE " ); + } + fprintf (epicsGetStdout(),"\n" ); +} + +/* + * epicsThreadShowAll () + */ +epicsShareFunc void epicsShareAPI epicsThreadShowAll ( unsigned level ) +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + if ( ! pGbl ) { + return; + } + + EnterCriticalSection ( & pGbl->mutex ); + + epicsThreadShowPrivate ( 0, level ); + for ( pParm = ( win32ThreadParam * ) ellFirst ( & pGbl->threadList ); + pParm; pParm = ( win32ThreadParam * ) ellNext ( & pParm->node ) ) { + epicsThreadShowPrivate ( ( epicsThreadId ) pParm, level ); + } + + LeaveCriticalSection ( & pGbl->mutex ); +} + +/* + * epicsThreadShow () + */ +epicsShareFunc void epicsShareAPI epicsThreadShow ( epicsThreadId id, unsigned level ) +{ + epicsThreadShowPrivate ( 0, level ); + epicsThreadShowPrivate ( id, level ); +} + +/* + * epicsThreadOnce () + */ +epicsShareFunc void epicsShareAPI epicsThreadOnce ( + epicsThreadOnceId *id, void (*func)(void *), void *arg ) +{ + static struct epicsThreadOSD threadOnceComplete; + #define EPICS_THREAD_ONCE_DONE & threadOnceComplete + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + + assert ( pGbl ); + + EnterCriticalSection ( & pGbl->mutex ); + + if ( *id != EPICS_THREAD_ONCE_DONE ) { + if ( *id == EPICS_THREAD_ONCE_INIT ) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + LeaveCriticalSection ( & pGbl->mutex ); + func ( arg ); + EnterCriticalSection ( & pGbl->mutex ); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if ( *id == epicsThreadGetIdSelf() ) { + LeaveCriticalSection ( & pGbl->mutex ); + cantProceed( "Recursive epicsThreadOnce() initialization\n" ); + } else + while ( *id != EPICS_THREAD_ONCE_DONE ) { + /* Another thread is in the above func(arg) call. */ + LeaveCriticalSection ( & pGbl->mutex ); + epicsThreadSleep ( epicsThreadSleepQuantum() ); + EnterCriticalSection ( & pGbl->mutex ); + } + } + LeaveCriticalSection ( & pGbl->mutex ); +} + +/* + * epicsThreadPrivateCreate () + */ +epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate () +{ + epicsThreadPrivateOSD *p = ( epicsThreadPrivateOSD * ) malloc ( sizeof ( *p ) ); + if ( p ) { + p->key = TlsAlloc (); + if ( p->key == 0xFFFFFFFF ) { + free ( p ); + p = 0; + } + } + return p; +} + +/* + * epicsThreadPrivateDelete () + */ +epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete ( epicsThreadPrivateId p ) +{ + BOOL stat = TlsFree ( p->key ); + assert ( stat ); + free ( p ); +} + +/* + * epicsThreadPrivateSet () + */ +epicsShareFunc void epicsShareAPI epicsThreadPrivateSet ( epicsThreadPrivateId pPvt, void *pVal ) +{ + BOOL stat = TlsSetValue ( pPvt->key, (void *) pVal ); + assert (stat); +} + +/* + * epicsThreadPrivateGet () + */ +epicsShareFunc void * epicsShareAPI epicsThreadPrivateGet ( epicsThreadPrivateId pPvt ) +{ + return ( void * ) TlsGetValue ( pPvt->key ); +} + +#ifdef TEST_CODES +void testPriorityMapping () +{ + unsigned i; + + for (i=epicsThreadPriorityMin; i +#include +#include +#include + +// +// WIN32 +// +#define VC_EXTRALEAN +#define STRICT +#include + +// +// EPICS +// +#define epicsExportSharedSymbols +#include "epicsTime.h" +#include "generalTimeSup.h" +#include "epicsTimer.h" +#include "errlog.h" +#include "epicsAssert.h" +#include "epicsThread.h" + +#if defined ( DEBUG ) +# define debugPrintf(argsInParen) ::printf argsInParen +#else +# define debugPrintf(argsInParen) +#endif + +static int osdTimeGetCurrent ( epicsTimeStamp *pDest ); + +// GNU seems to require that 64 bit constants have LL on +// them. The borland compiler fails to compile constants +// with the LL suffix. MS compiler doesnt care. +#ifdef __GNUC__ +#define LL_CONSTANT(VAL) VAL ## LL +#else +#define LL_CONSTANT(VAL) VAL +#endif + +// for mingw +#if !defined ( MAXLONGLONG ) +#define MAXLONGLONG LL_CONSTANT(0x7fffffffffffffff) +#endif + +static const LONGLONG epicsEpochInFileTime = LL_CONSTANT(0x01b41e2a18d64000); + +class currentTime : public epicsTimerNotify { +public: + currentTime (); + ~currentTime (); + void getCurrentTime ( epicsTimeStamp & dest ); + void startPLL (); +private: + CRITICAL_SECTION mutex; + LONGLONG lastPerfCounter; + LONGLONG perfCounterFreq; + LONGLONG epicsTimeLast; // nano-sec since the EPICS epoch + LONGLONG perfCounterFreqPLL; + LONGLONG lastPerfCounterPLL; + LONGLONG lastFileTimePLL; + epicsTimerQueueActive * pTimerQueue; + epicsTimer * pTimer; + bool perfCtrPresent; + + epicsTimerNotify::expireStatus expire ( const epicsTime & ); +}; + +static currentTime * pCurrentTime = 0; +static const LONGLONG FILE_TIME_TICKS_PER_SEC = 10000000; +static const LONGLONG EPICS_TIME_TICKS_PER_SEC = 1000000000; +static const LONGLONG ET_TICKS_PER_FT_TICK = + EPICS_TIME_TICKS_PER_SEC / FILE_TIME_TICKS_PER_SEC; + +// +// Start and register time provider +// +static int timeRegister(void) +{ + pCurrentTime = new currentTime (); + + generalTimeCurrentTpRegister("PerfCounter", 150, osdTimeGetCurrent); + + pCurrentTime->startPLL (); + return 1; +} +static int done = timeRegister(); + +// +// osdTimeGetCurrent () +// +static int osdTimeGetCurrent ( epicsTimeStamp *pDest ) +{ + assert ( pCurrentTime ); + + pCurrentTime->getCurrentTime ( *pDest ); + return epicsTimeOK; +} + +inline void UnixTimeToFileTime ( const time_t * pAnsiTime, LPFILETIME pft ) +{ + LONGLONG ll = Int32x32To64 ( *pAnsiTime, 10000000 ) + LL_CONSTANT(116444736000000000); + pft->dwLowDateTime = static_cast < DWORD > ( ll ); + pft->dwHighDateTime = static_cast < DWORD > ( ll >>32 ); +} + +static int daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, + 31, 30, 31, 30, 31 }; + +static bool isLeapYear ( DWORD year ) +{ + if ( (year % 4) == 0 ) { + return ( ( year % 100 ) != 0 || ( year % 400 ) == 0 ); + } else { + return false; + } +} + +static int dayOfYear ( DWORD day, DWORD month, DWORD year ) +{ + DWORD nDays = 0; + for ( unsigned m = 1; m < month; m++ ) { + nDays += daysInMonth[m-1]; + if ( m == 2 && isLeapYear(year) ) { + nDays++; + } + } + return nDays + day; +} + +// synthesize a reentrant gmtime on WIN32 +int epicsShareAPI epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) +{ + FILETIME ft; + UnixTimeToFileTime ( pAnsiTime, &ft ); + + SYSTEMTIME st; + BOOL status = FileTimeToSystemTime ( &ft, &st ); + if ( ! status ) { + return epicsTimeERROR; + } + + pTM->tm_sec = st.wSecond; // seconds after the minute - [0,59] + pTM->tm_min = st.wMinute; // minutes after the hour - [0,59] + pTM->tm_hour = st.wHour; // hours since midnight - [0,23] + assert ( st.wDay >= 1 && st.wDay <= 31 ); + pTM->tm_mday = st.wDay; // day of the month - [1,31] + assert ( st.wMonth >= 1 && st.wMonth <= 12 ); + pTM->tm_mon = st.wMonth - 1; // months since January - [0,11] + assert ( st.wYear >= 1900 ); + pTM->tm_year = st.wYear - 1900; // years since 1900 + pTM->tm_wday = st.wDayOfWeek; // days since Sunday - [0,6] + pTM->tm_yday = dayOfYear ( st.wDay, st.wMonth, st.wYear ) - 1; + pTM->tm_isdst = 0; + + return epicsTimeOK; +} + +// synthesize a reentrant localtime on WIN32 +int epicsShareAPI epicsTime_localtime ( + const time_t * pAnsiTime, struct tm * pTM ) +{ + FILETIME ft; + UnixTimeToFileTime ( pAnsiTime, & ft ); + + TIME_ZONE_INFORMATION tzInfo; + DWORD tzStatus = GetTimeZoneInformation ( & tzInfo ); + if ( tzStatus == TIME_ZONE_ID_INVALID ) { + return epicsTimeERROR; + } + + // + // There are remarkable weaknessess in the FileTimeToLocalFileTime + // interface so we dont use it here. Unfortunately, there is no + // corresponding function that works on file time. + // + SYSTEMTIME st; + BOOL success = FileTimeToSystemTime ( & ft, & st ); + if ( ! success ) { + return epicsTimeERROR; + } + SYSTEMTIME lst; + success = SystemTimeToTzSpecificLocalTime ( + & tzInfo, & st, & lst ); + if ( ! success ) { + return epicsTimeERROR; + } + + // + // We must convert back to file time so that we can determine if DST + // is active... + // + FILETIME lft; + success = SystemTimeToFileTime ( & lst, & lft ); + if ( ! success ) { + return epicsTimeERROR; + } + + int is_dst = -1; // unknown state of dst + if ( tzStatus != TIME_ZONE_ID_UNKNOWN && + tzInfo.StandardDate.wMonth != 0 && + tzInfo.DaylightDate.wMonth != 0) { + // determine if the specified date is + // in daylight savings time + tzInfo.StandardDate.wYear = st.wYear; + FILETIME StandardDateFT; + success = SystemTimeToFileTime ( + & tzInfo.StandardDate, & StandardDateFT ); + if ( ! success ) { + return epicsTimeERROR; + } + tzInfo.DaylightDate.wYear = st.wYear; + FILETIME DaylightDateFT; + success = SystemTimeToFileTime ( + & tzInfo.DaylightDate, & DaylightDateFT ); + if ( ! success ) { + return epicsTimeERROR; + } + if ( CompareFileTime ( & lft, & DaylightDateFT ) >= 0 + && CompareFileTime ( & lft, & StandardDateFT ) < 0 ) { + is_dst = 1; + } + else { + is_dst = 0; + } + } + + pTM->tm_sec = lst.wSecond; // seconds after the minute - [0,59] + pTM->tm_min = lst.wMinute; // minutes after the hour - [0,59] + pTM->tm_hour = lst.wHour; // hours since midnight - [0,23] + assert ( lst.wDay >= 1 && lst.wDay <= 31 ); + pTM->tm_mday = lst.wDay; // day of the month - [1,31] + assert ( lst.wMonth >= 1 && lst.wMonth <= 12 ); + pTM->tm_mon = lst.wMonth - 1; // months since January - [0,11] + assert ( lst.wYear >= 1900 ); + pTM->tm_year = lst.wYear - 1900; // years since 1900 + pTM->tm_wday = lst.wDayOfWeek; // days since Sunday - [0,6] + pTM->tm_yday = dayOfYear ( lst.wDay, lst.wMonth, lst.wYear ) - 1; + pTM->tm_isdst = is_dst; + + return epicsTimeOK; +} + +currentTime::currentTime () : + lastPerfCounter ( 0 ), + perfCounterFreq ( 0 ), + epicsTimeLast ( 0 ), + perfCounterFreqPLL ( 0 ), + lastPerfCounterPLL ( 0 ), + lastFileTimePLL ( 0 ), + pTimerQueue ( 0 ), + pTimer ( 0 ), + perfCtrPresent ( false ) +{ + InitializeCriticalSection ( & this->mutex ); + + // avoid interruptions by briefly becoming a time critical thread + int originalPriority = GetThreadPriority ( GetCurrentThread () ); + SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL ); + + FILETIME ft; + GetSystemTimeAsFileTime ( & ft ); + LARGE_INTEGER tmp; + QueryPerformanceCounter ( & tmp ); + this->lastPerfCounter = tmp.QuadPart; + // if no high resolution counters then default to low res file time + if ( QueryPerformanceFrequency ( & tmp ) ) { + this->perfCounterFreq = tmp.QuadPart; + this->perfCtrPresent = true; + } + SetThreadPriority ( GetCurrentThread (), originalPriority ); + + LARGE_INTEGER liFileTime; + liFileTime.LowPart = ft.dwLowDateTime; + liFileTime.HighPart = ft.dwHighDateTime; + + if ( liFileTime.QuadPart >= epicsEpochInFileTime ) { + // the windows file time has a maximum resolution of 100 nS + // and a nominal resolution of 10 mS - 16 mS + this->epicsTimeLast = + ( liFileTime.QuadPart - epicsEpochInFileTime ) * + ET_TICKS_PER_FT_TICK; + } + else { + errlogPrintf ( + "win32 osdTime.cpp detected questionable " + "system date prior to EPICS epoch\n" ); + this->epicsTimeLast = 0; + } + + this->perfCounterFreqPLL = this->perfCounterFreq; + this->lastPerfCounterPLL = this->lastPerfCounter; + this->lastFileTimePLL = liFileTime.QuadPart; +} + +currentTime::~currentTime () +{ + DeleteCriticalSection ( & this->mutex ); + if ( this->pTimer ) { + this->pTimer->destroy (); + } + if ( this->pTimerQueue ) { + this->pTimerQueue->release (); + } +} + +void currentTime::getCurrentTime ( epicsTimeStamp & dest ) +{ + if ( this->perfCtrPresent ) { + EnterCriticalSection ( & this->mutex ); + + LARGE_INTEGER curPerfCounter; + QueryPerformanceCounter ( & curPerfCounter ); + + LONGLONG offset; + if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) { + offset = curPerfCounter.QuadPart - this->lastPerfCounter; + } + else { + // + // must have been a timer roll-over event + // + // It takes 9.223372036855e+18/perf_freq sec to roll over this + // counter. This is currently about 245118 years using the perf + // counter freq value on my system (1193182). Nevertheless, I + // have code for this situation because the performance + // counter resolution will more than likely improve over time. + // + offset = ( MAXLONGLONG - this->lastPerfCounter ) + + ( curPerfCounter.QuadPart + MAXLONGLONG ); + } + if ( offset < MAXLONGLONG / EPICS_TIME_TICKS_PER_SEC ) { + offset *= EPICS_TIME_TICKS_PER_SEC; + offset /= this->perfCounterFreq; + } + else { + double fpOffset = static_cast < double > ( offset ); + fpOffset *= EPICS_TIME_TICKS_PER_SEC; + fpOffset /= static_cast < double > ( this->perfCounterFreq ); + offset = static_cast < LONGLONG > ( fpOffset ); + } + LONGLONG epicsTimeCurrent = this->epicsTimeLast + offset; + if ( this->epicsTimeLast > epicsTimeCurrent ) { + double diff = static_cast < double > + ( this->epicsTimeLast - epicsTimeCurrent ); + errlogPrintf ( + "currentTime::getCurrentTime(): %f sec " + "time discontinuity detected\n", + diff ); + } + this->epicsTimeLast = epicsTimeCurrent; + this->lastPerfCounter = curPerfCounter.QuadPart; + + LeaveCriticalSection ( & this->mutex ); + + dest.secPastEpoch = static_cast < epicsUInt32 > + ( epicsTimeCurrent / EPICS_TIME_TICKS_PER_SEC ); + dest.nsec = static_cast < epicsUInt32 > + ( epicsTimeCurrent % EPICS_TIME_TICKS_PER_SEC ); + } + else { + // if high resolution performance counters are not supported then + // fall back to low res file time + FILETIME ft; + GetSystemTimeAsFileTime ( & ft ); + dest = epicsTime ( ft ); + } +} + +// +// Maintain corrected version of the performance counter's frequency using +// a phase locked loop. This approach is similar to NTP's. +// +epicsTimerNotify::expireStatus currentTime::expire ( const epicsTime & ) +{ + // avoid interruptions by briefly becoming a time critical thread + LARGE_INTEGER curFileTime; + LARGE_INTEGER curPerfCounter; + { + int originalPriority = GetThreadPriority ( GetCurrentThread () ); + SetThreadPriority ( GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL ); + FILETIME ft; + GetSystemTimeAsFileTime ( & ft ); + QueryPerformanceCounter ( & curPerfCounter ); + SetThreadPriority ( GetCurrentThread (), originalPriority ); + + curFileTime.LowPart = ft.dwLowDateTime; + curFileTime.HighPart = ft.dwHighDateTime; + } + + EnterCriticalSection ( & this->mutex ); + + LONGLONG perfCounterDiff = curPerfCounter.QuadPart - this->lastPerfCounterPLL; + if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) { + perfCounterDiff = curPerfCounter.QuadPart - this->lastPerfCounterPLL; + } + else { + // + // must have been a timer roll-over event + // + // It takes 9.223372036855e+18/perf_freq sec to roll over this + // counter. This is currently about 245118 years using the perf + // counter freq value on my system (1193182). Nevertheless, I + // have code for this situation because the performance + // counter resolution will more than likely improve over time. + // + perfCounterDiff = ( MAXLONGLONG - this->lastPerfCounterPLL ) + + ( curPerfCounter.QuadPart + MAXLONGLONG ); + } + this->lastPerfCounterPLL = curPerfCounter.QuadPart; + + LONGLONG fileTimeDiff = curFileTime.QuadPart - this->lastFileTimePLL; + this->lastFileTimePLL = curFileTime.QuadPart; + + // discard glitches + if ( fileTimeDiff == 0 ) { + LeaveCriticalSection( & this->mutex ); + debugPrintf ( ( "currentTime: file time difference in PLL was zero\n" ) ); + return expireStatus ( restart, 1.0 /* sec */ ); + } + + LONGLONG freq = ( FILE_TIME_TICKS_PER_SEC * perfCounterDiff ) / fileTimeDiff; + LONGLONG delta = freq - this->perfCounterFreqPLL; + + // discard glitches + LONGLONG bound = this->perfCounterFreqPLL >> 10; + if ( delta < -bound || delta > bound ) { + LeaveCriticalSection( & this->mutex ); + debugPrintf ( ( "freq est out of bounds l=%d e=%d h=%d\n", + static_cast < int > ( -bound ), + static_cast < int > ( delta ), + static_cast < int > ( bound ) ) ); + return expireStatus ( restart, 1.0 /* sec */ ); + } + + // update feedback loop estimating the performance counter's frequency + LONGLONG feedback = delta >> 8; + this->perfCounterFreqPLL += feedback; + + LONGLONG perfCounterDiffSinceLastFetch; + if ( curPerfCounter.QuadPart >= this->lastPerfCounter ) { + perfCounterDiffSinceLastFetch = + curPerfCounter.QuadPart - this->lastPerfCounter; + } + else { + // + // must have been a timer roll-over event + // + // It takes 9.223372036855e+18/perf_freq sec to roll over this + // counter. This is currently about 245118 years using the perf + // counter freq value on my system (1193182). Nevertheless, I + // have code for this situation because the performance + // counter resolution will more than likely improve over time. + // + perfCounterDiffSinceLastFetch = + ( MAXLONGLONG - this->lastPerfCounter ) + + ( curPerfCounter.QuadPart + MAXLONGLONG ); + } + + // Update the current estimated time. + this->epicsTimeLast += + ( perfCounterDiffSinceLastFetch * EPICS_TIME_TICKS_PER_SEC ) + / this->perfCounterFreq; + this->lastPerfCounter = curPerfCounter.QuadPart; + + LONGLONG epicsTimeFromCurrentFileTime = + ( curFileTime.QuadPart - epicsEpochInFileTime ) * + ET_TICKS_PER_FT_TICK; + + delta = epicsTimeFromCurrentFileTime - this->epicsTimeLast; + if ( delta > EPICS_TIME_TICKS_PER_SEC || delta < -EPICS_TIME_TICKS_PER_SEC ) { + // When there is an abrupt shift in the current computed time vs + // the time derived from the current file time then someone has + // probabably adjusted the real time clock and the best reaction + // is to just assume the new time base + this->epicsTimeLast = epicsTimeFromCurrentFileTime; + this->perfCounterFreq = this->perfCounterFreqPLL; + debugPrintf ( ( "currentTime: did someone adjust the date?\n" ) ); + } + else { + // update the effective performance counter frequency that will bring + // our calculated time base in syncy with file time one second from now. + this->perfCounterFreq = + ( EPICS_TIME_TICKS_PER_SEC * this->perfCounterFreqPLL ) + / ( delta + EPICS_TIME_TICKS_PER_SEC ); + + // limit effective performance counter frequency rate of change + LONGLONG lowLimit = this->perfCounterFreqPLL - bound; + if ( this->perfCounterFreq < lowLimit ) { + debugPrintf ( ( "currentTime: out of bounds low freq excursion %d\n", + static_cast ( lowLimit - this->perfCounterFreq ) ) ); + this->perfCounterFreq = lowLimit; + } + else { + LONGLONG highLimit = this->perfCounterFreqPLL + bound; + if ( this->perfCounterFreq > highLimit ) { + debugPrintf ( ( "currentTime: out of bounds high freq excursion %d\n", + static_cast ( this->perfCounterFreq - highLimit ) ) ); + this->perfCounterFreq = highLimit; + } + } + +# if defined ( DEBUG ) + LARGE_INTEGER sysFreq; + QueryPerformanceFrequency ( & sysFreq ); + double freqDiff = static_cast + ( this->perfCounterFreq - sysFreq.QuadPart ); + freqDiff /= sysFreq.QuadPart; + freqDiff *= 100.0; + double freqEstDiff = static_cast + ( this->perfCounterFreqPLL - sysFreq.QuadPart ); + freqEstDiff /= sysFreq.QuadPart; + freqEstDiff *= 100.0; + debugPrintf ( ( "currentTime: freq delta %f %% freq est delta %f %% time delta %f sec\n", + freqDiff, freqEstDiff, static_cast < double > ( delta ) / EPICS_TIME_TICKS_PER_SEC ) ); +# endif + } + + LeaveCriticalSection ( & this->mutex ); + + return expireStatus ( restart, 1.0 /* sec */ ); +} + +void currentTime::startPLL () +{ + // create frequency estimation timer when needed + if ( this->perfCtrPresent && ! this->pTimerQueue ) { + this->pTimerQueue = & epicsTimerQueueActive::allocate ( true ); + this->pTimer = & this->pTimerQueue->createTimer (); + this->pTimer->start ( *this, 1.0 ); + } +} + +epicsTime::operator FILETIME () const +{ + LARGE_INTEGER ftTicks; + ftTicks.QuadPart = ( this->secPastEpoch * FILE_TIME_TICKS_PER_SEC ) + + ( this->nSec / ET_TICKS_PER_FT_TICK ); + ftTicks.QuadPart += epicsEpochInFileTime; + FILETIME ts; + ts.dwLowDateTime = ftTicks.LowPart; + ts.dwHighDateTime = ftTicks.HighPart; + return ts; +} + +epicsTime::epicsTime ( const FILETIME & ts ) +{ + LARGE_INTEGER lift; + lift.LowPart = ts.dwLowDateTime; + lift.HighPart = ts.dwHighDateTime; + if ( lift.QuadPart > epicsEpochInFileTime ) { + LONGLONG fileTimeTicksSinceEpochEPICS = + lift.QuadPart - epicsEpochInFileTime; + this->secPastEpoch = static_cast < epicsUInt32 > + ( fileTimeTicksSinceEpochEPICS / FILE_TIME_TICKS_PER_SEC ); + this->nSec = static_cast < epicsUInt32 > + ( ( fileTimeTicksSinceEpochEPICS % FILE_TIME_TICKS_PER_SEC ) * + ET_TICKS_PER_FT_TICK ); + } + else { + this->secPastEpoch = 0; + this->nSec = 0; + } +} + +epicsTime & epicsTime::operator = ( const FILETIME & rhs ) +{ + *this = epicsTime ( rhs ); + return *this; +} diff --git a/src/libCom/osi/os/WIN32/osdTime.h b/src/libCom/osi/os/WIN32/osdTime.h new file mode 100644 index 000000000..0d58c9a71 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdTime.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* 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: jba@aps.anl.gov-20101026214840-203j1gphhlgj61p0 + * + * Author: Jeff Hill + */ + +#ifndef osdTimeh +#define osdTimeh + +#if ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED) +struct timespec { + time_t tv_sec; /* seconds since some epoch */ + long tv_nsec; /* nanoseconds within the second */ +}; +#endif /* ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED) */ + +#endif /* ifndef osdTimeh */ + diff --git a/src/libCom/osi/os/WIN32/osdWireConfig.h b/src/libCom/osi/os/WIN32/osdWireConfig.h new file mode 100644 index 000000000..1715786e0 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdWireConfig.h @@ -0,0 +1,16 @@ +/* + * WIN32 version of + * osdWireConfig.h + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +/* for now, assume that win32 runs only on generic little endian */ +#define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER + +#endif /* ifdef osdWireConfig_h */ diff --git a/src/libCom/osi/os/WIN32/osiFileName.h b/src/libCom/osi/os/WIN32/osiFileName.h new file mode 100644 index 000000000..658975d89 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osiFileName.h @@ -0,0 +1,22 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#define OSI_PATH_LIST_SEPARATOR ";" +#define OSI_PATH_SEPARATOR "\\" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/WIN32/osiUnistd.h b/src/libCom/osi/os/WIN32/osiUnistd.h new file mode 100644 index 000000000..2fe1bb399 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osiUnistd.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include diff --git a/src/libCom/osi/os/WIN32/setThreadName.cpp b/src/libCom/osi/os/WIN32/setThreadName.cpp new file mode 100644 index 000000000..49663c8af --- /dev/null +++ b/src/libCom/osi/os/WIN32/setThreadName.cpp @@ -0,0 +1,51 @@ + +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#define VC_EXTRALEAN +#define STRICT +#if _WIN64 +# define _WIN32_WINNT 0x400 /* defining this drops support for W95 */ +#endif +#include + +/* + * this was copied directly from an example in visual c++ 7 documentation, + * It uses visual C++ specific keywords for exception handling, but is + * probably only useful when using the visual c++ or later debugger. + * + * Usage: setThreadName (-1, "MainThread"); + */ +extern "C" void setThreadName ( DWORD dwThreadID, LPCSTR szThreadName ) +{ +#if _MSC_VER >= 1300 && defined ( _DEBUG ) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero + } THREADNAME_INFO; + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( 0x406D1388, 0, + sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info ); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + { + } +#endif +} diff --git a/src/libCom/osi/os/WIN32/systemCallIntMech.cpp b/src/libCom/osi/os/WIN32/systemCallIntMech.cpp new file mode 100644 index 000000000..bcb0ba7a0 --- /dev/null +++ b/src/libCom/osi/os/WIN32/systemCallIntMech.cpp @@ -0,0 +1,23 @@ + +/*************************************************************************\ +* 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 + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" + +enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery () +{ + return esscimqi_socketCloseRequired; +} diff --git a/src/libCom/osi/os/cygwin32/devLibVMEOSD.c b/src/libCom/osi/os/cygwin32/devLibVMEOSD.c new file mode 100644 index 000000000..1f0bd2a69 --- /dev/null +++ b/src/libCom/osi/os/cygwin32/devLibVMEOSD.c @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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 */ + +#include + +#define epicsExportSharedSymbols +#include "devLibVME.h" + +epicsShareDef devLibVME *pdevLibVME = NULL; diff --git a/src/libCom/osi/os/cygwin32/osdSock.h b/src/libCom/osi/os/cygwin32/osdSock.h new file mode 100644 index 000000000..8d8326ce5 --- /dev/null +++ b/src/libCom/osi/os/cygwin32/osdSock.h @@ -0,0 +1,76 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * cygwin32 specific include + */ + + +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef int osiSocklen_t; +#define FD_IN_FDSET(FD) ((FD)=0) +#ifndef SHUT_RD +#define SHUT_RD 0 +#endif +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +#define SOCK_EWOULDBLOCK EWOULDBLOCK +#define SOCK_ENOBUFS ENOBUFS +#define SOCK_ECONNRESET ECONNRESET +#define SOCK_ETIMEDOUT ETIMEDOUT +#define SOCK_EADDRINUSE EADDRINUSE +#define SOCK_ECONNREFUSED ECONNREFUSED +#define SOCK_ECONNABORTED ECONNABORTED +#define SOCK_EINPROGRESS EINPROGRESS +#define SOCK_EISCONN EISCONN +#define SOCK_EALREADY EALREADY +#define SOCK_EINVAL EINVAL +#define SOCK_EINTR EINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE EMFILE +#define SOCK_SHUTDOWN ESHUTDOWN +#define SOCK_ENOTSOCK ENOTSOCK +#define SOCK_EBADF EBADF + +#define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) + +#endif /*osdSockH*/ + diff --git a/src/libCom/osi/os/cygwin32/osdSockAddrReuse.cpp b/src/libCom/osi/os/cygwin32/osdSockAddrReuse.cpp new file mode 100644 index 000000000..a2ac8cd1b --- /dev/null +++ b/src/libCom/osi/os/cygwin32/osdSockAddrReuse.cpp @@ -0,0 +1,45 @@ + +/*************************************************************************\ +* 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 + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +/* + * Note: WINSOCK appears to assign a different functionality for + * SO_REUSEADDR compared to other OS. With WINSOCK SO_REUSEADDR indicates + * that simultaneously servers can bind to the same TCP port on the same host! + * Also, servers are always enabled to reuse a port immediately after + * they exit ( even if SO_REUSEADDR isnt set ). + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ +} + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} diff --git a/src/libCom/osi/os/cygwin32/osdStrtod.h b/src/libCom/osi/os/cygwin32/osdStrtod.h new file mode 100644 index 000000000..28fd36b9d --- /dev/null +++ b/src/libCom/osi/os/cygwin32/osdStrtod.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +epicsShareFunc double epicsStrtod(const char *str, char **endp); diff --git a/src/libCom/osi/os/cygwin32/osiFileName.h b/src/libCom/osi/os/cygwin32/osiFileName.h new file mode 100644 index 000000000..658975d89 --- /dev/null +++ b/src/libCom/osi/os/cygwin32/osiFileName.h @@ -0,0 +1,22 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#define OSI_PATH_LIST_SEPARATOR ";" +#define OSI_PATH_SEPARATOR "\\" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/cygwin32/systemCallIntMech.cpp b/src/libCom/osi/os/cygwin32/systemCallIntMech.cpp new file mode 100644 index 000000000..1f5c3abed --- /dev/null +++ b/src/libCom/osi/os/cygwin32/systemCallIntMech.cpp @@ -0,0 +1,30 @@ + +/*************************************************************************\ +* 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-20101104192413-fxlfgfq0kuhsi23a */ +/* + * Author: Jeff Hill + */ + +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" + +enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery () +{ +#if (CYGWIN_VERSION_DLL_MAJOR >= 1007) + // Behaviour changed in Cygwin 1.7 release. + return esscimqi_socketCloseRequired; +#else + return esscimqi_socketBothShutdownRequired; +#endif +} diff --git a/src/libCom/osi/os/default/devLibVMEOSD.c b/src/libCom/osi/os/default/devLibVMEOSD.c new file mode 100644 index 000000000..a1c659f4e --- /dev/null +++ b/src/libCom/osi/os/default/devLibVMEOSD.c @@ -0,0 +1,17 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* 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 */ + +#include + +#include "devLibVME.h" + +/* This file must contain no definitions other than the following: */ + +devLibVME *pdevLibVME; diff --git a/src/libCom/osi/os/default/epicsGetopt.h b/src/libCom/osi/os/default/epicsGetopt.h new file mode 100644 index 000000000..4bcc962cb --- /dev/null +++ b/src/libCom/osi/os/default/epicsGetopt.h @@ -0,0 +1,17 @@ +/*************************************************************************\ +* 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. +* Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer +* Synchrotronstrahlung. +* 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. +\*************************************************************************/ +#ifndef _EPICS_GETOPT_H +#define _EPICS_GETOPT_H + +#include + +#endif /* _EPICS_GETOPT_H */ diff --git a/src/libCom/osi/os/default/epicsReadline.c b/src/libCom/osi/os/default/epicsReadline.c new file mode 100644 index 000000000..80936836a --- /dev/null +++ b/src/libCom/osi/os/default/epicsReadline.c @@ -0,0 +1,372 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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: Eric Norum Date: 12DEC2001 */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "envDefs.h" +#include "epicsReadline.h" + +#define EPICS_COMMANDLINE_LIBRARY_EPICS 0 +#define EPICS_COMMANDLINE_LIBRARY_LIBTECLA 1 +#define EPICS_COMMANDLINE_LIBRARY_READLINE 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_CURSES 2 +#define EPICS_COMMANDLINE_LIBRARY_READLINE_NCURSES 2 + +#ifndef EPICS_COMMANDLINE_LIBRARY +#define EPICS_COMMANDLINE_LIBRARY EPICS_COMMANDLINE_LIBRARY_EPICS +#endif + + + +#if EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_LIBTECLA +#include +#include + +/* + * Create a command-line context + */ +void * epicsShareAPI +epicsReadlineBegin (FILE *in) +{ + GetLine *gl; + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) i = 0; + gl = new_GetLine(200, i * 40); + if ((gl != NULL) && (in != NULL)) + gl_change_terminal(gl, in, stdout, NULL); + return gl; +} + +/* + * Read a line of input + */ +char * epicsShareAPI +epicsReadline (const char *prompt, void *context) +{ + char *line; + char *nl; + + line = gl_get_line(context, prompt ? prompt : "", NULL, -1); + if ((line != NULL) && ((nl = strchr(line, '\n')) != NULL)) + *nl = '\0'; + return line; +} + +/* + * Destroy a command-line context + */ +void epicsShareAPI +epicsReadlineEnd(void *context) +{ + del_GetLine(context); +} + + +#elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_READLINE + +#include +#include + +struct readlineContext { + FILE *in; + char *line; +}; + +/* + * Create a command-line context + */ +void * epicsShareAPI +epicsReadlineBegin(FILE *in) +{ + struct readlineContext *readlineContext; + + readlineContext = malloc(sizeof *readlineContext); + if (readlineContext != NULL) { + readlineContext->in = in; + readlineContext->line = NULL; + if (in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 0) i = 0; + stifle_history (i); + rl_bind_key ('\t', rl_insert); + } + } + return readlineContext; +} + +/* + * Read a line of input + */ +char * epicsShareAPI +epicsReadline (const char *prompt, void *context) +{ + struct readlineContext *readlineContext = context; + + int c; /* char is unsigned on some archs, EOF is -ve */ + char *line = NULL; + int linelen = 0; + int linesize = 50; + + free (readlineContext->line); + readlineContext->line = NULL; + if (readlineContext->in == NULL) { + line = readline (prompt); + } + else { + line = (char *)malloc (linesize * sizeof *line); + if (line == NULL) { + printf ("Out of memory!\n"); + return NULL; + } + if (prompt) { + fputs (prompt, stdout); + fflush (stdout); + } + while ((c = getc (readlineContext->in)) != '\n') { + if (c == EOF) { + free (line); + line = NULL; + break; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc (line, linesize * sizeof *line); + if (cp == NULL) { + printf ("Out of memory!\n"); + free (line); + line = NULL; + break; + } + line = cp; + } + line[linelen++] = c; + } + if (line) + line[linelen] = '\0'; + } + readlineContext->line = line; + if (line && line[0] != '\0') + add_history (line); + return line; +} + +/* + * Destroy a command-line context + */ +void epicsShareAPI +epicsReadlineEnd (void *context) +{ + struct readlineContext *readlineContext = context; + + if (readlineContext) { + free(readlineContext->line); + free(readlineContext); + } +} + + +#elif EPICS_COMMANDLINE_LIBRARY == EPICS_COMMANDLINE_LIBRARY_EPICS + +#if defined(vxWorks) + +#include +#include +#define LEDLIB_LINESIZE 1000 + +struct readlineContext { + int ledId; + char line[LEDLIB_LINESIZE]; + FILE *in; +}; + +/* + * Create a command-line context + */ +void * epicsShareAPI +epicsReadlineBegin(FILE *in) +{ + struct readlineContext *readlineContext; + + readlineContext = malloc(sizeof *readlineContext); + if (readlineContext != NULL) { + readlineContext->ledId = ERROR; + readlineContext->in = in; + if (in == NULL) { + long i = 50; + + envGetLongConfigParam(&IOCSH_HISTSIZE, &i); + if (i < 1) i = 1; + readlineContext->ledId = ledOpen(fileno(stdin), fileno(stdout), i); + if (readlineContext->ledId == ERROR) { + readlineContext->in = stdin; + printf("Warning -- Unabled to allocate space for command-line history.\n"); + printf("Warning -- Command-line editting disabled.\n"); + } + } + } + return readlineContext; +} + +/* + * Read a line of input + */ +char * epicsShareAPI +epicsReadline (const char *prompt, void *context) +{ + struct readlineContext *readlineContext = context; + int i; + + if (prompt) { + fputs(prompt, stdout); + fflush(stdout); + } + if (readlineContext->ledId != ERROR) { + i = ledRead(readlineContext->ledId, readlineContext->line, LEDLIB_LINESIZE-1); + if (i < 0) + return NULL; + } + else { + if (fgets(readlineContext->line, LEDLIB_LINESIZE, readlineContext->in) == NULL) + return NULL; + i = strlen(readlineContext->line); + } + if ((i >= 1) && (readlineContext->line[i-1] == '\n')) + readlineContext->line[i-1] = '\0'; + else + readlineContext->line[i] = '\0'; + return readlineContext->line; +} + +/* + * Destroy a command-line context + */ +void epicsShareAPI +epicsReadlineEnd (void *context) +{ + struct readlineContext *readlineContext = context; + + if (readlineContext) { + if (readlineContext->ledId != ERROR) + ledClose(readlineContext->ledId); + free(readlineContext); + } +} + +#else /* !vxWorks */ + +struct readlineContext { + FILE *in; + char *line; +}; + +/* + * Create a command-line context + */ +void * epicsShareAPI +epicsReadlineBegin(FILE *in) +{ + struct readlineContext *readlineContext; + + readlineContext = malloc(sizeof *readlineContext); + if (readlineContext != NULL) { + readlineContext->in = in; + readlineContext->line = NULL; + } + return readlineContext; +} + +/* + * Read a line of input + */ +char * epicsShareAPI +epicsReadline (const char *prompt, void *context) +{ + struct readlineContext *readlineContext = context; + + int c; /* char is unsigned on some archs, EOF is -ve */ + char *line = NULL; + int linelen = 0; + int linesize = 50; + FILE *in; + + free (readlineContext->line); + readlineContext->line = NULL; + if ((in = readlineContext->in) == NULL) { + in = stdin; + if (prompt != NULL) { + fputs (prompt, stdout); + fflush (stdout); + } + } + line = (char *)malloc (linesize * sizeof *line); + if (line == NULL) { + printf ("Out of memory!\n"); + return NULL; + } + while ((c = getc (in)) != '\n') { + if (c == EOF) { + if (ferror(in)) { + if ((errno == EINTR) || (errno == EPIPE)) { + clearerr(in); + continue; + } + } + free (line); + return NULL; + } + if ((linelen + 1) >= linesize) { + char *cp; + + linesize += 50; + cp = (char *)realloc (line, linesize * sizeof *line); + if (cp == NULL) { + printf ("Out of memory!\n"); + free (line); + return NULL; + } + line = cp; + } + line[linelen++] = c; + } + line[linelen] = '\0'; + readlineContext->line = line; + return line; +} + +/* + * Destroy a command-line context + */ +void epicsShareAPI +epicsReadlineEnd (void *context) +{ + struct readlineContext *readlineContext = context; + + if (readlineContext) { + free(readlineContext->line); + free(readlineContext); + } +} + +#endif /* !vxWorks */ + +#else + +# error "Unsupported EPICS_COMMANDLINE_LIBRARY" + +#endif diff --git a/src/libCom/osi/os/default/epicsReadline.h b/src/libCom/osi/os/default/epicsReadline.h new file mode 100644 index 000000000..f6ddf2791 --- /dev/null +++ b/src/libCom/osi/os/default/epicsReadline.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef _EPICS_READLINE_H +#define _EPICS_READLINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +epicsShareFunc void * epicsShareAPI epicsReadlineBegin (FILE *in); +epicsShareFunc char * epicsShareAPI epicsReadline (const char *prompt, void *context); +epicsShareFunc void epicsShareAPI epicsReadlineEnd (void *context); + +#ifdef __cplusplus +} +#endif + +#endif /* _EPICS_READLINE_H */ diff --git a/src/libCom/osi/os/default/epicsSocketConvertErrnoToString.cpp b/src/libCom/osi/os/default/epicsSocketConvertErrnoToString.cpp new file mode 100644 index 000000000..88d1b536a --- /dev/null +++ b/src/libCom/osi/os/default/epicsSocketConvertErrnoToString.cpp @@ -0,0 +1,35 @@ + +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osdSock.c */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Jeff Hill + * Date: 04-05-94 + * + */ + +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" + +/* + * epicsSocketConvertErrnoToString() + */ +void epicsSocketConvertErrnoToString ( + char * pBuf, unsigned bufSize ) +{ + if ( bufSize ) { + strncpy ( pBuf, strerror ( SOCKERRNO ), bufSize ); + pBuf[bufSize-1] = '\0'; + } +} + diff --git a/src/libCom/osi/os/default/osdAssert.c b/src/libCom/osi/os/default/osdAssert.c new file mode 100644 index 000000000..7e5068b79 --- /dev/null +++ b/src/libCom/osi/os/default/osdAssert.c @@ -0,0 +1,52 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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: Jeffrey Hill + * Date: 02-27-95 + */ + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsVersion.h" +#include "epicsAssert.h" +#include "epicsThread.h" +#include "epicsTime.h" +#include "cantProceed.h" + + +void epicsAssert (const char *pFile, const unsigned line, + const char *pExp, const char *pAuthorName) +{ + epicsTimeStamp current; + + errlogPrintf("\n\n\n" + "A call to 'assert(%s)'\n" + " by thread '%s' failed in %s line %u.\n", + pExp, epicsThreadGetNameSelf(), pFile, line); + errlogPrintf("EPICS Release %s.\n", epicsReleaseVersion); + + if (epicsTimeGetCurrent(¤t) == 0) { + char date[64]; + + epicsTimeToStrftime(date, sizeof(date), + "%Y-%m-%d %H:%M:%S.%f %Z", ¤t); + errlogPrintf("Local time is %s\n", date); + } + + if (!pAuthorName) { + pAuthorName = "the author"; + } + errlogPrintf("Please E-mail this message to %s or to tech-talk@aps.anl.gov\n", + pAuthorName); + + errlogPrintf("Calling epicsThreadSuspendSelf()\n"); + epicsThreadSuspendSelf (); +} diff --git a/src/libCom/osi/os/default/osdEnv.c b/src/libCom/osi/os/default/osdEnv.c new file mode 100644 index 000000000..41f9130e3 --- /dev/null +++ b/src/libCom/osi/os/default/osdEnv.c @@ -0,0 +1,77 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include +#include +#include +#include +#include +#include "epicsFindSymbol.h" + +/* + * Set the value of an environment variable + * Leaks memory, but the assumption is that this routine won't be + * called often enough for the leak to be a problem. + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + char *cp; + + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); + strcpy (cp, name); + strcat (cp, "="); + strcat (cp, value); + if (putenv (cp) < 0) { + errPrintf( + -1L, + __FILE__, + __LINE__, + "Failed to set environment parameter \"%s\" to \"%s\": %s\n", + name, + value, + strerror (errno)); + free (cp); + } +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/src/libCom/osi/os/default/osdFindSymbol.c b/src/libCom/osi/os/default/osdFindSymbol.c new file mode 100644 index 000000000..99ca2832d --- /dev/null +++ b/src/libCom/osi/os/default/osdFindSymbol.c @@ -0,0 +1,21 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/default/epicsFindSymbol.c */ + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ return 0; } + +epicsShareFunc const char *epicsLoadError(void) +{ return "epicsLoadLibrary not implemented"; } + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ return 0;} diff --git a/src/libCom/osi/os/default/osdInterrupt.c b/src/libCom/osi/os/default/osdInterrupt.c new file mode 100644 index 000000000..91c5c5681 --- /dev/null +++ b/src/libCom/osi/os/default/osdInterrupt.c @@ -0,0 +1,58 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/default/osdInterrupt.c */ + +/* Author: Marty Kraimer Date: 15JUL99 */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "errlog.h" +#include "epicsInterrupt.h" + + +static epicsMutexId globalLock = NULL; +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + +static void initOnce(void *junk) +{ + globalLock = epicsMutexMustCreate(); +} + +epicsShareFunc int epicsInterruptLock() +{ + epicsThreadOnce(&onceId, initOnce, NULL); + epicsMutexMustLock(globalLock); + return 0; +} + +epicsShareFunc void epicsInterruptUnlock(int key) +{ + if (!globalLock) + cantProceed("epicsInterruptUnlock called before epicsInterruptLock\n"); + epicsMutexUnlock(globalLock); +} + +epicsShareFunc int epicsInterruptIsInterruptContext() +{ + return 0; +} + +epicsShareFunc void epicsInterruptContextMessage(const char *message) +{ + errlogPrintf("%s", message); +} + diff --git a/src/libCom/osi/os/default/osdInterrupt.h b/src/libCom/osi/os/default/osdInterrupt.h new file mode 100644 index 000000000..5559d6280 --- /dev/null +++ b/src/libCom/osi/os/default/osdInterrupt.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef osdInterrupth +#define osdInterrupth + +#endif /* osdInterrupth */ + diff --git a/src/libCom/osi/os/default/osdMessageQueue.cpp b/src/libCom/osi/os/default/osdMessageQueue.cpp new file mode 100644 index 000000000..6b3c4d1af --- /dev/null +++ b/src/libCom/osi/os/default/osdMessageQueue.cpp @@ -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. +\*************************************************************************/ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMessageQueue.h" +#include +#include +#include +#include + +/* + * Event cache + */ +struct eventNode { + ELLNODE link; + epicsEventId event; +}; + +/* + * List of threads waiting to send or receive a message + */ +struct threadNode { + ELLNODE link; + struct eventNode *evp; + void *buf; + unsigned int size; + volatile bool eventSent; +}; + +/* + * Message info + */ +struct epicsMessageQueueOSD { + ELLLIST sendQueue; + ELLLIST receiveQueue; + ELLLIST eventFreeList; + int numberOfSendersWaiting; + + epicsMutexId mutex; + unsigned long capacity; + unsigned long maxMessageSize; + + unsigned long *buf; + char *firstMessageSlot; + char *lastMessageSlot; + volatile char *inPtr; + volatile char *outPtr; + unsigned long slotSize; + + bool full; +}; + +epicsShareFunc epicsMessageQueueId epicsShareAPI epicsMessageQueueCreate( + unsigned int capacity, + unsigned int maxMessageSize) +{ + epicsMessageQueueId pmsg; + unsigned int slotBytes, slotLongs; + + assert(capacity != 0); + pmsg = (epicsMessageQueueId)callocMustSucceed(1, sizeof(*pmsg), "epicsMessageQueueCreate"); + pmsg->capacity = capacity; + pmsg->maxMessageSize = maxMessageSize; + slotLongs = 1 + ((maxMessageSize + sizeof(unsigned long) - 1) / sizeof(unsigned long)); + slotBytes = slotLongs * sizeof(unsigned long); + pmsg->buf = (unsigned long *)callocMustSucceed(pmsg->capacity, slotBytes, "epicsMessageQueueCreate"); + pmsg->inPtr = pmsg->outPtr = pmsg->firstMessageSlot = (char *)&pmsg->buf[0]; + pmsg->lastMessageSlot = (char *)&pmsg->buf[(capacity - 1) * slotLongs]; + pmsg->full = false; + pmsg->slotSize = slotBytes; + pmsg->mutex = epicsMutexMustCreate(); + ellInit(&pmsg->sendQueue); + ellInit(&pmsg->receiveQueue); + ellInit(&pmsg->eventFreeList); + return pmsg; +} + +epicsShareFunc void epicsShareAPI +epicsMessageQueueDestroy(epicsMessageQueueId pmsg) +{ + struct eventNode *evp; + + while ((evp = reinterpret_cast < struct eventNode * > + ( ellGet(&pmsg->eventFreeList) ) ) != NULL) { + epicsEventDestroy(evp->event); + free(evp); + } + epicsMutexDestroy(pmsg->mutex); + free(pmsg->buf); + free(pmsg); +} + +static struct eventNode * +getEventNode(epicsMessageQueueId pmsg) +{ + struct eventNode *evp; + + evp = reinterpret_cast < struct eventNode * > ( ellGet(&pmsg->eventFreeList) ); + if (evp == NULL) { + evp = (struct eventNode *) callocMustSucceed(1, sizeof(*evp), + "epicsMessageQueueGetEventNode"); + evp->event = epicsEventMustCreate(epicsEventEmpty); + } + return evp; +} + +static int +mySend(epicsMessageQueueId pmsg, void *message, unsigned int size, bool wait, bool haveTimeout, double timeout) +{ + char *myInPtr, *nextPtr; + struct threadNode *pthr; + + if(size > pmsg->maxMessageSize) + return -1; + + /* + * See if message can be sent + */ + epicsMutexLock(pmsg->mutex); + if ((pmsg->numberOfSendersWaiting > 0) + || (pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL))) { + /* + * Return if not allowed to wait + */ + if (!wait) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + + /* + * Wait + */ + struct threadNode threadNode; + threadNode.evp = getEventNode(pmsg); + threadNode.eventSent = false; + ellAdd(&pmsg->sendQueue, &threadNode.link); + pmsg->numberOfSendersWaiting++; + epicsMutexUnlock(pmsg->mutex); + if(haveTimeout) + epicsEventWaitWithTimeout(threadNode.evp->event, timeout); + else + epicsEventWait(threadNode.evp->event); + epicsMutexLock(pmsg->mutex); + if(!threadNode.eventSent) + ellDelete(&pmsg->sendQueue, &threadNode.link); + pmsg->numberOfSendersWaiting--; + ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); + if (pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL)) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + } + + /* + * Copy message to waiting receiver + */ + if ((pthr = reinterpret_cast < struct threadNode * > + ( ellGet(&pmsg->receiveQueue) ) ) != NULL) { + if(size <= pthr->size) + memcpy(pthr->buf, message, size); + pthr->size = size; + pthr->eventSent = true; + epicsEventSignal(pthr->evp->event); + epicsMutexUnlock(pmsg->mutex); + return 0; + } + + /* + * Copy to queue + */ + myInPtr = (char *)pmsg->inPtr; + if (myInPtr == pmsg->lastMessageSlot) + nextPtr = pmsg->firstMessageSlot; + else + nextPtr = myInPtr + pmsg->slotSize; + if (nextPtr == (char *)pmsg->outPtr) + pmsg->full = true; + *(volatile unsigned long *)myInPtr = size; + memcpy((unsigned long *)myInPtr + 1, message, size); + pmsg->inPtr = nextPtr; + epicsMutexUnlock(pmsg->mutex); + return 0; +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueTrySend(epicsMessageQueueId pmsg, void *message, unsigned int size) +{ + return mySend(pmsg, message, size, false, false, 0.0); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueSend(epicsMessageQueueId pmsg, void *message, unsigned int size) +{ + return mySend(pmsg, message, size, true, false, 0.0); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueSendWithTimeout(epicsMessageQueueId pmsg, void *message, unsigned int size, double timeout) +{ + return mySend(pmsg, message, size, true, true, timeout); +} + +static int +myReceive(epicsMessageQueueId pmsg, void *message, unsigned int size, bool wait, bool haveTimeout, double timeout) +{ + char *myOutPtr; + unsigned long l; + struct threadNode *pthr; + + /* + * If there's a message on the queue, copy it + */ + epicsMutexLock(pmsg->mutex); + myOutPtr = (char *)pmsg->outPtr; + if ((myOutPtr != pmsg->inPtr) || pmsg->full) { + int ret; + l = *(unsigned long *)myOutPtr; + if (l <= size) { + memcpy(message, (unsigned long *)myOutPtr + 1, l); + ret = l; + } + else { + ret = -1; + } + if (myOutPtr == pmsg->lastMessageSlot) + pmsg->outPtr = pmsg->firstMessageSlot; + else + pmsg->outPtr += pmsg->slotSize; + pmsg->full = false; + + /* + * Wake up the oldest task waiting to send + */ + if ((pthr = reinterpret_cast < struct threadNode * > + ( ellGet(&pmsg->sendQueue) ) ) != NULL) { + pthr->eventSent = true; + epicsEventSignal(pthr->evp->event); + } + epicsMutexUnlock(pmsg->mutex); + return ret; + } + + /* + * Return if not allowed to wait + */ + if (!wait) { + epicsMutexUnlock(pmsg->mutex); + return -1; + } + + /* + * Wake up the oldest task waiting to send + */ + if ((pthr = reinterpret_cast < struct threadNode * > + ( ellGet(&pmsg->sendQueue) ) ) != NULL) { + pthr->eventSent = true; + epicsEventSignal(pthr->evp->event); + } + + /* + * Wait for message to arrive + */ + struct threadNode threadNode; + threadNode.evp = getEventNode(pmsg); + threadNode.buf = message; + threadNode.size = size; + threadNode.eventSent = false; + ellAdd(&pmsg->receiveQueue, &threadNode.link); + epicsMutexUnlock(pmsg->mutex); + if(haveTimeout) + epicsEventWaitWithTimeout(threadNode.evp->event, timeout); + else + epicsEventWait(threadNode.evp->event); + epicsMutexLock(pmsg->mutex); + if(!threadNode.eventSent) + ellDelete(&pmsg->receiveQueue, &threadNode.link); + ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); + epicsMutexUnlock(pmsg->mutex); + if(threadNode.eventSent && (threadNode.size <= size)) + return threadNode.size; + return -1; +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueTryReceive(epicsMessageQueueId pmsg, void *message, unsigned int size) +{ + return myReceive(pmsg, message, size, false, false, 0.0); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueReceive(epicsMessageQueueId pmsg, void *message, unsigned int size) +{ + return myReceive(pmsg, message, size, true, false, 0.0); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueueReceiveWithTimeout(epicsMessageQueueId pmsg, void *message, unsigned int size, double timeout) +{ + return myReceive(pmsg, message, size, true, true, timeout); +} + +epicsShareFunc int epicsShareAPI +epicsMessageQueuePending(epicsMessageQueueId pmsg) +{ + char *myInPtr, *myOutPtr; + int nmsg; + + epicsMutexLock(pmsg->mutex); + myInPtr = (char *)pmsg->inPtr; + myOutPtr = (char *)pmsg->outPtr; + if (pmsg->full) + nmsg = pmsg->capacity; + else if (myInPtr >= myOutPtr) + nmsg = (myInPtr - myOutPtr) / pmsg->slotSize; + else + nmsg = pmsg->capacity - (myOutPtr - myInPtr) / pmsg->slotSize; + epicsMutexUnlock(pmsg->mutex); + return nmsg; +} + +epicsShareFunc void epicsShareAPI +epicsMessageQueueShow(epicsMessageQueueId pmsg, int level) +{ + printf("Message Queue Used:%d Slots:%lu", epicsMessageQueuePending(pmsg), pmsg->capacity); + if (level >= 1) + printf(" Maximum size:%lu", pmsg->maxMessageSize); + printf("\n"); +} diff --git a/src/libCom/osi/os/default/osdMessageQueue.h b/src/libCom/osi/os/default/osdMessageQueue.h new file mode 100644 index 000000000..dabf6a71e --- /dev/null +++ b/src/libCom/osi/os/default/osdMessageQueue.h @@ -0,0 +1,3 @@ +/* + * Nothing needed for default implementation + */ diff --git a/src/libCom/osi/os/default/osdNetIntf.c b/src/libCom/osi/os/default/osdNetIntf.c new file mode 100644 index 000000000..86b83e63e --- /dev/null +++ b/src/libCom/osi/os/default/osdNetIntf.c @@ -0,0 +1,325 @@ +/*************************************************************************\ +* 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 + * Date: 04-05-94 + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "epicsAssert.h" +#include "errlog.h" + +#ifdef DEBUG +# define ifDepenDebugPrintf(argsInParen) printf argsInParen +#else +# define ifDepenDebugPrintf(argsInParen) +#endif + +/* + * Determine the size of an ifreq structure + * Made difficult by the fact that addresses larger than the structure + * size may be returned from the kernel. + */ +static size_t ifreqSize ( struct ifreq *pifreq ) +{ + size_t size; + + size = ifreq_size ( pifreq ); + if ( size < sizeof ( *pifreq ) ) { + size = sizeof ( *pifreq ); + } + return size; +} + +/* + * Move to the next ifreq structure + */ +static struct ifreq * ifreqNext ( struct ifreq *pifreq ) +{ + struct ifreq *ifr; + + ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq ); + ifDepenDebugPrintf( ("ifreqNext() pifreq 0x%08x, size 0x%08x, ifr 0x%08x\n", pifreq, ifreqSize (pifreq), ifr) ); + return ifr; +} + + +/* + * osiSockDiscoverBroadcastAddresses () + */ +epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses + (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr) +{ + static const unsigned nelem = 100; + int status; + struct ifconf ifconf; + struct ifreq *pIfreqList; + struct ifreq *pIfreqListEnd; + struct ifreq *pifreq; + struct ifreq *pnextifreq; + osiSockAddrNode *pNewNode; + + if ( pMatchAddr->sa.sa_family == AF_INET ) { + if ( pMatchAddr->ia.sin_addr.s_addr == htonl (INADDR_LOOPBACK) ) { + pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); + if ( pNewNode == NULL ) { + errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" ); + return; + } + pNewNode->addr.ia.sin_family = AF_INET; + pNewNode->addr.ia.sin_port = htons ( 0 ); + pNewNode->addr.ia.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + ellAdd ( pList, &pNewNode->node ); + return; + } + } + + /* + * use pool so that we avoid using too much stack space + * + * nelem is set to the maximum interfaces + * on one machine here + */ + pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) ); + if (!pIfreqList) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): no memory to complete request\n"); + return; + } + + ifconf.ifc_len = nelem * sizeof(*pifreq); + ifconf.ifc_req = pIfreqList; + status = socket_ioctl (socket, SIOCGIFCONF, &ifconf); + if (status < 0 || ifconf.ifc_len == 0) { + ifDepenDebugPrintf(("osiSockDiscoverBroadcastAddresses(): status: 0x08x, ifconf.ifc_len: %d\n", status, ifconf.ifc_len)); + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): unable to fetch network interface configuration\n"); + free (pIfreqList); + return; + } + + pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList); + pIfreqListEnd--; + + for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { + uint32_t current_ifreqsize; + + /* + * find the next ifreq + */ + pnextifreq = ifreqNext (pifreq); + + /* determine ifreq size */ + current_ifreqsize = ifreqSize ( pifreq ); + /* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */ + memmove(pIfreqList, pifreq, current_ifreqsize); + + ifDepenDebugPrintf (("osiSockDiscoverBroadcastAddresses(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n", + pIfreqList->ifr_name, + ifreq_size(pifreq), + current_ifreqsize)); + + /* + * If its not an internet interface then dont use it + */ + if ( pIfreqList->ifr_addr.sa_family != AF_INET ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) ); + continue; + } + + /* + * if it isnt a wildcarded interface then look for + * an exact match + */ + if ( pMatchAddr->sa.sa_family != AF_UNSPEC ) { + if ( pMatchAddr->sa.sa_family != AF_INET ) { + continue; + } + if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) { + struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr; + if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) ); + continue; + } + } + } + + status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList ); + if ( status ) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name); + continue; + } + + /* + * dont bother with interfaces that have been disabled + */ + if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" was down\n", pIfreqList->ifr_name) ); + continue; + } + + /* + * dont use the loop back interface + */ + if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) ); + continue; + } + + pNewNode = (osiSockAddrNode *) calloc (1, sizeof (*pNewNode) ); + if ( pNewNode == NULL ) { + errlogPrintf ( "osiSockDiscoverBroadcastAddresses(): no memory available for configuration\n" ); + free ( pIfreqList ); + return; + } + + /* + * If this is an interface that supports + * broadcast fetch the broadcast address. + * + * Otherwise if this is a point to point + * interface then use the destination address. + * + * Otherwise CA will not query through the + * interface. + */ + if ( pIfreqList->ifr_flags & IFF_BROADCAST ) { + status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList); + if ( status ) { + errlogPrintf ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name); + free ( pNewNode ); + continue; + } + pNewNode->addr.sa = pIfreqList->ifr_broadaddr; + ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) ); + } +#if defined (IFF_POINTOPOINT) + else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) { + status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList); + if ( status ) { + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) ); + free ( pNewNode ); + continue; + } + pNewNode->addr.sa = pIfreqList->ifr_dstaddr; + } +#endif + else { + ifDepenDebugPrintf ( ( "osiSockDiscoverBroadcastAddresses(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) ); + free ( pNewNode ); + continue; + } + + ifDepenDebugPrintf ( ("osiSockDiscoverBroadcastAddresses(): net intf \"%s\" found\n", pIfreqList->ifr_name) ); + + /* + * LOCK applied externally + */ + ellAdd ( pList, &pNewNode->node ); + } + + free ( pIfreqList ); +} + +/* + * osiLocalAddr () + */ +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket) +{ + static const unsigned nelem = 100; + static char init = 0; + static osiSockAddr addr; + int status; + struct ifconf ifconf; + struct ifreq *pIfreqList; + struct ifreq *pifreq; + struct ifreq *pIfreqListEnd; + struct ifreq *pnextifreq; + + if ( init ) { + return addr; + } + + memset ( (void *) &addr, '\0', sizeof ( addr ) ); + addr.sa.sa_family = AF_UNSPEC; + + pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pIfreqList) ); + if ( ! pIfreqList ) { + errlogPrintf ( "osiLocalAddr(): no memory to complete request\n" ); + return addr; + } + + ifconf.ifc_len = nelem * sizeof ( *pIfreqList ); + ifconf.ifc_req = pIfreqList; + status = socket_ioctl ( socket, SIOCGIFCONF, &ifconf ); + if ( status < 0 || ifconf.ifc_len == 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( + "osiLocalAddr(): SIOCGIFCONF ioctl failed because \"%s\"\n", + sockErrBuf ); + free ( pIfreqList ); + return addr; + } + + pIfreqListEnd = (struct ifreq *) ( ifconf.ifc_len + (char *) ifconf.ifc_req ); + pIfreqListEnd--; + + for ( pifreq = ifconf.ifc_req; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) { + osiSockAddr addrCpy; + + /* + * find the next if req + */ + pnextifreq = ifreqNext ( pifreq ); + + if ( pifreq->ifr_addr.sa_family != AF_INET ) { + ifDepenDebugPrintf ( ("osiLocalAddr(): interface %s was not AF_INET\n", pifreq->ifr_name) ); + continue; + } + + addrCpy.sa = pifreq->ifr_addr; + + status = socket_ioctl ( socket, SIOCGIFFLAGS, pifreq ); + if ( status < 0 ) { + errlogPrintf ( "osiLocalAddr(): net intf flags fetch for %s failed\n", pifreq->ifr_name ); + continue; + } + + if ( ! ( pifreq->ifr_flags & IFF_UP ) ) { + ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s was down\n", pifreq->ifr_name) ); + continue; + } + + /* + * dont use the loop back interface + */ + if ( pifreq->ifr_flags & IFF_LOOPBACK ) { + ifDepenDebugPrintf ( ("osiLocalAddr(): ignoring loopback interface: %s\n", pifreq->ifr_name) ); + continue; + } + + ifDepenDebugPrintf ( ("osiLocalAddr(): net intf %s found\n", pifreq->ifr_name) ); + + init = 1; + addr = addrCpy; + break; + } + + free ( pIfreqList ); + return addr; +} diff --git a/src/libCom/osi/os/default/osdPoolStatus.c b/src/libCom/osi/os/default/osdPoolStatus.c new file mode 100644 index 000000000..bb764b2ad --- /dev/null +++ b/src/libCom/osi/os/default/osdPoolStatus.c @@ -0,0 +1,23 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "osiPoolStatus.h" + +/* + * osiSufficentSpaceInPool () + * + * @@@@@ not implemented @@@@@ + * + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + return 1; +} diff --git a/src/libCom/osi/os/default/osdPoolStatus.h b/src/libCom/osi/os/default/osdPoolStatus.h new file mode 100644 index 000000000..efb2cfab8 --- /dev/null +++ b/src/libCom/osi/os/default/osdPoolStatus.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +#ifndef osdPoolStatush +#define osdPoolStatush + +#endif /* osdPoolStatush */ diff --git a/src/libCom/osi/os/default/osdSignal.cpp b/src/libCom/osi/os/default/osdSignal.cpp new file mode 100644 index 000000000..08dfa023c --- /dev/null +++ b/src/libCom/osi/os/default/osdSignal.cpp @@ -0,0 +1,21 @@ +/*************************************************************************\ + * 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. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * All NOOPs if the os isnt POSIX + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} + diff --git a/src/libCom/osi/os/default/osdVME.h b/src/libCom/osi/os/default/osdVME.h new file mode 100644 index 000000000..b9922b189 --- /dev/null +++ b/src/libCom/osi/os/default/osdVME.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* 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 */ + +/* + * OS-dependent VME support + */ diff --git a/src/libCom/osi/os/default/osdWireConfig.h b/src/libCom/osi/os/default/osdWireConfig.h new file mode 100644 index 000000000..e8def865e --- /dev/null +++ b/src/libCom/osi/os/default/osdWireConfig.h @@ -0,0 +1,64 @@ +/* + * Default version of osdWireConfig.h that might + * work on UNIX like systems that define + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +/* This file must be usable from both C and C++ */ + +/* if compilation fails because this wasnt found then you may need to define an OS + specific osdWireConfig.h */ +#include + +#ifdef __BYTE_ORDER +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +# elif __BYTE_ORDER == __BIG_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to run on the specified __BYTE_ORDER +# endif +#else +# ifdef BYTE_ORDER +# if BYTE_ORDER == LITTLE_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +# elif BYTE_ORDER == BIG_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to run on the specified BYTE_ORDER +# endif +# else +# error doesnt specify __BYTE_ORDER or BYTE_ORDER - is an OS specific osdWireConfig.h needed? +# endif +#endif + +#ifdef __FLOAT_WORD_ORDER +# if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_LITTLE +# elif __FLOAT_WORD_ORDER == __BIG_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to specified __FLOAT_WORD_ORDER +# endif +#else +# ifdef FLOAT_WORD_ORDER +# if FLOAT_WORD_ORDER == LITTLE_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_LITTLE +# elif FLOAT_WORD_ORDER == BIG_ENDIAN +# define EPICS_FLOAT_WORD_ORDER EPICS_ENDIAN_BIG +# else +# error EPICS hasnt been ported to specified FLOAT_WORD_ORDER +# endif +# else + /* assume that if neither __FLOAT_WORD_ORDER nor FLOAT_WORD_ORDER are + defined then weird fp ordered archs like arm nwfp aren't supported */ +# define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER +# endif +#endif + +#endif /* ifdef osdWireConfig_h */ diff --git a/src/libCom/osi/os/default/osdWireFormat.h b/src/libCom/osi/os/default/osdWireFormat.h new file mode 100644 index 000000000..6db3e20ab --- /dev/null +++ b/src/libCom/osi/os/default/osdWireFormat.h @@ -0,0 +1,247 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 2000, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireFormat +#define osdWireFormat + +#ifdef __SUNPRO_CC +# include +#else +# include +#endif + +#include "epicsEndian.h" + +// +// The default assumption is that the local floating point format is +// IEEE and that these routines only need to perform byte swapping +// as a side effect of copying an aligned operand into an unaligned +// network byte stream. OS specific code can provide a alternative +// for this file if that assumption is wrong. +// + +// +// EPICS_CONVERSION_REQUIRED is set if either the byte order +// or the floating point word order are not exactly big endian. +// This can be set by hand above for a specific architecture +// should there be an architecture that is a weird middle endian +// ieee floating point format that is also big endian integer. +// +#if EPICS_BYTE_ORDER != EPICS_ENDIAN_BIG || EPICS_FLOAT_WORD_ORDER != EPICS_BYTE_ORDER +# if ! defined ( EPICS_CONVERSION_REQUIRED ) +# define EPICS_CONVERSION_REQUIRED +# endif +#endif + +// +// We still use a big endian wire format for CA consistent with the internet, +// but inconsistent with the vast majority of CPUs +// + +template <> +inline void WireGet < epicsFloat64 > ( + const epicsUInt8 * pWireSrc, epicsFloat64 & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union { + epicsFloat64 _f; + epicsUInt32 _u[2]; + } tmp; +# if EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + WireGet ( pWireSrc, tmp._u[1] ); + WireGet ( pWireSrc + 4, tmp._u[0] ); +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + WireGet ( pWireSrc, tmp._u[0] ); + WireGet ( pWireSrc + 4, tmp._u[1] ); +# else +# error unsupported floating point word order +# endif + dst = tmp._f; +} + +#if defined ( __GNUC__ ) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 0 ) +template <> +inline void WireGet < epicsOldString > ( + const epicsUInt8 * pWireSrc, epicsOldString & dst ) +{ + memcpy ( dst, pWireSrc, sizeof ( dst ) ); +} +#else +inline void WireGet ( + const epicsUInt8 * pWireSrc, epicsOldString & dst ) +{ + memcpy ( dst, pWireSrc, sizeof ( dst ) ); +} +#endif + +template <> +inline void WireSet < epicsFloat64 > ( + const epicsFloat64 & src, epicsUInt8 * pWireDst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union { + epicsFloat64 _f; + epicsUInt32 _u[2]; + } tmp; + tmp._f = src; +# if EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + WireSet ( tmp._u[1], pWireDst ); + WireSet ( tmp._u[0], pWireDst + 4 ); +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + WireSet ( tmp._u[0], pWireDst ); + WireSet ( tmp._u[1], pWireDst + 4 ); +# else +# error unsupported floating point word order +# endif +} + +#if defined ( __GNUC__ ) && ( __GNUC__ == 4 && __GNUC_MINOR__ <= 0 ) +template <> +inline void WireSet < epicsOldString > ( + const epicsOldString & src, epicsUInt8 * pWireDst ) +{ + memcpy ( pWireDst, src, sizeof ( src ) ); +} +#else +inline void WireSet ( + const epicsOldString & src, epicsUInt8 * pWireDst ) +{ + memcpy ( pWireDst, src, sizeof ( src ) ); +} +#endif + +template <> +inline void AlignedWireGet < epicsUInt16 > ( + const epicsUInt16 & src, epicsUInt16 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error unsupported endian type +# endif +} + +template <> +inline void AlignedWireGet < epicsUInt32 > ( + const epicsUInt32 & src, epicsUInt32 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error unsupported endian type +# endif +} + +template <> +inline void AlignedWireGet < epicsFloat64 > ( + const epicsFloat64 & src, epicsFloat64 & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union Swapper { + epicsUInt32 _u[2]; + epicsFloat64 _f; + }; +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG && EPICS_FLOAT_WORD_ORDER == EPICS_BYTE_ORDER + dst = src; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + Swapper tmp; + tmp._f = src; + AlignedWireGet ( tmp._u[0], tmp._u[0] ); + AlignedWireGet ( tmp._u[1], tmp._u[1] ); + dst = tmp._f; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + Swapper srcu, dstu; + srcu._f = src; + AlignedWireGet ( srcu._u[1], dstu._u[0] ); + AlignedWireGet ( srcu._u[0], dstu._u[1] ); + dst = dstu._f; +# else +# error unsupported floating point word order +# endif +} + +template <> +inline void AlignedWireSet < epicsUInt16 > + ( const epicsUInt16 & src, epicsUInt16 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error undefined endian type +# endif +} + +template <> +inline void AlignedWireSet < epicsUInt32 > ( + const epicsUInt32 & src, epicsUInt32 & dst ) +{ +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_LITTLE + dst = byteSwap ( src ); +# elif EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG + dst = src; +# else +# error undefined endian type +# endif +} + +template <> +inline void AlignedWireSet < epicsFloat64 > ( + const epicsFloat64 & src, epicsFloat64 & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + union Swapper { + epicsUInt32 _u[2]; + epicsFloat64 _f; + }; +# if EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG && EPICS_FLOAT_WORD_ORDER == EPICS_BYTE_ORDER + dst = src; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_BIG + Swapper tmp; + tmp._f = src; + AlignedWireSet ( tmp._u[0], tmp._u[0] ); + AlignedWireSet ( tmp._u[1], tmp._u[1] ); + dst = tmp._f; +# elif EPICS_FLOAT_WORD_ORDER == EPICS_ENDIAN_LITTLE + Swapper srcu, dstu; + srcu._f = src; + AlignedWireSet ( srcu._u[1], dstu._u[0] ); + AlignedWireSet ( srcu._u[0], dstu._u[1] ); + dst = dstu._f; +# else +# error unsupported floating point word order +# endif +} + +#endif // osdWireFormat diff --git a/src/libCom/osi/os/freebsd/osdSock.h b/src/libCom/osi/os/freebsd/osdSock.h new file mode 100644 index 000000000..0b9e51508 --- /dev/null +++ b/src/libCom/osi/os/freebsd/osdSock.h @@ -0,0 +1,83 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + + +#ifdef __cplusplus +} +#endif + +#ifndef IPPORT_USERRESERVED +#define IPPORT_USERRESERVED 5000 +#endif + + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_name)) + +#endif /*osdSockH*/ + diff --git a/src/libCom/osi/os/freebsd/osdTime.h b/src/libCom/osi/os/freebsd/osdTime.h new file mode 100644 index 000000000..e2373c0ad --- /dev/null +++ b/src/libCom/osi/os/freebsd/osdTime.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is 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: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +/* + * We need this include file since the POSIX version + * causes `struct timespec' to be defined in more than one place. + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsShareAPI + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/src/libCom/osi/os/freebsd/osiFileName.h b/src/libCom/osi/os/freebsd/osiFileName.h new file mode 100644 index 000000000..a64c19ad8 --- /dev/null +++ b/src/libCom/osi/os/freebsd/osiFileName.h @@ -0,0 +1,20 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/freebsd/osiUnistd.h b/src/libCom/osi/os/freebsd/osiUnistd.h new file mode 100644 index 000000000..8a371a024 --- /dev/null +++ b/src/libCom/osi/os/freebsd/osiUnistd.h @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +/* + * Some systems fail to provide prototypes of these functions. + * Others provide different prototypes. + * There seems to be no way to handle this automatically, so + * if you get compile errors, just make the appropriate changes here. + */ diff --git a/src/libCom/osi/os/iOS/epicsMath.h b/src/libCom/osi/os/iOS/epicsMath.h new file mode 100644 index 000000000..493583f70 --- /dev/null +++ b/src/libCom/osi/os/iOS/epicsMath.h @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include + +#define finite(x) isfinite(x) + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/src/libCom/osi/os/iOS/osdEnv.c b/src/libCom/osi/os/iOS/osdEnv.c new file mode 100644 index 000000000..3c7b5056f --- /dev/null +++ b/src/libCom/osi/os/iOS/osdEnv.c @@ -0,0 +1,78 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* osdEnv.c */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include +#include +#include +#include +#include +#include "epicsFindSymbol.h" + + +/* + * Set the value of an environment variable + * Leaks memory, but the assumption is that this routine won't be + * called often enough for the leak to be a problem. + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + char *cp; + + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); + strcpy (cp, name); + strcat (cp, "="); + strcat (cp, value); + if (putenv (cp) < 0) { + errPrintf( + -1L, + __FILE__, + __LINE__, + "Failed to set environment parameter \"%s\" to \"%s\": %s\n", + name, + value, + strerror (errno)); + free (cp); + } +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + extern char **environ; + char **sp; + + for (sp = environ ; (sp != NULL) && (*sp != NULL) ; sp++) + printf ("%s\n", *sp); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/src/libCom/osi/os/iOS/osdSock.h b/src/libCom/osi/os/iOS/osdSock.h new file mode 100644 index 000000000..de1923967 --- /dev/null +++ b/src/libCom/osi/os/iOS/osdSock.h @@ -0,0 +1,82 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is 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: Eric Norum + */ + +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +/*#include +#include */ +#include +#include +#include +#include +#include +#include /* close() and others */ + + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; +typedef socklen_t osiSocklen_t; + +#define FD_IN_FDSET(FD) ((FD)ifr_addr.sa_len + sizeof((pifreq)->ifr_name)) + +#endif /*osdSockH*/ diff --git a/src/libCom/osi/os/iOS/osdSockAddrReuse.cpp b/src/libCom/osi/os/iOS/osdSockAddrReuse.cpp new file mode 100644 index 000000000..535384184 --- /dev/null +++ b/src/libCom/osi/os/iOS/osdSockAddrReuse.cpp @@ -0,0 +1,48 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, 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 is 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 + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} + +/* + * SO_REUSEPORT is not in POSIX + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEPORT, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEPORT?\n"); + } +} diff --git a/src/libCom/osi/os/iOS/osdTime.h b/src/libCom/osi/os/iOS/osdTime.h new file mode 100644 index 000000000..8fd9c8aea --- /dev/null +++ b/src/libCom/osi/os/iOS/osdTime.h @@ -0,0 +1,30 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is 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: Eric Norum + */ + +#ifndef osdTimeh +#define osdTimeh + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void convertDoubleToWakeTime(double timeout, + struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/src/libCom/osi/os/iOS/osiFileName.h b/src/libCom/osi/os/iOS/osiFileName.h new file mode 100644 index 000000000..052636097 --- /dev/null +++ b/src/libCom/osi/os/iOS/osiFileName.h @@ -0,0 +1,18 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is 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: Eric Norum + */ + +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/posix/README b/src/libCom/osi/os/posix/README new file mode 100644 index 000000000..59f3d9524 --- /dev/null +++ b/src/libCom/osi/os/posix/README @@ -0,0 +1,13 @@ +A knowledge of pthreads is necessary to understand osdThread.c and osdSem.c. + +The following are good references + +Programming with POSIX Threads, David R. Butenhof, Addison-weslet, 1997 + +Multithreaded Programming with Pthreads, Bil Lewis & Daniel J. Berg, +Sun Microsystems, 1998 + +Pthreads Programming, Bradford Nichols etc, O'Reilly & Associates, 1996 + +The implementation of semMutex is based on the example code by Bil Leeis +that os available via the WWW. diff --git a/src/libCom/osi/os/posix/epicsMath.h b/src/libCom/osi/os/posix/epicsMath.h new file mode 100644 index 000000000..b09c7024b --- /dev/null +++ b/src/libCom/osi/os/posix/epicsMath.h @@ -0,0 +1,27 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/src/libCom/osi/os/posix/epicsTempFile.cpp b/src/libCom/osi/os/posix/epicsTempFile.cpp new file mode 100644 index 000000000..f662284d5 --- /dev/null +++ b/src/libCom/osi/os/posix/epicsTempFile.cpp @@ -0,0 +1,37 @@ +/*************************************************************************\ +* 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 + +#define epicsExportSharedSymbols +#include "epicsStdio.h" + +extern "C" +epicsShareFunc void epicsShareAPI epicsTempName ( + char * pNameBuf, size_t nameBufLength ) +{ + if ( nameBufLength ) { + pNameBuf[0] = '\0'; + char nameBuf[L_tmpnam]; + if ( tmpnam ( nameBuf ) ) { + if ( nameBufLength > strlen ( nameBuf ) ) { + strncpy ( pNameBuf, nameBuf, nameBufLength ); + } + } + } +} + + +extern "C" +epicsShareFunc FILE * epicsShareAPI epicsTempFile ( void ) +{ + return tmpfile (); +} + diff --git a/src/libCom/osi/os/posix/osdEvent.c b/src/libCom/osi/os/posix/osdEvent.c new file mode 100644 index 000000000..dd3923c7d --- /dev/null +++ b/src/libCom/osi/os/posix/osdEvent.c @@ -0,0 +1,175 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/posix/osdEvent.c */ + +/* Author: Marty Kraimer Date: 13AUG1999 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsEvent.h" +#include "cantProceed.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsAssert.h" + +/* Until these can be demonstrated to work leave them undefined*/ +#undef _POSIX_THREAD_PROCESS_SHARED +#undef _POSIX_THREAD_PRIO_INHERIT + +typedef struct epicsEventOSD { + pthread_mutex_t mutex; + pthread_cond_t cond; + int isFull; +}epicsEventOSD; + +#define checkStatus(status,message) \ +if((status)) { \ + errlogPrintf("epicsEvent %s failed: error %s\n",(message),strerror((status)));} + +#define checkStatusQuit(status,message,method) \ +if(status) { \ + errlogPrintf("epicsEvent %s failed: error %s\n",(message),strerror((status))); \ + cantProceed((method)); \ +} + +static int mutexLock(pthread_mutex_t *id) +{ + int status; + + while(1) { + status = pthread_mutex_lock(id); + if(status!=EINTR) return status; + errlogPrintf("pthread_mutex_lock returned EINTR. Violates SUSv3\n"); + } +} + +static int condTimedwait(pthread_cond_t *condId, pthread_mutex_t *mutexId, + struct timespec *time) +{ + int status; + while(1) { + status = pthread_cond_timedwait(condId,mutexId,time); + if(status!=EINTR) return status; + errlogPrintf("pthread_cond_timedwait returned EINTR. Violates SUSv3\n"); + } +} + +static int condWait(pthread_cond_t *condId, pthread_mutex_t *mutexId) +{ + int status; + while(1) { + status = pthread_cond_wait(condId,mutexId); + if(status!=EINTR) return status; + errlogPrintf("pthread_cond_wait returned EINTR. Violates SUSv3\n"); + } +} + +epicsShareFunc epicsEventId epicsShareAPI epicsEventCreate(epicsEventInitialState initialState) +{ + epicsEventOSD *pevent; + int status; + + pevent = callocMustSucceed(1,sizeof(*pevent),"epicsEventCreate"); + status = pthread_mutex_init(&pevent->mutex,0); + checkStatusQuit(status,"pthread_mutex_init","epicsEventCreate"); + status = pthread_cond_init(&pevent->cond,0); + checkStatusQuit(status,"pthread_cond_init","epicsEventCreate"); + if(initialState==epicsEventFull) pevent->isFull = 1; + return((epicsEventId)pevent); +} + +epicsShareFunc epicsEventId epicsShareAPI epicsEventMustCreate(epicsEventInitialState initialState) +{ + epicsEventId id = epicsEventCreate (initialState); + assert (id); + return id; +} + +epicsShareFunc void epicsShareAPI epicsEventDestroy(epicsEventId pevent) +{ + int status; + + status = pthread_mutex_destroy(&pevent->mutex); + checkStatus(status,"pthread_mutex_destroy"); + status = pthread_cond_destroy(&pevent->cond); + checkStatus(status,"pthread_cond_destroy"); + free(pevent); +} + +epicsShareFunc void epicsShareAPI epicsEventSignal(epicsEventId pevent) +{ + int status; + + status = mutexLock(&pevent->mutex); + checkStatusQuit(status,"pthread_mutex_lock","epicsEventSignal"); + if(!pevent->isFull) { + pevent->isFull = 1; + status = pthread_cond_signal(&pevent->cond); + checkStatus(status,"pthread_cond_signal"); + } + status = pthread_mutex_unlock(&pevent->mutex); + checkStatusQuit(status,"pthread_mutex_unlock","epicsEventSignal"); +} + +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventWait(epicsEventId pevent) +{ + int status; + + if(!pevent) return(epicsEventWaitError); + status = mutexLock(&pevent->mutex); + checkStatusQuit(status,"pthread_mutex_lock","epicsEventWait"); + /*no need for while since caller must be prepared for no work*/ + if(!pevent->isFull) { + status = condWait(&pevent->cond,&pevent->mutex); + checkStatusQuit(status,"pthread_cond_wait","epicsEventWait"); + } + pevent->isFull = 0; + status = pthread_mutex_unlock(&pevent->mutex); + checkStatusQuit(status,"pthread_mutex_unlock","epicsEventWait"); + return(epicsEventWaitOK); +} + +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventWaitWithTimeout(epicsEventId pevent, double timeout) +{ + struct timespec wakeTime; + int status = 0; + int unlockStatus; + + status = mutexLock(&pevent->mutex); + checkStatusQuit(status,"pthread_mutex_lock","epicsEventWaitWithTimeout"); + if(!pevent->isFull) { + convertDoubleToWakeTime(timeout,&wakeTime); + status = condTimedwait( + &pevent->cond,&pevent->mutex,&wakeTime); + } + if(status==0) pevent->isFull = 0; + unlockStatus = pthread_mutex_unlock(&pevent->mutex); + checkStatusQuit(unlockStatus,"pthread_mutex_unlock","epicsEventWaitWithTimeout"); + if(status==0) return(epicsEventWaitOK); + if(status==ETIMEDOUT) return(epicsEventWaitTimeout); + checkStatus(status,"pthread_cond_timedwait"); + return(epicsEventWaitError); +} + +epicsShareFunc epicsEventWaitStatus epicsShareAPI epicsEventTryWait(epicsEventId id) +{ + return(epicsEventWaitWithTimeout(id,0.0)); +} + +epicsShareFunc void epicsShareAPI epicsEventShow(epicsEventId id,unsigned int level) +{ +} diff --git a/src/libCom/osi/os/posix/osdEvent.h b/src/libCom/osi/os/posix/osdEvent.h new file mode 100644 index 000000000..e2f8b9af9 --- /dev/null +++ b/src/libCom/osi/os/posix/osdEvent.h @@ -0,0 +1,10 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* for a pure posix implementation no osdEvent.h definitions are needed*/ diff --git a/src/libCom/osi/os/posix/osdMutex.c b/src/libCom/osi/os/posix/osdMutex.c new file mode 100644 index 000000000..064b82fe3 --- /dev/null +++ b/src/libCom/osi/os/posix/osdMutex.c @@ -0,0 +1,284 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osi/os/posix/osdMutex.c */ + +/* Author: Marty Kraimer Date: 13AUG1999 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMutex.h" +#include "cantProceed.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsAssert.h" + +#define checkStatus(status,message) \ +if((status)) { \ + errlogPrintf("epicsMutex %s failed: error %s\n",(message),strerror((status)));} + +#define checkStatusQuit(status,message,method) \ +if(status) { \ + errlogPrintf("epicsMutex %s failed: error %s\n",(message),strerror((status))); \ + cantProceed((method)); \ +} + +static int mutexLock(pthread_mutex_t *id) +{ + int status; + + while ((status = pthread_mutex_lock(id)) == EINTR) { + errlogPrintf("pthread_mutex_lock returned EINTR. Violates SUSv3\n"); + } + return status; +} + +/* Until these can be demonstrated to work leave them undefined*/ +/* On solaris 8 _POSIX_THREAD_PRIO_INHERIT fails*/ +#undef _POSIX_THREAD_PROCESS_SHARED +#undef _POSIX_THREAD_PRIO_INHERIT + +/* Two completely different implementations are provided below + * If support is available for PTHREAD_MUTEX_RECURSIVE then + * only pthread_mutex is used. + * If support is not available for PTHREAD_MUTEX_RECURSIVE then + * a much more complicated solution is required + */ + + +#if defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 +typedef struct epicsMutexOSD { + pthread_mutexattr_t mutexAttr; + pthread_mutex_t lock; +} epicsMutexOSD; + +epicsMutexOSD * epicsMutexOsdCreate(void) { + epicsMutexOSD *pmutex; + int status; + + pmutex = callocMustSucceed(1, sizeof(*pmutex), "epicsMutexOsdCreate"); + status = pthread_mutexattr_init(&pmutex->mutexAttr); + checkStatusQuit(status,"pthread_mutexattr_init", "epicsMutexOsdCreate"); + +#if defined _POSIX_THREAD_PRIO_INHERIT + status = pthread_mutexattr_setprotocol(&pmutex->mutexAttr, + PTHREAD_PRIO_INHERIT); + if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); +#endif /*_POSIX_THREAD_PRIO_INHERIT*/ + + status = pthread_mutexattr_settype(&pmutex->mutexAttr, + PTHREAD_MUTEX_RECURSIVE); + if (errVerbose) checkStatus(status, "pthread_mutexattr_settype"); + + status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); + checkStatusQuit(status, "pthread_mutex_init", "epicsMutexOsdCreate"); + return pmutex; +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) +{ + int status; + + status = pthread_mutex_destroy(&pmutex->lock); + checkStatus(status, "pthread_mutex_destroy"); + status = pthread_mutexattr_destroy(&pmutex->mutexAttr); + checkStatus(status, "pthread_mutexattr_destroy"); + free(pmutex); +} + +void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) +{ + int status; + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatusQuit(status, "pthread_mutex_unlock", "epicsMutexOsdUnlock"); +} + +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) +{ + int status; + + if (!pmutex) return epicsMutexLockError; + status = mutexLock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + checkStatusQuit(status, "pthread_mutex_lock", "epicsMutexOsdLock"); + return epicsMutexLockOK; +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) +{ + int status; + + if (!pmutex) return epicsMutexLockError; + status = pthread_mutex_trylock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + if (status == EBUSY) return epicsMutexLockTimeout; + checkStatusQuit(status, "pthread_mutex_lock", "epicsMutexOsdTryLock"); + return epicsMutexLockOK; +} + +void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level) +{ +} + +#else /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ + +typedef struct epicsMutexOSD { + pthread_mutexattr_t mutexAttr; + pthread_mutex_t lock; + pthread_cond_t waitToBeOwner; +#if defined _POSIX_THREAD_PROCESS_SHARED + pthread_condattr_t condAttr; +#endif /*_POSIX_THREAD_PROCESS_SHARED*/ + int count; + int owned; /* TRUE | FALSE */ + pthread_t ownerTid; +} epicsMutexOSD; + +epicsMutexOSD * epicsMutexOsdCreate(void) { + epicsMutexOSD *pmutex; + int status; + + pmutex = callocMustSucceed(1, sizeof(*pmutex), "epicsMutexOsdCreate"); + status = pthread_mutexattr_init(&pmutex->mutexAttr); + checkStatusQuit(status, "pthread_mutexattr_init", "epicsMutexOsdCreate"); + +#if defined _POSIX_THREAD_PRIO_INHERIT + status = pthread_mutexattr_setprotocol( + &pmutex->mutexAttr,PTHREAD_PRIO_INHERIT); + if (errVerbose) checkStatus(status, "pthread_mutexattr_setprotocal"); +#endif /*_POSIX_THREAD_PRIO_INHERIT*/ + + status = pthread_mutex_init(&pmutex->lock, &pmutex->mutexAttr); + checkStatusQuit(status, "pthread_mutex_init", "epicsMutexOsdCreate"); + +#if defined _POSIX_THREAD_PROCESS_SHARED + status = pthread_condattr_init(&pmutex->condAttr); + checkStatus(status, "pthread_condattr_init"); + status = pthread_condattr_setpshared(&pmutex->condAttr, + PTHREAD_PROCESS_PRIVATE); + checkStatus(status, "pthread_condattr_setpshared"); + status = pthread_cond_init(&pmutex->waitToBeOwner, &pmutex->condAttr); +#else + status = pthread_cond_init(&pmutex->waitToBeOwner, 0); +#endif /*_POSIX_THREAD_PROCESS_SHARED*/ + checkStatusQuit(status, "pthread_cond_init", "epicsMutexOsdCreate"); + return pmutex; +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * pmutex) +{ + int status; + + status = pthread_cond_destroy(&pmutex->waitToBeOwner); + checkStatus(status, "pthread_cond_destroy"); +#if defined _POSIX_THREAD_PROCESS_SHARED + status = pthread_condattr_destroy(&pmutex->condAttr); +#endif /*_POSIX_THREAD_PROCESS_SHARED*/ + status = pthread_mutex_destroy(&pmutex->lock); + checkStatus(status, "pthread_mutex_destroy"); + status = pthread_mutexattr_destroy(&pmutex->mutexAttr); + checkStatus(status, "pthread_mutexattr_destroy"); + free(pmutex); +} + +void epicsMutexOsdUnlock(struct epicsMutexOSD * pmutex) +{ + int status; + + status = mutexLock(&pmutex->lock); + checkStatusQuit(status, "pthread_mutex_lock", "epicsMutexOsdUnlock"); + + if ((pmutex->count <= 0) || (pmutex->ownerTid != pthread_self())) { + errlogPrintf("epicsMutexOsdUnlock but caller is not owner\n"); + status = pthread_mutex_unlock(&pmutex->lock); + checkStatusQuit(status, "pthread_mutex_unlock", "epicsMutexOsdUnlock"); + return; + } + + pmutex->count--; + if (pmutex->count == 0) { + pmutex->owned = 0; + pmutex->ownerTid = 0; + pthread_cond_signal(&pmutex->waitToBeOwner); + } + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatusQuit(status, "pthread_mutex_unlock", "epicsMutexOsdUnlock"); +} + +static int condWait(pthread_cond_t *condId, pthread_mutex_t *mutexId) +{ + int status; + + while ((status = pthread_cond_wait(condId, mutexId)) == EINTR) { + errlogPrintf("pthread_cond_wait returned EINTR. Violates SUSv3\n"); + } + return status; +} + +epicsMutexLockStatus epicsMutexOsdLock(struct epicsMutexOSD * pmutex) +{ + pthread_t tid = pthread_self(); + int status; + + if (!pmutex || !tid) return epicsMutexLockError; + status = mutexLock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + checkStatusQuit(status, "pthread_mutex_lock", "epicsMutexOsdLock"); + + while (pmutex->owned && !pthread_equal(pmutex->ownerTid, tid)) + condWait(&pmutex->waitToBeOwner, &pmutex->lock); + pmutex->ownerTid = tid; + pmutex->owned = 1; + pmutex->count++; + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatusQuit(status, "pthread_mutex_unlock", "epicsMutexOsdLock"); + return epicsMutexLockOK; +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * pmutex) +{ + pthread_t tid = pthread_self(); + epicsMutexLockStatus result; + int status; + + status = mutexLock(&pmutex->lock); + if (status == EINVAL) return epicsMutexLockError; + checkStatusQuit(status, "pthread_mutex_lock", "epicsMutexOsdTryLock"); + + if (!pmutex->owned || pthread_equal(pmutex->ownerTid, tid)) { + pmutex->ownerTid = tid; + pmutex->owned = 1; + pmutex->count++; + result = epicsMutexLockOK; + } else { + result = epicsMutexLockTimeout; + } + + status = pthread_mutex_unlock(&pmutex->lock); + checkStatusQuit(status, "pthread_mutex_unlock", "epicsMutexOsdTryLock"); + return result; +} + +void epicsMutexOsdShow(struct epicsMutexOSD *pmutex,unsigned int level) +{ + printf("ownerTid %p count %d owned %d\n", + (void *)pmutex->ownerTid, pmutex->count, pmutex->owned); +} +#endif /*defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE)>=500 */ diff --git a/src/libCom/osi/os/posix/osdMutex.h b/src/libCom/osi/os/posix/osdMutex.h new file mode 100644 index 000000000..cc416384a --- /dev/null +++ b/src/libCom/osi/os/posix/osdMutex.h @@ -0,0 +1,10 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* for a pure posix implementation no osdMutex.h definitions are needed*/ diff --git a/src/libCom/osi/os/posix/osdProcess.c b/src/libCom/osi/os/posix/osdProcess.c new file mode 100644 index 000000000..97d3570a0 --- /dev/null +++ b/src/libCom/osi/os/posix/osdProcess.c @@ -0,0 +1,109 @@ +/*************************************************************************\ +* 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 + * + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" +#include "errlog.h" +#include "epicsAssert.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + struct passwd *p; + + p = getpwuid ( getuid () ); + if ( p && p->pw_name ) { + size_t len = strlen ( p->pw_name ); + unsigned uiLength; + + if ( len > UINT_MAX || len <= 0 ) { + return osiGetUserNameFail; + } + uiLength = (unsigned) len; + + if ( uiLength + 1 >= bufSizeIn ) { + return osiGetUserNameFail; + } + + strncpy ( pBuf, p->pw_name, (size_t) bufSizeIn ); + + return osiGetUserNameSuccess; + } + else { + return osiGetUserNameFail; + } +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName) +{ + int status; + + /* + * create a duplicate process + */ + status = fork (); + if (status < 0) { + return osiSpawnDetachedProcessFail; + } + + /* + * return to the caller + * if its in the initiating process + */ + if (status) { + return osiSpawnDetachedProcessSuccess; + } + + /* + * close all open files except for STDIO so they will not + * be inherited by the spawned process. + */ + { + int fd, maxfd = sysconf ( _SC_OPEN_MAX ); + for ( fd = 0; fd <= maxfd; fd++ ) { + if (fd==STDIN_FILENO) continue; + if (fd==STDOUT_FILENO) continue; + if (fd==STDERR_FILENO) continue; + close (fd); + } + } + + /* + * overlay the specified executable + */ + status = execlp (pBaseExecutableName, pBaseExecutableName, (char *)NULL); + if ( status < 0 ) { + fprintf ( stderr, "**** The executable \"%s\" couldn't be located\n", pBaseExecutableName ); + fprintf ( stderr, "**** because of errno = \"%s\".\n", strerror (errno) ); + fprintf ( stderr, "**** You may need to modify your PATH environment variable.\n" ); + fprintf ( stderr, "**** Unable to start \"%s\" process.\n", pProcessName); + } + /* Don't run our parent's atexit() handlers */ + _exit ( -1 ); +} diff --git a/src/libCom/osi/os/posix/osdSignal.cpp b/src/libCom/osi/os/posix/osdSignal.cpp new file mode 100644 index 000000000..3f8deb4fb --- /dev/null +++ b/src/libCom/osi/os/posix/osdSignal.cpp @@ -0,0 +1,57 @@ +/*************************************************************************\ + * Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Authors: J. Hill, A. Johnson + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +static void ignoreIfDefault(int signum, const char *name) +{ + struct sigaction curAction; + int status = sigaction(signum, NULL, &curAction); + + if (status >= 0 && + curAction.sa_handler == SIG_DFL) { + curAction.sa_handler = SIG_IGN; + status = sigaction(signum, &curAction, NULL); + } + if (status < 0) { + fprintf(stderr, "%s: sigaction failed for %s, %s\n", + __FILE__, name, strerror(errno)); + } +} + +/* + * epicsSignalInstallSigHupIgnore () + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore (void) +{ + ignoreIfDefault(SIGHUP, "SIGHUP"); +} + +/* + * epicsSignalInstallSigPipeIgnore () + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore (void) +{ + ignoreIfDefault(SIGPIPE, "SIGPIPE"); +} + +/* Disabled */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm + ( struct epicsThreadOSD * /* threadId */ ) {} diff --git a/src/libCom/osi/os/posix/osdSock.c b/src/libCom/osi/os/posix/osdSock.c new file mode 100644 index 000000000..1effdb87f --- /dev/null +++ b/src/libCom/osi/os/posix/osdSock.c @@ -0,0 +1,184 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osdSock.c */ +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Jeff Hill + * Date: 04-05-94 + * + */ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "osiSock.h" +#include "epicsAssert.h" +#include "errlog.h" + +/* + * Protect some routines which are not thread-safe + */ +static epicsMutexId infoMutex; +static void createInfoMutex (void *unused) +{ + infoMutex = epicsMutexMustCreate (); +} +static void lockInfo (void) +{ + static epicsThreadOnceId infoMutexOnceFlag = EPICS_THREAD_ONCE_INIT; + + epicsThreadOnce (&infoMutexOnceFlag, createInfoMutex, NULL); + epicsMutexMustLock (infoMutex); +} +static void unlockInfo (void) +{ + epicsMutexUnlock (infoMutex); +} + +/* + * NOOP + */ +int osiSockAttach() +{ + return 1; +} + +/* + * NOOP + */ +void osiSockRelease() +{ +} + +/* + * this version sets the file control flags so that + * the socket will be closed if the user uses exec() + * as is the case with third party tools such as TCL/TK + */ +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ) +{ + SOCKET sock = socket ( domain, type, protocol ); + if ( sock < 0 ) { + sock = INVALID_SOCKET; + } + else { + int status = fcntl ( sock, F_SETFD, FD_CLOEXEC ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketCreate: failed to " + "fcntl FD_CLOEXEC because \"%s\"\n", + buf ); + close ( sock ); + sock = INVALID_SOCKET; + } + } + return sock; +} + +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) +{ + int newSock = accept ( sock, pAddr, addrlen ); + if ( newSock < 0 ) { + newSock = INVALID_SOCKET; + } + else { + int status = fcntl ( newSock, F_SETFD, FD_CLOEXEC ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketCreate: failed to " + "fcntl FD_CLOEXEC because \"%s\"\n", + buf ); + close ( newSock ); + newSock = INVALID_SOCKET; + } + } + return newSock; +} + +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) +{ + int status = close ( s ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketDestroy: failed to " + "close a socket because \"%s\"\n", + buf ); + } +} + +/* + * ipAddrToHostName + * On many systems, gethostbyaddr must be protected by a + * mutex since the routine is not thread-safe. + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName + (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + struct hostent *ent; + int ret = 0; + + if (bufSize<1) { + return 0; + } + + lockInfo (); + ent = gethostbyaddr((const char *) pAddr, sizeof (*pAddr), AF_INET); + if (ent) { + strncpy (pBuf, ent->h_name, bufSize); + pBuf[bufSize-1] = '\0'; + ret = strlen (pBuf); + } + unlockInfo (); + return ret; +} + +/* + * hostToIPAddr () + * On many systems, gethostbyname must be protected by a + * mutex since the routine is not thread-safe. + */ +epicsShareFunc int epicsShareAPI hostToIPAddr + (const char *pHostName, struct in_addr *pIPA) +{ + struct hostent *phe; + int ret = -1; + + lockInfo (); + phe = gethostbyname (pHostName); + if (phe && phe->h_addr_list[0]) { + if (phe->h_addrtype==AF_INET && phe->h_length<=sizeof(struct in_addr)) { + struct in_addr *pInAddrIn = (struct in_addr *) phe->h_addr_list[0]; + + *pIPA = *pInAddrIn; + ret = 0; + } + } + unlockInfo (); + return ret; +} + + + diff --git a/src/libCom/osi/os/posix/osdSockAddrReuse.cpp b/src/libCom/osi/os/posix/osdSockAddrReuse.cpp new file mode 100644 index 000000000..20beac38c --- /dev/null +++ b/src/libCom/osi/os/posix/osdSockAddrReuse.cpp @@ -0,0 +1,50 @@ + +/*************************************************************************\ +* 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 + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include "errlog.h" + +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} + +/* + * SO_REUSEPORT is not in POSIX + */ +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ) +{ + int yes = true; + int status; + status = setsockopt ( s, SOL_SOCKET, SO_REUSEADDR, + (char *) & yes, sizeof ( yes ) ); + if ( status < 0 ) { + errlogPrintf ( + "epicsSocketEnablePortUseForDatagramFanout: " + "unable to set SO_REUSEADDR?\n"); + } +} diff --git a/src/libCom/osi/os/posix/osdStdio.c b/src/libCom/osi/os/posix/osdStdio.c new file mode 100644 index 000000000..2a23cee71 --- /dev/null +++ b/src/libCom/osi/os/posix/osdStdio.c @@ -0,0 +1,31 @@ +/*************************************************************************\ +* 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 +#define epicsExportSharedSymbols +#include + +epicsShareFunc int epicsShareAPI epicsSnprintf( + char *str, size_t size, const char *format, ...) +{ + int nchars; + va_list pvar; + + va_start(pvar,format); + nchars = epicsVsnprintf(str,size,format,pvar); + va_end (pvar); + return(nchars); +} + +epicsShareFunc int epicsShareAPI epicsVsnprintf( + char *str, size_t size, const char *format, va_list ap) +{ + return vsnprintf ( str, size, format, ap ); +} diff --git a/src/libCom/osi/os/posix/osdStrtod.h b/src/libCom/osi/os/posix/osdStrtod.h new file mode 100644 index 000000000..502889a9d --- /dev/null +++ b/src/libCom/osi/os/posix/osdStrtod.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +/* + * epicsStrtod() for systems with working strtod() routine + */ +#define epicsStrtod strtod diff --git a/src/libCom/osi/os/posix/osdThread.c b/src/libCom/osi/os/posix/osdThread.c new file mode 100644 index 000000000..df0321ae2 --- /dev/null +++ b/src/libCom/osi/os/posix/osdThread.c @@ -0,0 +1,753 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* epicsThread.c */ + +/* Author: Marty Kraimer Date: 18JAN2000 */ + +/* This is a posix implementation of epicsThread */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsStdio.h" +#include "ellLib.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsString.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "errlog.h" +#include "epicsAssert.h" +#include "epicsExit.h" + +static int mutexLock(pthread_mutex_t *id) +{ + int status; + + while(1) { + status = pthread_mutex_lock(id); + if(status!=EINTR) return status; + fprintf(stderr,"pthread_mutex_lock returned EINTR. Violates SUSv3\n"); + } +} + +#if defined DONT_USE_POSIX_THREAD_PRIORITY_SCHEDULING +#undef _POSIX_THREAD_PRIORITY_SCHEDULING +#endif + +typedef struct commonAttr{ + pthread_attr_t attr; + struct sched_param schedParam; + int maxPriority; + int minPriority; + int schedPolicy; +} commonAttr; + +typedef struct epicsThreadOSD { + ELLNODE node; + pthread_t tid; + pthread_attr_t attr; + struct sched_param schedParam; + EPICSTHREADFUNC createFunc; + void *createArg; + epicsEventId suspendEvent; + int isSuspended; + int isEpicsThread; + int isFifoScheduled; + int isOnThreadList; + unsigned int osiPriority; + char *name; +} epicsThreadOSD; + +static pthread_key_t getpthreadInfo; +static pthread_mutex_t onceLock; +static pthread_mutex_t listLock; +static ELLLIST pthreadList = ELLLIST_INIT; +static commonAttr *pcommonAttr = 0; +static int epicsThreadOnceCalled = 0; + +static epicsThreadOSD *createImplicit(void); + +#define checkStatus(status,message) \ +if((status)) {\ + errlogPrintf("%s error %s\n",(message),strerror((status))); \ +} + +#define checkStatusQuit(status,message,method) \ +if(status) { \ + errlogPrintf("%s error %s\n",(message),strerror((status))); \ + cantProceed((method)); \ +} + +/* The following are for use by once, which is only invoked from epicsThreadInit*/ +/* Until epicsThreadInit completes errlogInit will not work */ +/* It must also be used by init_threadInfo otherwise errlogInit could get */ +/* called recursively */ +#define checkStatusOnce(status,message) \ +if((status)) {\ + fprintf(stderr,"%s error %s\n",(message),strerror((status))); } + +#define checkStatusOnceQuit(status,message,method) \ +if(status) { \ + fprintf(stderr,"%s error %s",(message),strerror((status))); \ + fprintf(stderr," %s\n",method); \ + fprintf(stderr,"epicsThreadInit cant proceed. Program exiting\n"); \ + exit(-1);\ +} + + +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) +static int getOssPriorityValue(epicsThreadOSD *pthreadInfo) +{ + double maxPriority,minPriority,slope,oss; + + if(pcommonAttr->maxPriority==pcommonAttr->minPriority) + return(pcommonAttr->maxPriority); + maxPriority = (double)pcommonAttr->maxPriority; + minPriority = (double)pcommonAttr->minPriority; + slope = (maxPriority - minPriority)/100.0; + oss = (double)pthreadInfo->osiPriority * slope + minPriority; + return((int)oss); +} +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ +static void setSchedulingPolicy(epicsThreadOSD *pthreadInfo,int policy) +{ +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) + int status; + + status = pthread_attr_getschedparam( + &pthreadInfo->attr,&pthreadInfo->schedParam); + checkStatusOnce(status,"pthread_attr_getschedparam"); + pthreadInfo->schedParam.sched_priority = getOssPriorityValue(pthreadInfo); + status = pthread_attr_setschedpolicy( + &pthreadInfo->attr,policy); + checkStatusOnce(status,"pthread_attr_setschedpolicy"); + status = pthread_attr_setschedparam( + &pthreadInfo->attr,&pthreadInfo->schedParam); + checkStatusOnce(status,"pthread_attr_setschedparam"); + status = pthread_attr_setinheritsched( + &pthreadInfo->attr,PTHREAD_EXPLICIT_SCHED); + checkStatusOnce(status,"pthread_attr_setinheritsched"); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ +} + +static epicsThreadOSD * create_threadInfo(const char *name) +{ + epicsThreadOSD *pthreadInfo; + + pthreadInfo = callocMustSucceed(1,sizeof(*pthreadInfo),"create_threadInfo"); + pthreadInfo->suspendEvent = epicsEventMustCreate(epicsEventEmpty); + pthreadInfo->name = epicsStrDup(name); + return pthreadInfo; +} + +static epicsThreadOSD * init_threadInfo(const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + epicsThreadOSD *pthreadInfo; + int status; + + pthreadInfo = create_threadInfo(name); + pthreadInfo->createFunc = funptr; + pthreadInfo->createArg = parm; + status = pthread_attr_init(&pthreadInfo->attr); + checkStatusOnce(status,"pthread_attr_init"); + if(status) return 0; + status = pthread_attr_setdetachstate( + &pthreadInfo->attr, PTHREAD_CREATE_DETACHED); + checkStatusOnce(status,"pthread_attr_setdetachstate"); +#if defined (_POSIX_THREAD_ATTR_STACKSIZE) +#if ! defined (OSITHREAD_USE_DEFAULT_STACK) + status = pthread_attr_setstacksize( &pthreadInfo->attr,(size_t)stackSize); + checkStatusOnce(status,"pthread_attr_setstacksize"); +#endif /*OSITHREAD_USE_DEFAULT_STACK*/ +#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/ + status = pthread_attr_setscope(&pthreadInfo->attr,PTHREAD_SCOPE_PROCESS); + if(errVerbose) checkStatusOnce(status,"pthread_attr_setscope"); + pthreadInfo->osiPriority = priority; + return(pthreadInfo); +} + +static void free_threadInfo(epicsThreadOSD *pthreadInfo) +{ + int status; + + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","free_threadInfo"); + if(pthreadInfo->isOnThreadList) ellDelete(&pthreadList,&pthreadInfo->node); + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","free_threadInfo"); + epicsEventDestroy(pthreadInfo->suspendEvent); + status = pthread_attr_destroy(&pthreadInfo->attr); + checkStatusQuit(status,"pthread_attr_destroy","free_threadInfo"); + free(pthreadInfo->name); + free(pthreadInfo); +} + +static void once(void) +{ + epicsThreadOSD *pthreadInfo; + int status; + + pthread_key_create(&getpthreadInfo,0); + status = pthread_mutex_init(&onceLock,0); + checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit"); + status = pthread_mutex_init(&listLock,0); + checkStatusQuit(status,"pthread_mutex_init","epicsThreadInit"); + pcommonAttr = calloc(1,sizeof(commonAttr)); + if(!pcommonAttr) checkStatusOnceQuit(errno,"calloc","epicsThreadInit"); + status = pthread_attr_init(&pcommonAttr->attr); + checkStatusOnceQuit(status,"pthread_attr_init","epicsThreadInit"); + status = pthread_attr_setdetachstate( + &pcommonAttr->attr, PTHREAD_CREATE_DETACHED); + checkStatusOnce(status,"pthread_attr_setdetachstate"); + status = pthread_attr_setscope(&pcommonAttr->attr,PTHREAD_SCOPE_PROCESS); + if(errVerbose) checkStatusOnce(status,"pthread_attr_setscope"); +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) + status = pthread_attr_setschedpolicy( + &pcommonAttr->attr,SCHED_FIFO); + checkStatusOnce(status,"pthread_attr_setschedpolicy"); + status = pthread_attr_getschedpolicy( + &pcommonAttr->attr,&pcommonAttr->schedPolicy); + checkStatusOnce(status,"pthread_attr_getschedpolicy"); + status = pthread_attr_getschedparam( + &pcommonAttr->attr,&pcommonAttr->schedParam); + checkStatusOnce(status,"pthread_attr_getschedparam"); + pcommonAttr->maxPriority = sched_get_priority_max(pcommonAttr->schedPolicy); + if(pcommonAttr->maxPriority == -1) { + pcommonAttr->maxPriority = pcommonAttr->schedParam.sched_priority; + fprintf(stderr,"sched_get_priority_max failed set to %d\n", + pcommonAttr->maxPriority); + } + pcommonAttr->minPriority = sched_get_priority_min(pcommonAttr->schedPolicy); + if(pcommonAttr->minPriority == -1) { + pcommonAttr->minPriority = pcommonAttr->schedParam.sched_priority; + fprintf(stderr,"sched_get_priority_min failed set to %d\n", + pcommonAttr->maxPriority); + } +#else + if(errVerbose) fprintf(stderr,"task priorities are not implemented\n"); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + pthreadInfo = init_threadInfo("_main_",0,epicsThreadGetStackSize(epicsThreadStackSmall),0,0); + status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo); + checkStatusOnceQuit(status,"pthread_setspecific","epicsThreadInit"); + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","epicsThreadInit"); + ellAdd(&pthreadList,&pthreadInfo->node); + pthreadInfo->isOnThreadList = 1; + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadInit"); + status = atexit(epicsExitCallAtExits); + checkStatusOnce(status,"atexit"); + epicsThreadOnceCalled = 1; +} + +static void * start_routine(void *arg) +{ + epicsThreadOSD *pthreadInfo = (epicsThreadOSD *)arg; + int status; + int oldtype; + sigset_t blockAllSig; + + sigfillset(&blockAllSig); + pthread_sigmask(SIG_SETMASK,&blockAllSig,NULL); + status = pthread_setspecific(getpthreadInfo,arg); + checkStatusQuit(status,"pthread_setspecific","start_routine"); + status = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&oldtype); + checkStatusQuit(status,"pthread_setcanceltype","start_routine"); + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","start_routine"); + ellAdd(&pthreadList,&pthreadInfo->node); + pthreadInfo->isOnThreadList = 1; + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","start_routine"); + + (*pthreadInfo->createFunc)(pthreadInfo->createArg); + + epicsExitCallAtThreadExits (); + + free_threadInfo(pthreadInfo); + return(0); +} + +static void epicsThreadInit(void) +{ + static pthread_once_t once_control = PTHREAD_ONCE_INIT; + int status = pthread_once(&once_control,once); + checkStatusQuit(status,"pthread_once","epicsThreadInit"); +} + + +#define ARCH_STACK_FACTOR 1024 + + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass) +{ +#if ! defined (_POSIX_THREAD_ATTR_STACKSIZE) + return 0; +#elif defined (OSITHREAD_USE_DEFAULT_STACK) + return 0; +#else + static const unsigned stackSizeTable[epicsThreadStackBig+1] = + {128*ARCH_STACK_FACTOR, 256*ARCH_STACK_FACTOR, 512*ARCH_STACK_FACTOR}; + if (stackSizeClassepicsThreadStackBig) { + errlogPrintf("epicsThreadGetStackSize illegal argument (too large)"); + return stackSizeTable[epicsThreadStackBig]; + } + + return stackSizeTable[stackSizeClass]; +#endif /*_POSIX_THREAD_ATTR_STACKSIZE*/ +} + +epicsShareFunc void epicsShareAPI epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg) +{ + static struct epicsThreadOSD threadOnceComplete; + #define EPICS_THREAD_ONCE_DONE &threadOnceComplete + int status; + + epicsThreadInit(); + status = mutexLock(&onceLock); + if(status) { + fprintf(stderr,"epicsThreadOnce: pthread_mutex_lock returned %s.\n", + strerror(status)); + exit(-1); + } + + if (*id != EPICS_THREAD_ONCE_DONE) { + if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); + func(arg); + status = mutexLock(&onceLock); + checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if (*id == epicsThreadGetIdSelf()) { + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); + cantProceed("Recursive epicsThreadOnce() initialization\n"); + } else + while (*id != EPICS_THREAD_ONCE_DONE) { + /* Another thread is in the above func(arg) call. */ + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce"); + epicsThreadSleep(epicsThreadSleepQuantum()); + status = mutexLock(&onceLock); + checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce"); + } + } + status = pthread_mutex_unlock(&onceLock); + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadOnce"); +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadCreate(const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + epicsThreadOSD *pthreadInfo; + int status; + sigset_t blockAllSig, oldSig; + + epicsThreadInit(); + assert(pcommonAttr); + sigfillset(&blockAllSig); + pthread_sigmask(SIG_SETMASK,&blockAllSig,&oldSig); + pthreadInfo = init_threadInfo(name,priority,stackSize,funptr,parm); + if(pthreadInfo==0) return 0; + pthreadInfo->isEpicsThread = 1; + setSchedulingPolicy(pthreadInfo,SCHED_FIFO); + pthreadInfo->isFifoScheduled = 1; + status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr, + start_routine,pthreadInfo); + if(status==EPERM){ + /* Try again without SCHED_FIFO*/ + free_threadInfo(pthreadInfo); + pthreadInfo = init_threadInfo(name,priority,stackSize,funptr,parm); + if(pthreadInfo==0) return 0; + pthreadInfo->isEpicsThread = 1; + status = pthread_create(&pthreadInfo->tid,&pthreadInfo->attr, + start_routine,pthreadInfo); + } + checkStatusOnce(status,"pthread_create"); + if(status) { + free_threadInfo(pthreadInfo); + return 0; + } + status = pthread_sigmask(SIG_SETMASK,&oldSig,NULL); + checkStatusOnce(status,"pthread_sigmask"); + return(pthreadInfo); +} + +/* + * Cleanup routine for threads not created by epicsThreadCreate(). + */ +/* static void nonEPICSthreadCleanup(void *arg) +{ + epicsThreadOSD *pthreadInfo = (epicsThreadOSD *)arg; + + free(pthreadInfo->name); + free(pthreadInfo); +} */ + +/* + * Create dummy context for threads not created by epicsThreadCreate(). + */ +static epicsThreadOSD *createImplicit(void) +{ + epicsThreadOSD *pthreadInfo; + char name[64]; + pthread_t tid; + int status; + + tid = pthread_self(); + sprintf(name, "non-EPICS_%d", (int)tid); + pthreadInfo = create_threadInfo(name); + pthreadInfo->tid = tid; + pthreadInfo->osiPriority = 0; +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) + { + struct sched_param param; + int policy; + if(pthread_getschedparam(tid,&policy,¶m) == 0) + pthreadInfo->osiPriority = + (param.sched_priority - pcommonAttr->minPriority) * 100.0 / + (pcommonAttr->maxPriority - pcommonAttr->minPriority + 1); + } +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + status = pthread_setspecific(getpthreadInfo,(void *)pthreadInfo); + checkStatusQuit(status,"pthread_setspecific","createImplicit"); +/* pthread_cleanup_push(nonEPICSthreadCleanup, pthreadInfo); */ + return pthreadInfo; +} + +epicsShareFunc void epicsShareAPI epicsThreadSuspendSelf(void) +{ + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + pthreadInfo->isSuspended = 1; + epicsEventMustWait(pthreadInfo->suspendEvent); +} + +epicsShareFunc void epicsShareAPI epicsThreadResume(epicsThreadOSD *pthreadInfo) +{ + assert(epicsThreadOnceCalled); + pthreadInfo->isSuspended = 0; + epicsEventSignal(pthreadInfo->suspendEvent); +} + +epicsShareFunc void epicsShareAPI epicsThreadExitMain(void) +{ + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + if(pthreadInfo->createFunc) { + errlogPrintf("called from non-main thread\n"); + cantProceed("epicsThreadExitMain"); + } + else { + free_threadInfo(pthreadInfo); + pthread_exit(0); + } +} + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPriority(epicsThreadId pthreadInfo) +{ + assert(epicsThreadOnceCalled); + return(pthreadInfo->osiPriority); +} + +epicsShareFunc unsigned int epicsShareAPI epicsThreadGetPrioritySelf(void) +{ + epicsThreadInit(); + return(epicsThreadGetPriority(epicsThreadGetIdSelf())); +} + +epicsShareFunc void epicsShareAPI epicsThreadSetPriority(epicsThreadId pthreadInfo,unsigned int priority) +{ +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) + int status; +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + + assert(epicsThreadOnceCalled); + assert(pthreadInfo); + if(!pthreadInfo->isEpicsThread) { + fprintf(stderr,"epicsThreadSetPriority called by non epics thread\n"); + return; + } + pthreadInfo->osiPriority = priority; + if(!pthreadInfo->isFifoScheduled) return; +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) + pthreadInfo->schedParam.sched_priority = getOssPriorityValue(pthreadInfo); + status = pthread_attr_setschedparam( + &pthreadInfo->attr,&pthreadInfo->schedParam); + if(errVerbose) checkStatus(status,"pthread_attr_setschedparam"); + status = pthread_setschedparam( + pthreadInfo->tid,pcommonAttr->schedPolicy,&pthreadInfo->schedParam); + if(errVerbose) checkStatus(status,"pthread_setschedparam"); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ +} + +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadHighestPriorityLevelBelow( + unsigned int priority, unsigned *pPriorityJustBelow) +{ + unsigned newPriority = priority - 1; +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) + int diff; + diff = pcommonAttr->maxPriority - pcommonAttr->minPriority; + if(diff<0) diff = -diff; + if(diff>1 && diff <100) newPriority -= 100/(diff+1); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + if (newPriority <= 99) { + *pPriorityJustBelow = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +epicsShareFunc epicsThreadBooleanStatus epicsShareAPI epicsThreadLowestPriorityLevelAbove( + unsigned int priority, unsigned *pPriorityJustAbove) +{ + unsigned newPriority = priority + 1; + +#if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) + int diff; + diff = pcommonAttr->maxPriority - pcommonAttr->minPriority; + if(diff<0) diff = -diff; + if(diff>1 && diff <100) newPriority += 100/(diff+1); +#endif /* _POSIX_THREAD_PRIORITY_SCHEDULING */ + if (newPriority <= 99) { + *pPriorityJustAbove = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +epicsShareFunc int epicsShareAPI epicsThreadIsEqual(epicsThreadId p1, epicsThreadId p2) +{ + assert(epicsThreadOnceCalled); + assert(p1); + assert(p2); + return(pthread_equal(p1->tid,p2->tid)); +} + +epicsShareFunc int epicsShareAPI epicsThreadIsSuspended(epicsThreadId pthreadInfo) { + assert(epicsThreadOnceCalled); + assert(pthreadInfo); + return(pthreadInfo->isSuspended ? 1 : 0); +} + +epicsShareFunc void epicsShareAPI epicsThreadSleep(double seconds) +{ + struct timespec delayTime; + struct timespec remainingTime; + double nanoseconds; + + delayTime.tv_sec = (time_t)seconds; + nanoseconds = (seconds - (double)delayTime.tv_sec) *1e9; + delayTime.tv_nsec = (long)nanoseconds; + while (nanosleep(&delayTime, &remainingTime) == -1 && + errno == EINTR) + delayTime = remainingTime; +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetIdSelf(void) { + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + assert ( pthreadInfo ); + return(pthreadInfo); +} + +epicsShareFunc pthread_t epicsShareAPI epicsThreadGetPosixThreadId ( epicsThreadId threadId ) +{ + return threadId->tid; +} + +epicsShareFunc epicsThreadId epicsShareAPI epicsThreadGetId(const char *name) { + epicsThreadOSD *pthreadInfo; + int status; + + assert(epicsThreadOnceCalled); + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","epicsThreadGetId"); + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while(pthreadInfo) { + if(strcmp(name,pthreadInfo->name) == 0) break; + pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); + } + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadGetId"); + + return(pthreadInfo); +} + +epicsShareFunc const char epicsShareAPI *epicsThreadGetNameSelf() +{ + epicsThreadOSD *pthreadInfo; + + epicsThreadInit(); + pthreadInfo = (epicsThreadOSD *)pthread_getspecific(getpthreadInfo); + if(pthreadInfo==NULL) + pthreadInfo = createImplicit(); + return(pthreadInfo->name); +} + +epicsShareFunc void epicsShareAPI epicsThreadGetName(epicsThreadId pthreadInfo, char *name, size_t size) +{ + assert(epicsThreadOnceCalled); + strncpy(name, pthreadInfo->name, size-1); + name[size-1] = '\0'; +} + +static void showThreadInfo(epicsThreadOSD *pthreadInfo,unsigned int level) +{ + if(!pthreadInfo) { + fprintf(epicsGetStdout()," NAME EPICS ID " + "PTHREAD ID OSIPRI OSSPRI STATE\n"); + } else { + struct sched_param param; + int policy; + int priority = 0; + + if(pthreadInfo->tid) { + int status; + status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m); + if(!status) priority = param.sched_priority; + } + fprintf(epicsGetStdout(),"%16.16s %12p %12lu %3d%8d %8.8s\n", + pthreadInfo->name,(void *) + pthreadInfo,(unsigned long)pthreadInfo->tid, + pthreadInfo->osiPriority,priority, + pthreadInfo->isSuspended?"SUSPEND":"OK"); + } +} + +epicsShareFunc void epicsShareAPI epicsThreadShowAll(unsigned int level) +{ + epicsThreadOSD *pthreadInfo; + int status; + + epicsThreadInit(); + epicsThreadShow(0,level); + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","epicsThreadShowAll"); + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while(pthreadInfo) { + showThreadInfo(pthreadInfo,level); + pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); + } + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadShowAll"); +} + +epicsShareFunc void epicsShareAPI epicsThreadShow(epicsThreadId showThread, unsigned int level) +{ + epicsThreadOSD *pthreadInfo; + int status; + int found = 0; + + epicsThreadInit(); + if(!showThread) { + showThreadInfo(0,level); + return; + } + status = mutexLock(&listLock); + checkStatusQuit(status,"pthread_mutex_lock","epicsThreadShowAll"); + pthreadInfo=(epicsThreadOSD *)ellFirst(&pthreadList); + while(pthreadInfo) { + if (((epicsThreadId)pthreadInfo == showThread) + || ((epicsThreadId)pthreadInfo->tid == showThread)) { + found = 1; + showThreadInfo(pthreadInfo,level); + } + pthreadInfo=(epicsThreadOSD *)ellNext(&pthreadInfo->node); + } + status = pthread_mutex_unlock(&listLock); + checkStatusQuit(status,"pthread_mutex_unlock","epicsThreadShowAll"); + if (!found) + printf("Thread %#lx (%lu) not found.\n", (unsigned long)showThread, (unsigned long)showThread); +} + + +epicsShareFunc epicsThreadPrivateId epicsShareAPI epicsThreadPrivateCreate(void) +{ + pthread_key_t *key; + int status; + + epicsThreadInit(); + key = callocMustSucceed(1,sizeof(pthread_key_t),"epicsThreadPrivateCreate"); + status = pthread_key_create(key,0); + checkStatusQuit(status,"pthread_key_create","epicsThreadPrivateCreate"); + return((epicsThreadPrivateId)key); +} + +epicsShareFunc void epicsShareAPI epicsThreadPrivateDelete(epicsThreadPrivateId id) +{ + pthread_key_t *key = (pthread_key_t *)id; + int status; + + assert(epicsThreadOnceCalled); + status = pthread_key_delete(*key); + checkStatusQuit(status,"pthread_key_delete","epicsThreadPrivateDelete"); + free((void *)key); +} + +epicsShareFunc void epicsShareAPI epicsThreadPrivateSet (epicsThreadPrivateId id, void *value) +{ + pthread_key_t *key = (pthread_key_t *)id; + int status; + + assert(epicsThreadOnceCalled); + if(errVerbose && !value) + errlogPrintf("epicsThreadPrivateSet: setting value of 0\n"); + status = pthread_setspecific(*key,value); + checkStatusQuit(status,"pthread_setspecific","epicsThreadPrivateSet"); +} + +epicsShareFunc void epicsShareAPI *epicsThreadPrivateGet(epicsThreadPrivateId id) +{ + pthread_key_t *key = (pthread_key_t *)id; + + assert(epicsThreadOnceCalled); + return pthread_getspecific(*key); +} + +epicsShareFunc double epicsShareAPI epicsThreadSleepQuantum () +{ + double hz; + hz = sysconf ( _SC_CLK_TCK ); + return 1.0 / hz; +} + diff --git a/src/libCom/osi/os/posix/osdThread.h b/src/libCom/osi/os/posix/osdThread.h new file mode 100644 index 000000000..99cfe319a --- /dev/null +++ b/src/libCom/osi/os/posix/osdThread.h @@ -0,0 +1,27 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef osdThreadh +#define osdThreadh + +#include + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc pthread_t epicsShareAPI epicsThreadGetPosixThreadId ( epicsThreadId id ); + +#ifdef __cplusplus +} +#endif + +#endif /* osdThreadh */ diff --git a/src/libCom/osi/os/posix/osdTime.cpp b/src/libCom/osi/os/posix/osdTime.cpp new file mode 100644 index 000000000..62939a427 --- /dev/null +++ b/src/libCom/osi/os/posix/osdTime.cpp @@ -0,0 +1,118 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include + +#include "osiSock.h" + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "epicsTime.h" +#include "generalTimeSup.h" + +#ifdef CLOCK_REALTIME + #include "osiClockTime.h" + + #define TIME_INIT ClockTime_Init(CLOCKTIME_NOSYNC) +#else + /* Some posix systems like Darwin don't have CLOCK_REALTIME */ + + #define TIME_INIT generalTimeCurrentTpRegister("GetTimeOfDay", \ + LAST_RESORT_PRIORITY, osdTimeGetCurrent) + + extern "C" { + static int osdTimeGetCurrent (epicsTimeStamp *pDest) + { + struct timeval tv; + struct timezone tz; + + if (gettimeofday (&tv, &tz)) + return epicsTimeERROR; + + *pDest = epicsTime(tv); + return epicsTimeOK; + } + } // extern "C" +#endif + +#ifdef CYGWIN32 +int clock_settime(clockid_t clock, const timespec *tp) +{ + return -EFAULT; +} +#endif + + +static int timeRegister(void) +{ + TIME_INIT; + return 1; +} +static int done = timeRegister(); + + +int epicsTime_gmtime ( const time_t *pAnsiTime, // X aCC 361 + struct tm *pTM ) +{ + struct tm * pRet = gmtime_r ( pAnsiTime, pTM ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return epicsTimeERROR; + } +} + +int epicsTime_localtime ( const time_t *clock, // X aCC 361 + struct tm *result ) +{ + struct tm * pRet = localtime_r ( clock, result ); + if ( pRet ) { + return epicsTimeOK; + } + else { + return epicsTimeERROR; + } +} + +extern "C" epicsShareFunc void + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime) +{ + struct timespec wait; + int status; + + if(timeout<0.0) timeout = 0.0; + else if(timeout>3600.0) timeout = 3600.0; +#ifdef CLOCK_REALTIME + status = clock_gettime(CLOCK_REALTIME, wakeTime); +#else + { + struct timeval tv; + struct timezone tz; + status = gettimeofday(&tv, &tz); + wakeTime->tv_sec = tv.tv_sec; + wakeTime->tv_nsec = tv.tv_usec * 1000; + } +#endif + if (status) { + perror("convertDoubleToWakeTime"); + cantProceed("convertDoubleToWakeTime"); + } + wait.tv_sec = static_cast< long >(timeout); + wait.tv_nsec = static_cast< long >((timeout - (double)wait.tv_sec) * 1e9); + wakeTime->tv_sec += wait.tv_sec; + wakeTime->tv_nsec += wait.tv_nsec; + if (wakeTime->tv_nsec >= 1000000000L) { + wakeTime->tv_nsec -= 1000000000L; + ++wakeTime->tv_sec; + } +} diff --git a/src/libCom/osi/os/posix/osdTime.h b/src/libCom/osi/os/posix/osdTime.h new file mode 100644 index 000000000..cb18e58ba --- /dev/null +++ b/src/libCom/osi/os/posix/osdTime.h @@ -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. +\*************************************************************************/ + +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author: Jeff Hill + */ + +#ifndef osdTimeh +#define osdTimeh + +#include + +#ifndef _POSIX_TIMERS + struct timespec { + time_t tv_sec; /* seconds since some epoch */ + long tv_nsec; /* nanoseconds within the second */ + }; +#endif /* ifndef _POSIX_TIMERS */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +epicsShareFunc void epicsShareAPI + convertDoubleToWakeTime(double timeout,struct timespec *wakeTime); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ifndef osdTimeh */ + diff --git a/src/libCom/osi/os/posix/osiUnistd.h b/src/libCom/osi/os/posix/osiUnistd.h new file mode 100644 index 000000000..530b7fd89 --- /dev/null +++ b/src/libCom/osi/os/posix/osiUnistd.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* 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 + * + * + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 1986, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include diff --git a/src/libCom/osi/os/posix/systemCallIntMech.cpp b/src/libCom/osi/os/posix/systemCallIntMech.cpp new file mode 100644 index 000000000..6aa0cccee --- /dev/null +++ b/src/libCom/osi/os/posix/systemCallIntMech.cpp @@ -0,0 +1,23 @@ + +/*************************************************************************\ +* 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 + */ + +#define epicsExportSharedSymbols +#include "osiSock.h" + +enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery () +{ + return esscimqi_socketBothShutdownRequired; +} diff --git a/src/libCom/osi/os/solaris/epicsMath.h b/src/libCom/osi/os/solaris/epicsMath.h new file mode 100644 index 000000000..1cf60b5db --- /dev/null +++ b/src/libCom/osi/os/solaris/epicsMath.h @@ -0,0 +1,31 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne, LLC as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_epicsMath_H +#define INC_epicsMath_H + +#include +#include +#include + +#ifndef isinf +# define isinf(x) (((x)==(x)) && !finite((x))) +/* same as (!isnan(x) && !finite(x)) */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* INC_epicsMath_H */ diff --git a/src/libCom/osi/os/solaris/osdFindSymbol.c b/src/libCom/osi/os/solaris/osdFindSymbol.c new file mode 100644 index 000000000..967c220cd --- /dev/null +++ b/src/libCom/osi/os/solaris/osdFindSymbol.c @@ -0,0 +1,27 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/default/epicsFindSymbol.c */ + +#include + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + return dlopen(name, RTLD_LAZY | RTLD_GLOBAL); +} + +epicsShareFunc const char *epicsLoadError(void) +{ + return dlerror(); +} + +epicsShareFunc void * epicsShareAPI epicsFindSymbol(const char *name) +{ + return dlsym(0, name); +} diff --git a/src/libCom/osi/os/solaris/osdSock.h b/src/libCom/osi/os/solaris/osdSock.h new file mode 100644 index 000000000..93cd5eeab --- /dev/null +++ b/src/libCom/osi/os/solaris/osdSock.h @@ -0,0 +1,95 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * Solaris specific socket include + */ + +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* close() and others */ + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#define socket_ioctl(A,B,C) ioctl(A,B,C) +typedef int osiSockIoctl_t; + +#if SOLARIS > 6 || defined ( _SOCKLEN_T ) + typedef uint32_t osiSocklen_t; +#else + typedef int osiSocklen_t; +#endif + +#define DOES_NOT_ACCEPT_ZERO_LENGTH_UDP + +#define FD_IN_FDSET(FD) ((FD)=0) + +#define SOCK_EWOULDBLOCK EWOULDBLOCK +#define SOCK_ENOBUFS ENOBUFS +#define SOCK_ECONNRESET ECONNRESET +#define SOCK_ETIMEDOUT ETIMEDOUT +#define SOCK_EADDRINUSE EADDRINUSE +#define SOCK_ECONNREFUSED ECONNREFUSED +#define SOCK_ECONNABORTED ECONNABORTED +#define SOCK_EINPROGRESS EINPROGRESS +#define SOCK_EISCONN EISCONN +#define SOCK_EALREADY EALREADY +#define SOCK_EINVAL EINVAL +#define SOCK_EINTR EINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE EMFILE +#define SOCK_SHUTDOWN ESHUTDOWN +#define SOCK_ENOTSOCK ENOTSOCK +#define SOCK_EBADF EBADF + +#ifndef SHUT_RD +# define SHUT_RD 0 +#endif + +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif + +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +#ifndef INADDR_NONE +# define INADDR_NONE (0xffffffff) +#endif + +#define ifreq_size(pifreq) (sizeof(pifreq->ifr_name)) + +#endif /*osdSockH*/ + diff --git a/src/libCom/osi/os/solaris/osdStrtod.h b/src/libCom/osi/os/solaris/osdStrtod.h new file mode 100644 index 000000000..28fd36b9d --- /dev/null +++ b/src/libCom/osi/os/solaris/osdStrtod.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +epicsShareFunc double epicsStrtod(const char *str, char **endp); diff --git a/src/libCom/osi/os/solaris/osdWireConfig.h b/src/libCom/osi/os/solaris/osdWireConfig.h new file mode 100644 index 000000000..277fe395e --- /dev/null +++ b/src/libCom/osi/os/solaris/osdWireConfig.h @@ -0,0 +1,26 @@ +/* + * Solaris version of + * osdWireConfig.h + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +#include + +#if defined ( _LITTLE_ENDIAN ) +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +#elif defined ( _BIG_ENDIAN ) +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +#else +# error EPICS hasnt been ported to byte order specified by on Solaris +#endif + +/* for now, assume that Solaris doesnt run on weird arch like ARM NWFP */ +#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER + +#endif /* ifdef osdWireConfig_h */ + diff --git a/src/libCom/osi/os/solaris/osiFileName.h b/src/libCom/osi/os/solaris/osiFileName.h new file mode 100644 index 000000000..b79203992 --- /dev/null +++ b/src/libCom/osi/os/solaris/osiFileName.h @@ -0,0 +1,21 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/vxWorks/README b/src/libCom/osi/os/vxWorks/README new file mode 100644 index 000000000..bbc0046b3 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/README @@ -0,0 +1,3 @@ + +The source files here are vxWorks dependent. + diff --git a/src/libCom/osi/os/vxWorks/atReboot.cpp b/src/libCom/osi/os/vxWorks/atReboot.cpp new file mode 100644 index 000000000..cb4bff5cc --- /dev/null +++ b/src/libCom/osi/os/vxWorks/atReboot.cpp @@ -0,0 +1,49 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* atReboot.cpp */ + +/* Author: Marty Kraimer Date: 30AUG2003 */ + +#include + +#include "epicsDynLink.h" +#include "epicsExit.h" + +/* osdThread references atRebootExtern just to make this module load*/ +int atRebootExtern; + +typedef int (*sysAtReboot_t)(void(func)(void)); + +class atRebootRegister { +public: + atRebootRegister(); +}; + +atRebootRegister::atRebootRegister() +{ + STATUS status; + sysAtReboot_t sysAtReboot; + SYM_TYPE type; + + status = symFindByNameEPICS(sysSymTbl, "_sysAtReboot", + (char **)&sysAtReboot, &type); + if (status == OK) { + status = sysAtReboot(epicsExitCallAtExits); + if (status != OK) { + printf("atReboot: sysAtReboot returned error %d\n", status); + } + } else { + printf("BSP routine sysAtReboot() not found, epicsExit() will not be\n" + "called by reboot. For reduced functionality, call\n" + " rebootHookAdd(epicsExitCallAtExits)\n"); + } +} + +static atRebootRegister atRebootRegisterObj; diff --git a/src/libCom/osi/os/vxWorks/camacLib.h b/src/libCom/osi/os/vxWorks/camacLib.h new file mode 100644 index 000000000..e3c181c97 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/camacLib.h @@ -0,0 +1,59 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* camacLib.h -- Prototypes for camacLib.o + * + * Marty Wise + * 10/11/93 + * + */ + +static char ht2992_h_RCSID[] = "Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd"; + +/********************************/ +/* GLOBAL DATA */ +/********************************/ +extern int debug_hook; + +extern struct glob_dat { + int total; + int read_error[5]; + int write_error[5]; + int cmd_error[5]; + int total_err; + int lam_count[12]; +} debug_dat; + + +/********************************/ +/* FUNCTION PROTOTYPES */ +/********************************/ + +void cdreg(int *ext, int b, int c, int n, int a); +void cfsa(int f, int ext, int *dat, int *q); +void cssa(int f, int ext, short *dat, int *q); +void ccci(int ext, int l); +void cccz(int ext); +void cccc(int ext); +void ccinit(int b); +void ctci(int ext, int *l); +void cgreg(int ext, int *b, int *c, int *n, int *a); +void cfmad(int f, int extb[2], int *intc, int cb[4]); +void cfubc(int f, int ext, int *intc, int cb[4]); +void cfubc(int f, int ext, int *intc, int cb[4]); +void csmad(int f, int extb[2], short *intc, int cb[4]); +void ctcd(int ext, int *l); +void cccd(int ext, int l); +void csga(int fa[], int exta[], unsigned short intc[], int qa[], int cb[4]); +void cfga(int fa[], int exta[], int intc[], int qa[], int cb[4]); +void cfubr(int f, int ext, int intc[], int cb[4]); +void csubc(int f, int ext, unsigned short *intc, int cb[4]); +void csubr(int f, int ext, int intc[], int cb[4]); +void print_reg(int ext); + diff --git a/src/libCom/osi/os/vxWorks/devLibVMEOSD.c b/src/libCom/osi/os/vxWorks/devLibVMEOSD.c new file mode 100644 index 000000000..42d42b91f --- /dev/null +++ b/src/libCom/osi/os/vxWorks/devLibVMEOSD.c @@ -0,0 +1,502 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 + * + * Archictecture dependent support for common device driver resources + * + * Author: Jeff Hill + * Date: 10-30-98 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsFindSymbol.h" +#include "devLibVME.h" +#include "errlog.h" + +typedef void myISR (void *pParam); + +#if CPU_FAMILY != PPC +/* + * A list of the names of the unexpected interrupt handlers + * ( some of these are provided by wrs ) + */ +static char *defaultHandlerNames[] = { + "excStub", + "excIntStub", + "unsolicitedHandlerEPICS"}; + +static myISR *defaultHandlerAddr[NELEMENTS(defaultHandlerNames)]; +#endif + +static myISR *isrFetch(unsigned vectorNumber); + +/* + * this routine needs to be in the symbol table + * for this code to work correctly + */ +static void unsolicitedHandlerEPICS(int vectorNumber); + +/* + * this is in veclist.c + */ +int cISRTest(void (*)(), void (**)(), void **); + +/* + * Make sure that the CR/CSR addressing mode is defined. + * (it may not be in older versions of vxWorks) + */ +#ifndef VME_AM_CSR +# define VME_AM_CSR (0x2f) +#endif + +/* + * we use a translation between an EPICS encoding + * and a vxWorks encoding here + * to reduce dependency of drivers on vxWorks + * + * we assume that the BSP are configured to use these + * address modes by default + */ + +#define EPICSAddrTypeNoConvert -1 + +int EPICStovxWorksAddrType[] + = { + VME_AM_SUP_SHORT_IO, + VME_AM_STD_SUP_DATA, + VME_AM_EXT_SUP_DATA, + EPICSAddrTypeNoConvert, + VME_AM_CSR + }; + +#if CPU_FAMILY != PPC +static void initHandlerAddrList(void); +#endif + +/* + * maps logical address to physical address, but does not detect + * two device drivers that are using the same address range + */ +static long vxDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress); + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue); + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isnt present + */ +static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue); + +static void *devA24Malloc(size_t size); +static void devA24Free(void *pBlock); +static long devInit(void) { return 0;} + +static long vxDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter); + +static long vxDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +); + +static long vxDevEnableInterruptLevelVME (unsigned level); + +static long vxDevDisableInterruptLevelVME (unsigned level); + +static int vxDevInterruptInUseVME (unsigned vectorNumber); + +/* + * used by dynamic bind in devLib.c + */ +static devLibVME vxVirtualOS = { + vxDevMapAddr, vxDevReadProbe, vxDevWriteProbe, + vxDevConnectInterruptVME, vxDevDisconnectInterruptVME, + vxDevEnableInterruptLevelVME, vxDevDisableInterruptLevelVME, + devA24Malloc,devA24Free,devInit,vxDevInterruptInUseVME +}; +devLibVME *pdevLibVME = &vxVirtualOS; + +/* + * devConnectInterruptVME + * + * wrapper to minimize driver dependency on vxWorks + */ +static long vxDevConnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)(), + void *parameter) +{ + int status; + + + if (devInterruptInUseVME(vectorNumber)) { + return S_dev_vectorInUse; + } + status = intConnect( + (void *)INUM_TO_IVEC(vectorNumber), + pFunction, + (int) parameter); + if (status<0) { + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * + * vxDevDisconnectInterruptVME() + * + * wrapper to minimize driver dependency on vxWorks + * + * The parameter pFunction should be set to the C function pointer that + * was connected. It is used as a key to prevent a driver from removing + * an interrupt handler that was installed by another driver + * + */ +static long vxDevDisconnectInterruptVME ( + unsigned vectorNumber, + void (*pFunction)() +) +{ + void (*psub)(); + int status; + +# if CPU_FAMILY == PPC + return S_dev_vecInstlFail; +# endif + + /* + * If pFunction not connected to this vector + * then they are probably disconnecting from the wrong vector + */ + psub = isrFetch(vectorNumber); + if(psub != pFunction){ + return S_dev_vectorNotInUse; + } + + status = intConnect( + (void *)INUM_TO_IVEC(vectorNumber), + unsolicitedHandlerEPICS, + (int) vectorNumber); + if(status<0){ + return S_dev_vecInstlFail; + } + + return 0; +} + +/* + * enable VME interrupt level + */ +static long vxDevEnableInterruptLevelVME (unsigned level) +{ +# if CPU_FAMILY != I80X86 + int s; + s = sysIntEnable (level); + if (s!=OK) { + return S_dev_intEnFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * enable ISA interrupt level + */ +long devEnableInterruptLevelISA (unsigned level) +{ +# if CPU_FAMILY == I80X86 + int s; + s = sysIntEnablePIC (level); + if (s!=OK) { + return S_dev_intEnFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * disable ISA interrupt level + */ +long devDisableInterruptLevelISA (unsigned level) +{ +# if CPU_FAMILY == I80X86 + int s; + s = sysIntDisablePIC (level); + if (s!=OK) { + return S_dev_intEnFail; + } +# else + return S_dev_intEnFail; +# endif + + return 0; +} + +/* + * disable VME interrupt level + */ +static long vxDevDisableInterruptLevelVME (unsigned level) +{ +# if CPU_FAMILY != I80X86 + int s; + s = sysIntDisable (level); + if (s!=OK) { + return S_dev_intDissFail; + } + return 0; +# else + return S_dev_intEnFail; +# endif +} + +/* + * vxDevMapAddr () + */ +static long vxDevMapAddr (epicsAddressType addrType, unsigned options, + size_t logicalAddress, size_t size, volatile void **ppPhysicalAddress) +{ + long status; + + if (ppPhysicalAddress==NULL) { + return S_dev_badArgument; + } + + if (EPICStovxWorksAddrType[addrType] == EPICSAddrTypeNoConvert) + { + *ppPhysicalAddress = (void *) logicalAddress; + } + else + { + status = sysBusToLocalAdrs (EPICStovxWorksAddrType[addrType], + (char *) logicalAddress, (char **)ppPhysicalAddress); + if (status) { + return S_dev_addrMapFail; + } + } + + return 0; +} + +/* + * a bus error safe "wordSize" read at the specified address which returns + * unsuccessful status if the device isn't present + */ +static long vxDevReadProbe (unsigned wordSize, volatile const void *ptr, void *pValue) +{ + long status; + + status = vxMemProbe ((char *)ptr, VX_READ, wordSize, (char *) pValue); + if (status!=OK) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * a bus error safe "wordSize" write at the specified address which returns + * unsuccessful status if the device isn't present + */ +static long vxDevWriteProbe (unsigned wordSize, volatile void *ptr, const void *pValue) +{ + long status; + + status = vxMemProbe ((char *)ptr, VX_WRITE, wordSize, (char *) pValue); + if (status!=OK) { + return S_dev_noDevice; + } + + return 0; +} + +/* + * isrFetch() + */ +static myISR *isrFetch(unsigned vectorNumber) +{ + myISR *psub; + myISR *pCISR; + void *pParam; + int s; + + /* + * fetch the handler or C stub attached at this vector + */ + psub = (myISR *) intVecGet((FUNCPTR *)INUM_TO_IVEC(vectorNumber)); + + if ( psub ) { + /* + * from libvxWorks/veclist.c + * + * checks to see if it is a C ISR + * and if so finds the function pointer and + * the parameter passed + */ + s = cISRTest(psub, &pCISR, &pParam); + if(!s){ + psub = pCISR; + } + } + + return psub; +} + +/* + * determine if a VME interrupt vector is in use + */ +static int vxDevInterruptInUseVME (unsigned vectorNumber) +{ +#if CPU_FAMILY == PPC + return FALSE; +#else + { + static int init; + int i; + myISR *psub; + + if (!init) { + initHandlerAddrList(); + init = TRUE; + } + + psub = isrFetch (vectorNumber); + + /* + * its a C routine. Does it match a default handler? + */ + for (i=0; i EPICS and avoid architecture knowledge + */ + +#include "epicsDynLink.h" + +STATUS symFindByNameEPICS( + SYMTAB_ID symTblId, + char *name, + char **ppvalue, + SYM_TYPE *pType ) +{ + static int leadingUnderscore = 1; + static int init = 0; + STATUS status = ERROR; + + if (!init) { + char *pSymValue; + SYM_TYPE type; + status = symFindByName ( symTblId, "symFindByNameEPICS", &pSymValue, &type ); + if (status==OK) { + leadingUnderscore = 0; + } + init = 1; + } + + if (name[0] != '_' || leadingUnderscore) { + status = symFindByName ( symTblId, name, ppvalue, pType ); + } + else { + status = symFindByName ( symTblId, (name+1), ppvalue, pType ); + } + + return status; +} + +STATUS symFindByNameAndTypeEPICS( + SYMTAB_ID symTblId, + char *name, + char **ppvalue, + SYM_TYPE *pType, + SYM_TYPE sType, + SYM_TYPE mask ) +{ + static int leadingUnderscore = 1; + static int init = 0; + STATUS status = ERROR; + + if (!init) { + char *pSymValue; + SYM_TYPE type; + status = symFindByName (symTblId, "symFindByNameAndTypeEPICS", &pSymValue, &type ); + if (status==OK) { + leadingUnderscore = 0; + } + init = 1; + } + + if (name[0] != '_' || leadingUnderscore) { + status = symFindByNameAndType ( symTblId, name, ppvalue, pType, sType, mask ); + } + else if (leadingUnderscore) { + status = symFindByNameAndType ( symTblId, (name+1), ppvalue, pType, sType, mask ); + } + + return status; +} + + + diff --git a/src/libCom/osi/os/vxWorks/epicsDynLink.h b/src/libCom/osi/os/vxWorks/epicsDynLink.h new file mode 100644 index 000000000..85c6c8dd0 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/epicsDynLink.h @@ -0,0 +1,51 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * These routines will eventually need to be made OS independent + * (currently this is vxWorks specific) + */ + +#ifndef epicsDynLinkh +#define epicsDynLinkh + +#ifdef symFindByName +#undef symFindByName +#endif + +#include "vxWorks.h" +#include "symLib.h" +#include "sysSymTbl.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +STATUS symFindByNameEPICS( + SYMTAB_ID symTblId, + char *name, + char **pvalue, + SYM_TYPE *pType); + +STATUS symFindByNameAndTypeEPICS( + SYMTAB_ID symTblId, + char *name, + char **pvalue, + SYM_TYPE *pType, + SYM_TYPE sType, + SYM_TYPE mask); + +#ifdef __cplusplus +} +#endif + +#endif /* ifdef epicsDynLinkh */ + diff --git a/src/libCom/osi/os/vxWorks/epicsMath.h b/src/libCom/osi/os/vxWorks/epicsMath.h new file mode 100644 index 000000000..e973d2d9c --- /dev/null +++ b/src/libCom/osi/os/vxWorks/epicsMath.h @@ -0,0 +1,39 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef epicsMathh +#define epicsMathh + +#include +#include +#include + +/* private/mathP.h defines NAN as 4, and uses its value in the + * isNan() macro. We need mathP.h for isInf(), but can create + * our own isnan() test. epicsMath.cpp requires that NAN either + * be undef or yield the NaN value, so this solves the issue. + */ +#undef NAN + +#define isnan(D) (!(D == D)) +#define isinf(D) isInf(D) +#define finite(D) (!isnan(D) && !isInf(D)) + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern float epicsNAN; +epicsShareExtern float epicsINF; + +#ifdef __cplusplus +} +#endif + +#endif /* epicsMathh */ diff --git a/src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp b/src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp new file mode 100644 index 000000000..149a11a46 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/logMsgToErrlog.cpp @@ -0,0 +1,106 @@ +/*************************************************************************\ +* 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 + * + * route vxWorks logMsg messages into the EPICS logging system + * + * Author: Jeff Hill + * + */ + +#include +#include + +#include +#include +#include + +#include "errlog.h" + +// vxCommonLibrary calls logMsgToErrlog so that logMsgToErrlog gets loaded +extern "C" { + int logMsgToErrlog(); +} + +int logMsgToErrlog() { return 0;} + +static class errlogDevTimeInit +{ +public: + errlogDevTimeInit (); +} errlogDevInstance; + +static int errlogOpen ( DEV_HDR *, const char *, int ) +{ + return OK; +} + +static int errlogWrite ( DEV_HDR *, const char * pInBuf, int nbytes ) +{ + errlogPrintfNoConsole ( "%.*s", nbytes, pInBuf ); + return nbytes; +} + +errlogDevTimeInit::errlogDevTimeInit () +{ + int errlogNo = iosDrvInstall ( + 0, // create not supported + 0, // remove not supported + reinterpret_cast < FUNCPTR > ( errlogOpen ), + 0, // close is a noop + 0, // read not supported + reinterpret_cast < FUNCPTR > ( errlogWrite ), + 0 // ioctl not supported + ); + if ( errlogNo == ERROR ) { + errlogPrintf ( + "Unable to install driver routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + DEV_HDR * pDev = static_cast < DEV_HDR * > ( calloc ( 1, sizeof ( *pDev ) ) ); + if ( ! pDev ) { + errlogPrintf ( + "Unable to create driver data structure for routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + int status = iosDevAdd ( pDev, "/errlog/", errlogNo ); + if ( status < 0 ) { + errlogPrintf ( + "Unable to install device routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + free ( pDev ); + return; + } + int fd = open ( "/errlog/any", O_WRONLY, 0 ); + if ( fd < 0 ) { + errlogPrintf ( + "Unable to open fd routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + return; + } + status = logFdAdd ( fd ); + if ( status != OK) { + errlogPrintf ( + "Unable to install fd routing the vxWorks " + "logging system to the EPICS logging system because \"%s\"\n", + strerror ( errno ) ); + close ( fd ); + return; + } +} + diff --git a/src/libCom/osi/os/vxWorks/module_types.h b/src/libCom/osi/os/vxWorks/module_types.h new file mode 100644 index 000000000..a580a582a --- /dev/null +++ b/src/libCom/osi/os/vxWorks/module_types.h @@ -0,0 +1,495 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* module_types.h */ +/* base/include Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ +/* + * Author: Bob Dalesio + * Date: 12-07-88 + */ + +#ifndef INCLmodule_typesh +#define INCLmodule_typesh + +/* Device module types */ +/* + * all devices have corresponding entries in ~operator/db/src/menus.c + * changes must be made in both areas to keep the database and drivers in sync + */ +/* & in comment indicates tested with card 0 */ +/* % in comment indicates tested with card other than card 0 */ +/* # in comment indicates that the Nth card has been tested */ +/* !! never been tested */ + +/* + * @# If any changes are made to this file, check the procedures + * ab_card, and vme_card in signallist.c, and get_address in sigmenu.c. + */ + +#ifdef MODULE_TYPES_INIT +#define MODULE_TYPES_DEF(MT_DEF_PARM) MT_DEF_PARM +#else +#define MODULE_TYPES_DEF(MT_DEF_PARM) extern MT_DEF_PARM; +#endif + +/* Number of columns used in io_report. */ +#define IOR_MAX_COLS 4 + +/* I/O types */ +#define IO_AI 0 +#define IO_AO 1 +#define IO_BI 2 +#define IO_BO 3 +#define IO_SM 4 +#define IO_WF 5 +#define IO_TIMER 6 +#define MAX_IO_TYPE IO_TIMER + +/* bus types */ +/* must correspond to the values in link types */ +/* these defines are in ~gta/dbcon/h/link.h */ + + +/* equates for the Allen-Bradley cards. */ +#define AB_BASE_ADDR 0xc00000 /* base addr of first AB6008SV */ +#define AB_MAX_LINKS 2 /* number of serial links from VME */ +#define AB_MAX_ADAPTERS 8 /* number of physical adapters on a link */ +#define AB_MAX_CARDS 16 /* max number of IO cards per adapter */ +#define AB_CARD_ADAPTER 16 /* cards per logical adapter */ +#define AB_CHAN_CARD 16 /* max channels per card */ + +/* analog inputs */ +#define AB1771IL 0 /* &% Allen-Bradley low level analog input */ +#define AB1771IFE 1 /* &% Allen-Bradley low level analog input */ +#define AB1771IXE 2 /* &% Allen-Bradley millivolt input */ +#define XY566SE 3 /* & Xycom 12-bit Single Ended Scanned*/ +#define XY566DI 4 /* &% Xycom 12-bit Differential Scanned */ +#define XY566DIL 5 /* &% Xycom 12-bit Differential Latched */ +#define VXI_AT5_AI 6 /* % AT-5 VXI module's Analog Inputs */ +#define AB1771IFE_SE 7 /* % A-B IFE in 16 single-ended input mode */ +#define AB1771IFE_4to20MA 8 /* % A-B IFE in 8 double-ended 4to20Ma */ +#define DVX2502 9 /* &% DVX_2502 128 chan 16 bit differential */ +#define AB1771IFE_0to5V 10 /* % A-B IFE in 8 double-ended 4to20Ma */ +#define KSCV215 11 /* % KSC V215 VXI 16 bit differential */ +#define AB1771IrPlatinum 12 /* % A-B RTD Platinum */ +#define AB1771IrCopper 13 /* % A-B RTD Copper */ +#define MAX_AI_TYPES AB1771IrCopper +MODULE_TYPES_DEF(short ai_num_cards[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={12,12,12, 4, 4, 6,32,12,12, 1, 12, 32, 12,12}; +#endif +MODULE_TYPES_DEF(short ai_num_channels[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 8, 8, 8,32,16,16, 8,16, 8, 127, 8, 32,6,6}; +#endif +MODULE_TYPES_DEF(short ai_interruptable[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,0,0}; +#endif +MODULE_TYPES_DEF(short ai_bus[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 4, 2, 2, 2, 2, 4, 4, 2, 4, 2,4,4}; +#endif +MODULE_TYPES_DEF(unsigned short ai_addrs[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0,0x6000,0x7000,0xe000, 0xc014,0,0, 0xff00, 0, 0,0,0}; +#endif +MODULE_TYPES_DEF(long ai_memaddrs[MAX_AI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0,0,0,0x000000,0x040000,0x0c0000, 0,0,0, 0x100000, 0, 0,0,0}; +#endif + +/* analog outputs */ +#define AB1771OFE 0 /* &% Allen-Bradley 12 bit Analog Output */ +#define VMI4100 1 /* & VMIC VMIVME 4100 */ +#define ZIO085 2 /* & Ziomek 085 */ +#define VXI_AT5_AO 3 /* !! AT-5 VXI modules analog outputs */ +#define MAX_AO_TYPES VXI_AT5_AO +MODULE_TYPES_DEF(short ao_num_cards[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = {12, 4, 1, 32}; +#endif +MODULE_TYPES_DEF(short ao_num_channels[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = { 4, 16, 32, 16}; +#endif +MODULE_TYPES_DEF(short ao_interruptable[MAX_AO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 0, 0, 0, 1}; +#endif +MODULE_TYPES_DEF(short ao_bus[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 2, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short ao_addrs[MAX_AO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0x4100,0x0800, 0xc000}; +#endif + +/* binary inputs */ +#define ABBI_08_BIT 0 /* &% Allen-Bradley generic Binary In 8 bit */ +#define ABBI_16_BIT 1 /* &% Allen-Bradley generic Binary In 16 bit */ +#define BB910 2 /* & BURR BROWN MPV 910 (relay) */ +#define XY210 3 /* &% XYcom 32 bit binary in */ +#define VXI_AT5_BI 4 /* !! AT-5 VXI modules binary inputs */ +#define HPE1368A_BI 5 /* !! HP E1368A video switch */ +#define AT8_FP10S_BI 6 /* !! AT8 FP10 slave fast protect */ +#define XY240_BI 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ +#define MAX_BI_TYPES XY240_BI +MODULE_TYPES_DEF(short bi_num_cards[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 12, 12, 4, 4, 32, 32, 8, 2}; +#endif +MODULE_TYPES_DEF(short bi_num_channels[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 16, 32, 32, 32, 16, 32, 32}; +#endif +MODULE_TYPES_DEF(short bi_interruptable[MAX_BI_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 1, 1, 0, 0, 1, 1, 1, 1}; +#endif +MODULE_TYPES_DEF(short bi_bus[MAX_BI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 2, 2, 2, 2, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short bi_addrs[MAX_BI_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0xb800,0xa000, 0xc000, 0xc000, 0x0e00, 0xd000}; +#endif + +/* binary outputs */ +#define ABBO_08_BIT 0 /* &% Allen-Bradley 8 bit binary out */ +#define ABBO_16_BIT 1 /* &% Allen-Bradley 16 bit binary out */ +#define BB902 2 /* &% BURR BROWN MPV 902 (relay) */ +#define XY220 3 /* &% XYcom 32 bit binary out */ +#define VXI_AT5_BO 4 /* !! AT-5 VXI modules binary outputs */ +#define HPE1368A_BO 5 /* !! HP E1368A video switch */ +#define AT8_FP10M_BO 6 /* !! AT8 FP10 master fast protect */ +#define XY240_BO 7 /* !! Xycom 32 bit binary in / 32 bit binary out */ +#define MAX_BO_TYPES XY240_BO +MODULE_TYPES_DEF(short bo_num_cards[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={12, 12, 4, 1, 32, 32, 2, 2}; +#endif +MODULE_TYPES_DEF(short bo_num_channels[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 16, 32, 32, 32, 16, 32, 32}; +#endif +MODULE_TYPES_DEF(short bo_interruptable[MAX_BO_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 0, 0, 0, 0, 1, 0, 0, 1 }; +#endif +MODULE_TYPES_DEF(short bo_bus[MAX_BO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 4, 4, 2, 2, 2, 2, 2, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short bo_addrs[MAX_BO_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0,0,0xd800,0xc800, 0xc000, 0xc000, 0x0c00, 0xd000}; +#endif + +/* stepper motor drivers */ +#define CM57_83E 0 /* & Compumotor 57-83E motor controller */ +#define OMS_6AXIS 1 /* & OMS six axis motor controller */ +#define MAX_SM_TYPES OMS_6AXIS +MODULE_TYPES_DEF(short sm_num_cards[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 8, 8 }; +#endif +MODULE_TYPES_DEF(short sm_num_channels[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 1, 8}; +#endif +MODULE_TYPES_DEF(short sm_interruptable[MAX_SM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 0, 0 }; +#endif +MODULE_TYPES_DEF(short sm_bus[MAX_SM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 2, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short sm_addrs[MAX_SM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 0x8000, 0xfc00 }; +#endif + +/* waveforms */ +#define XY566WF 0 /* & Xycom 566 as a waveform */ +#define CAMAC_THING 1 /* !! CAMAC waveform digitizer */ +#define JGVTR1 2 /* & Joerger transient recorder */ +#define COMET 3 /* !! COMET transient recorder */ +#define MAX_WF_TYPES COMET +MODULE_TYPES_DEF(short wf_num_cards[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={4, 4, 8, 4}; +#endif +MODULE_TYPES_DEF(short wf_num_channels[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={1, 1, 1, 4}; +#endif +MODULE_TYPES_DEF(short wf_interruptable[MAX_WF_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = {0, 0, 0, 0}; +#endif +MODULE_TYPES_DEF(short wf_bus[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={2, 3, 2, 2}; +#endif +MODULE_TYPES_DEF(unsigned short wf_addrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0x9000, 0, 0xB000, 0xbc00}; +#endif +MODULE_TYPES_DEF(unsigned short wf_armaddrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + = {0x5400, 0, 0, 0}; +#endif +MODULE_TYPES_DEF(long wf_memaddrs[MAX_WF_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0x080000, 0, 0xb80000, 0xe0000000}; +#endif + + +/* timing cards */ +#define MZ8310 0 /* &% Mizar Timing Module */ +#define DG535 1 /* !! GPIB timing instrument */ +#define VXI_AT5_TIME 2 /* !! AT-5 VXI modules timing channels */ +#define MAX_TM_TYPES VXI_AT5_TIME +MODULE_TYPES_DEF(short tm_num_cards[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={ 4, 1, 32 }; +#endif +MODULE_TYPES_DEF(short tm_num_channels[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + ={10, 1, 10}; +#endif +MODULE_TYPES_DEF(short tm_interruptable[MAX_TM_TYPES+1] ) +#ifdef MODULE_TYPES_INIT + = { 1, 0, 1 }; +#endif +MODULE_TYPES_DEF(short tm_bus[MAX_TM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={ 2, 5, 2 }; +#endif +MODULE_TYPES_DEF(unsigned short tm_addrs[MAX_TM_TYPES+1]) +#ifdef MODULE_TYPES_INIT + ={0xf800, 0, 0xc000 }; +#endif + +/* AT830X clock cards */ +MODULE_TYPES_DEF(long AT830X_1_addrs ) +#ifdef MODULE_TYPES_INIT + = 0x0400; +#endif +MODULE_TYPES_DEF(short AT830X_1_num_cards ) +#ifdef MODULE_TYPES_INIT + = 2; +#endif +MODULE_TYPES_DEF(long AT830X_addrs ) +#ifdef MODULE_TYPES_INIT + = 0xaa0000; +#endif +MODULE_TYPES_DEF(short AT830X_num_cards ) +#ifdef MODULE_TYPES_INIT + = 2; +#endif + +/* + * system controller cards. + * (driver looks for only one card) + */ +MODULE_TYPES_DEF(long xy010ScA16Base) +#ifdef MODULE_TYPES_INIT + = 0x0000; +#endif +/* + * limit the size of the VXI logical address space + * + * = + 0xc000 + * + * LA VME address + * 0 + * EPICS_VXI_LA_COUNT + (EPICS_VXI_LA_COUNT-1)*64 + */ +MODULE_TYPES_DEF(unsigned char EPICS_VXI_LA_COUNT) +#ifdef MODULE_TYPES_INIT + = 32; +#endif + +/* + * + * address ranges for VXI A24 and A32 devices + * + */ +MODULE_TYPES_DEF(char *EPICS_VXI_A24_BASE) +#ifdef MODULE_TYPES_INIT + = (char *) 0x900000; +#endif +MODULE_TYPES_DEF(unsigned long EPICS_VXI_A24_SIZE) +#ifdef MODULE_TYPES_INIT + = 0x100000; +#endif +MODULE_TYPES_DEF(char *EPICS_VXI_A32_BASE) +#ifdef MODULE_TYPES_INIT + = (char *) 0x90000000; +#endif +MODULE_TYPES_DEF(unsigned long EPICS_VXI_A32_SIZE) +#ifdef MODULE_TYPES_INIT + = 0x10000000; +#endif + + +/****************************************************************************** + * + * Interrupt vector locations used by the MV167 CPU board. + * These are defined in mv167.h + * + * PCC2_INT_VEC_BASE 0x40 PCC interrupt vector base number + * any multiple of 0x10 + * UTIL_INT_VEC_BASE0 0x50 VMEchip2 utility interrupt + * vector base number + * any multiple of 0x10 + * UTIL_INT_VEC_BASE1 0x60 VMEchip2 utility interrupt + * vector base number + * any multiple of 0x10 + * + * INT_VEC_CD2400_A 0x90 int vec for channel A + * INT_VEC_CD2400_B 0x94 int vec for channel B + * INT_VEC_CD2400_C 0x98 int vec for channel C + * INT_VEC_CD2400_D 0x9c int vec for channel D + * + * LANC_IRQ_LEVEL 3 LNANC IRQ level + * MPCC_IRQ_LEVEL 4 serial comm IRQ level + * SYS_CLK_LEVEL 6 interrupt level for sysClk + * AUX_CLK_LEVEL 5 interrupt level for auxClk + * SCSI_IRQ_LEVEL 2 scsi interrupt level + * + ******************************************************************************/ + +/* interrupt vector allocation - one for each XY566 DIL card */ +MODULE_TYPES_DEF(int AI566_VNUM) +#ifdef MODULE_TYPES_INIT + =0xf8; /* Xycom 566 Differential Latched */ +#endif + +/* interrupt vector allocation - one for each DVX card */ +MODULE_TYPES_DEF(int DVX_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd0; +#endif + +/* stepper motor interrupt vector - one for each motor */ +MODULE_TYPES_DEF(int MD_INT_BASE) +#ifdef MODULE_TYPES_INIT + =0xf0; /* base of the motor int vector */ +#endif + +/* I reserve from here up to num_cards * 4 interrupting chans/card - joh */ +MODULE_TYPES_DEF(int MZ8310_INT_VEC_BASE) +#ifdef MODULE_TYPES_INIT + =0xe8; +#endif + +/* Allen-Bradley Serial Driver - MAX_AB_LINKS number of vectors */ +MODULE_TYPES_DEF(int AB_VEC_BASE) +#ifdef MODULE_TYPES_INIT + =0x60; +#endif + +/* only one interrupt vector allocated for all Joerger VTR1 boards joh */ +MODULE_TYPES_DEF(int JGVTR1_INT_VEC) +#ifdef MODULE_TYPES_INIT + =0xe0; +#endif + +/* AT830X_1 cards have 1 intr vector for each AT830X_1_num_cards (presently 2) */ +MODULE_TYPES_DEF(int AT830X_1_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd4; +#endif + +/* AT830X cards have 1 intr vector for each AT830X_num_cards (presently 2) */ +MODULE_TYPES_DEF(int AT830X_IVEC0) +#ifdef MODULE_TYPES_INIT + =0xd6; +#endif + +/* AT8 fast protect interrupt vector base */ +MODULE_TYPES_DEF(int AT8FP_IVEC_BASE) +#ifdef MODULE_TYPES_INIT + =0xa2; +#endif + + +MODULE_TYPES_DEF(int AT8FPM_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + =0xaa; +#endif + + +/****************************************************************************** + * + * Addresses and IRQ information used by the XVME402 bitbus cards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short BB_SHORT_OFF ) +#ifdef MODULE_TYPES_INIT + = 0x1800; /* the first address of link 0's region */ +#endif +#define BB_NUM_LINKS 4 /* max number of BB ports allowed */ +MODULE_TYPES_DEF(int BB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 0xa0; /* vectored interrupts (2 used for each link) */ +#endif +MODULE_TYPES_DEF(int BB_IRQ_LEVEL ) +#ifdef MODULE_TYPES_INIT + = 5; /* IRQ level */ +#endif + +/****************************************************************************** + * + * Information for the PEP modular Bitbus boards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short PEP_BB_SHORT_OFF ) +#ifdef MODULE_TYPES_INIT + = 0x1c00; +#endif +MODULE_TYPES_DEF(int PEP_BB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 0xe8; +#endif + +/****************************************************************************** + * + * Addresses and IRQ information used by the NI1014 and NI1014D bitbus cards. + * + ******************************************************************************/ +MODULE_TYPES_DEF(unsigned short NIGPIB_SHORT_OFF) +#ifdef MODULE_TYPES_INIT + = 0x5000;/* First address of link 0's region */ +#endif + /* Each link uses 0x0200 bytes */ +#define NIGPIB_NUM_LINKS 4 /* Max number of NI GPIB ports allowed */ +MODULE_TYPES_DEF(int NIGPIB_IVEC_BASE ) +#ifdef MODULE_TYPES_INIT + = 100; /* Vectored interrupts (2 used for each link) */ +#endif +MODULE_TYPES_DEF(int NIGPIB_IRQ_LEVEL ) +#ifdef MODULE_TYPES_INIT + =5; /* IRQ level */ +#endif + +#if 0 /* JRW */ +#define NI1014_LINK_NUM_BASE 0 +#endif + +/* + * nothing after this endif + */ +#endif /*INCLmodule_typesh*/ diff --git a/src/libCom/osi/os/vxWorks/osdEnv.c b/src/libCom/osi/os/vxWorks/osdEnv.c new file mode 100644 index 000000000..891711b07 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdEnv.c @@ -0,0 +1,74 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osdEnv.c */ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author: Eric Norum + * Date: May 7, 2001 + * + * Routines to modify/display environment variables and EPICS parameters + * + */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsFindSymbol.h" + +/* + * Set the value of an environment variable + * Leaks memory, but the assumption is that this routine won't be + * called often enough for the leak to be a problem. + */ +epicsShareFunc void epicsShareAPI epicsEnvSet (const char *name, const char *value) +{ + char *cp; + + cp = mallocMustSucceed (strlen (name) + strlen (value) + 2, "epicsEnvSet"); + strcpy (cp, name); + strcat (cp, "="); + strcat (cp, value); + if (putenv (cp) < 0) { + errPrintf( + -1L, + __FILE__, + __LINE__, + "Failed to set environment parameter \"%s\" to \"%s\": %s\n", + name, + value, + strerror (errno)); + free (cp); + } +} + +/* + * Show the value of the specified, or all, environment variables + */ +epicsShareFunc void epicsShareAPI epicsEnvShow (const char *name) +{ + if (name == NULL) { + envShow (0); + } + else { + const char *cp = getenv (name); + if (cp == NULL) + printf ("%s is not an environment variable.\n", name); + else + printf ("%s=%s\n", name, cp); + } +} diff --git a/src/libCom/osi/os/vxWorks/osdEvent.c b/src/libCom/osi/os/vxWorks/osdEvent.c new file mode 100644 index 000000000..692d6f63f --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdEvent.c @@ -0,0 +1,82 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* os/vxWorks/osdEvent.c */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include +#include +#include +#include +#include + +#include "epicsEvent.h" + +/* The following not defined in any vxWorks header */ +int sysClkRateGet(void); + +epicsEventId epicsEventCreate(epicsEventInitialState initialState) +{ + return (epicsEventId) semBCreate(SEM_Q_FIFO, + (initialState == epicsEventEmpty) ? SEM_EMPTY : SEM_FULL); +} + +epicsEventId epicsEventMustCreate(epicsEventInitialState initialState) +{ + epicsEventId id = epicsEventCreate(initialState); + assert(id); + return id; +} + +void epicsEventDestroy(epicsEventId id) +{ + semDelete((SEM_ID)id); +} + +epicsEventWaitStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut) +{ + int rate = sysClkRateGet(); + int status; + int ticks; + + if (timeOut <= 0.0) { + ticks = 0; + } else if (timeOut >= (double) INT_MAX / rate) { + ticks = WAIT_FOREVER; + } else { + ticks = timeOut * rate; + if (ticks <= 0) + ticks = 1; + } + status = semTake((SEM_ID)id, ticks); + if (status == OK) + return epicsEventWaitOK; + if (errno == S_objLib_OBJ_TIMEOUT || + (errno == S_objLib_OBJ_UNAVAILABLE && ticks == 0)) + return epicsEventWaitTimeout; + return epicsEventWaitError; +} + +epicsEventWaitStatus epicsEventTryWait(epicsEventId id) +{ + int status = semTake((SEM_ID)id, NO_WAIT); + + if (status == OK) + return epicsEventWaitOK; + if (errno == S_objLib_OBJ_UNAVAILABLE) + return epicsEventWaitTimeout; + return epicsEventWaitError; +} + +void epicsEventShow(epicsEventId id, unsigned int level) +{ + semShow((SEM_ID)id,level); +} diff --git a/src/libCom/osi/os/vxWorks/osdEvent.h b/src/libCom/osi/os/vxWorks/osdEvent.h new file mode 100644 index 000000000..450f8eac6 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdEvent.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* os/vxWorks/osdEvent.h */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include + +/* If the macro is replaced by inline it is necessary to say + static __inline__ + but then a warning message appears everywhere osdEvent.h is included +*/ + +#define epicsEventSignal(ID) semGive((SEM_ID)(ID)) + +#define epicsEventWait(ID) \ +(semTake((SEM_ID)(ID),WAIT_FOREVER)==OK ? epicsEventWaitOK : epicsEventWaitError) diff --git a/src/libCom/osi/os/vxWorks/osdFindSymbol.c b/src/libCom/osi/os/vxWorks/osdFindSymbol.c new file mode 100644 index 000000000..d9c9d1161 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdFindSymbol.c @@ -0,0 +1,86 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/vxWorks/osiFindSymbol */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbmf.h" +#include "epicsString.h" +#include "epicsFindSymbol.h" + +static char *errmsg = NULL; +static char *oldmsg = NULL; + +epicsShareFunc void * epicsLoadLibrary(const char *name) +{ + MODULE_ID m = 0; + int fd; + + if (oldmsg) { + free(oldmsg); + oldmsg = NULL; + } + if (errmsg) { + free(errmsg); + errmsg = NULL; + } + + fd = open(name, O_RDONLY, 0); + if (fd != ERROR) { + m = loadModule(fd, GLOBAL_SYMBOLS); + close(fd); + } + + if (!m) { + errmsg = epicsStrDup(strerror(errno)); + } + return m; +} + +epicsShareFunc const char *epicsLoadError(void) +{ + if (oldmsg) free(oldmsg); + oldmsg = errmsg; + errmsg = NULL; + return oldmsg; +} + +void *epicsFindSymbol(const char *name) +{ + STATUS status; + SYM_TYPE type; + char *pvalue; + + status = symFindByName( sysSymTbl, (char *)name, &pvalue, &type ); + if(status) { + if(name[0] == '_' ) { + status = symFindByName(sysSymTbl, (char *)(name+1), &pvalue, &type); + } else { + char *pname; + pname = dbmfMalloc(strlen(name) + 2); + strcpy(pname,"_"); + strcat(pname,name); + status = symFindByName(sysSymTbl,pname, &pvalue, &type); + dbmfFree(pname); + } + } + if(status) return(0); + return((void *)pvalue); +} diff --git a/src/libCom/osi/os/vxWorks/osdInterrupt.c b/src/libCom/osi/os/vxWorks/osdInterrupt.c new file mode 100644 index 000000000..3ac9e9e83 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdInterrupt.c @@ -0,0 +1,29 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osi/os/vxWorks/osdInterrupt.c */ + +/* Author: Marty Kraimer Date: 28JAN2000 */ + +#include +#include +#include + +#include "epicsInterrupt.h" + +int epicsInterruptLock() {return(intLock());} + +void epicsInterruptUnlock(int key) {intUnlock(key);} + +int epicsInterruptIsInterruptContext() {return(intContext());} + +void epicsInterruptContextMessage(const char *message) +{ + logMsg((char *)message,0,0,0,0,0,0); +} diff --git a/src/libCom/osi/os/vxWorks/osdInterrupt.h b/src/libCom/osi/os/vxWorks/osdInterrupt.h new file mode 100644 index 000000000..ad99926de --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdInterrupt.h @@ -0,0 +1,14 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* osi/os/vxWorks/osdInterrupt.h */ + +/* Author: Marty Kraimer Date: 28JAN2000 */ + +/*osdInterrupt.h not needed */ diff --git a/src/libCom/osi/os/vxWorks/osdMessageQueue.cpp b/src/libCom/osi/os/vxWorks/osdMessageQueue.cpp new file mode 100644 index 000000000..22fd9acaa --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdMessageQueue.cpp @@ -0,0 +1,56 @@ +/*************************************************************************\ +* 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 W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +#define epicsExportSharedSymbols +#include +#include "epicsMessageQueue.h" + +extern "C" int sysClkRateGet(void); + +epicsShareFunc int epicsShareAPI epicsMessageQueueSendWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int messageSize, + double timeout) +{ + int ticks; + + if (timeout<=0.0) { + ticks = 0; + } else { + ticks = (int)(timeout*sysClkRateGet()); + if(ticks<=0) ticks = 1; + } + return msgQSend((MSG_Q_ID)id, (char *)message, messageSize, ticks, MSG_PRI_NORMAL); +} + +epicsShareFunc int epicsShareAPI epicsMessageQueueReceiveWithTimeout( + epicsMessageQueueId id, + void *message, + unsigned int size, + double timeout) +{ + int ticks; + + if (timeout<=0.0) { + ticks = 0; + } else { + ticks = (int)(timeout*sysClkRateGet()); + if(ticks<=0) ticks = 1; + } + return msgQReceive((MSG_Q_ID)id, (char *)message, size, ticks); +} diff --git a/src/libCom/osi/os/vxWorks/osdMessageQueue.h b/src/libCom/osi/os/vxWorks/osdMessageQueue.h new file mode 100644 index 000000000..aa7437135 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdMessageQueue.h @@ -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. +\*************************************************************************/ +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ + +/* + * Very thin shims around vxWorks routines + */ +#include +#include + +#define epicsMessageQueueCreate(c,s) ((epicsMessageQueueId)msgQCreate((c),(s),MSG_Q_FIFO)) +#define epicsMessageQueueDestroy(q) (msgQDelete((MSG_Q_ID)(q))) + +#define epicsMessageQueueTrySend(q,m,l) (msgQSend((MSG_Q_ID)(q), (char*)(m), (l), NO_WAIT, MSG_PRI_NORMAL)) +#define epicsMessageQueueSend(q,m,l) (msgQSend((MSG_Q_ID)(q), (char*)(m), (l), WAIT_FOREVER, MSG_PRI_NORMAL)) + +#define epicsMessageQueueTryReceive(q,m,s) (msgQReceive((MSG_Q_ID)(q), (char*)(m), (s), NO_WAIT)) +#define epicsMessageQueueReceive(q,m,s) (msgQReceive((MSG_Q_ID)(q), (char*)(m), (s), WAIT_FOREVER)) + +#define epicsMessageQueuePending(q) (msgQNumMsgs((MSG_Q_ID)(q))) +#define epicsMessageQueueShow(q,l) (msgQShow((MSG_Q_ID)(q),(l))) diff --git a/src/libCom/osi/os/vxWorks/osdMutex.c b/src/libCom/osi/os/vxWorks/osdMutex.c new file mode 100644 index 000000000..38db2ab83 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdMutex.c @@ -0,0 +1,49 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* os/vxWorks/osdMutex.c */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include +#include +#include +#include + +/* The following not defined in an vxWorks header */ +int sysClkRateGet(void); + + +#include "epicsMutex.h" + +struct epicsMutexOSD * epicsMutexOsdCreate(void) +{ + return((struct epicsMutexOSD *) + semMCreate(SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY)); +} + +void epicsMutexOsdDestroy(struct epicsMutexOSD * id) +{ + semDelete((SEM_ID)id); +} + +epicsMutexLockStatus epicsMutexOsdTryLock(struct epicsMutexOSD * id) +{ + int status; + status = semTake((SEM_ID)id,NO_WAIT); + if(status==OK) return(epicsMutexLockOK); + if(errno==S_objLib_OBJ_UNAVAILABLE) return(epicsMutexLockTimeout); + return(epicsMutexLockError); +} + +void epicsMutexOsdShow(struct epicsMutexOSD * id,unsigned int level) +{ + semShow((SEM_ID)id,level); +} diff --git a/src/libCom/osi/os/vxWorks/osdMutex.h b/src/libCom/osi/os/vxWorks/osdMutex.h new file mode 100644 index 000000000..3ea951a8f --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdMutex.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* os/vxWorks/osdMutex.h */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +#include +#include + +/* If the macro is replaced by inline it is necessary to say + static __inline__ + but then a warning message appears everywhere osdMutex.h is included +*/ + +#define epicsMutexOsdUnlock(ID) semGive((SEM_ID)(ID)) + +#define epicsMutexOsdLock(ID) \ +(semTake((SEM_ID)(ID),WAIT_FOREVER)==OK ? epicsMutexLockOK : epicsMutexLockError) diff --git a/src/libCom/osi/os/vxWorks/osdPoolStatus.c b/src/libCom/osi/os/vxWorks/osdPoolStatus.c new file mode 100644 index 000000000..c8418a236 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdPoolStatus.c @@ -0,0 +1,70 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsThread.h" +#include "osiPoolStatus.h" + +/* + * It turns out that memPartInfoGet() and memFindMax() are very CPU intensive on vxWorks + * so we must spawn off a thread that periodically polls. Although this isnt 100% safe, I + * dont see what else to do. + * + * It takes about 30 uS to call memPartInfoGet() on a pcPentium I vxWorks system. + * + * joh + */ + +static epicsThreadOnceId osdMaxBlockOnceler = EPICS_THREAD_ONCE_INIT; + +static size_t osdMaxBlockSize = 0; + +static void osdSufficentSpaceInPoolQuery () +{ + int temp = memFindMax (); + + osdMaxBlockSize = ( temp > 0 ) ? (size_t) temp : 0; +} + +static void osdSufficentSpaceInPoolPoll ( void *pArgIn ) +{ + while ( 1 ) { + epicsThreadSleep ( 1.0 ); + osdSufficentSpaceInPoolQuery (); + } +} + +static void osdSufficentSpaceInPoolInit ( void *pArgIn ) +{ + epicsThreadId id; + + osdSufficentSpaceInPoolQuery (); + + id = epicsThreadCreate ( "poolPoll", epicsThreadPriorityMedium, + epicsThreadGetStackSize ( epicsThreadStackSmall ), + osdSufficentSpaceInPoolPoll, 0 ); +} + +/* + * osiSufficentSpaceInPool () + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ) +{ + epicsThreadOnce ( &osdMaxBlockOnceler, osdSufficentSpaceInPoolInit, 0 ); + + if ( UINT_MAX - 100000u >= contiguousBlockSize ) { + return ( osdMaxBlockSize > 100000 + contiguousBlockSize ); + } + else { + return 0; + } +} diff --git a/src/libCom/osi/os/vxWorks/osdProcess.c b/src/libCom/osi/os/vxWorks/osdProcess.c new file mode 100644 index 000000000..a0e22f24b --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdProcess.c @@ -0,0 +1,58 @@ +/*************************************************************************\ +* 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 is 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 + * + * Operating System Dependent Implementation of osiProcess.h + * + * Author: Jeff Hill + * + */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include + +#include + +#define epicsExportSharedSymbols +#include "osiProcess.h" +#include "errlog.h" + +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSizeIn) +{ + char pName[MAX_IDENTITY_LEN]; + unsigned uiLength; + size_t len; + + remCurIdGet ( pName, NULL ); + len = strlen ( pName ); + + if (len>UINT_MAX || len<=0) { + return osiGetUserNameFail; + } + uiLength = (unsigned) len; + + if ( uiLength + 1 >= bufSizeIn ) { + return osiGetUserNameFail; + } + + strncpy ( pBuf, pName, (size_t) bufSizeIn ); + + return osiGetUserNameSuccess; +} + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName) +{ + return osiSpawnDetachedProcessNoSupport; +} diff --git a/src/libCom/osi/os/vxWorks/osdSignal.cpp b/src/libCom/osi/os/vxWorks/osdSignal.cpp new file mode 100644 index 000000000..1157854aa --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdSignal.cpp @@ -0,0 +1,20 @@ +/*************************************************************************\ + * 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. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "epicsSignal.h" + +/* + * NOOP + */ +epicsShareFunc void epicsShareAPI epicsSignalInstallSigHupIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigPipeIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalInstallSigAlarmIgnore ( void ) {} +epicsShareFunc void epicsShareAPI epicsSignalRaiseSigAlarm ( struct epicsThreadOSD * /* threadId */ ) {} diff --git a/src/libCom/osi/os/vxWorks/osdSock.c b/src/libCom/osi/os/vxWorks/osdSock.c new file mode 100644 index 000000000..dd6bab973 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdSock.c @@ -0,0 +1,138 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include + +#ifndef vxWorks +#error this is a vxWorks specific source code +#endif + +#include +#include + +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "osiSock.h" + +int osiSockAttach() +{ + return 1; +} + +void osiSockRelease() +{ +} + +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ) +{ + SOCKET sock = socket ( domain, type, protocol ); + if ( sock < 0 ) { + sock = INVALID_SOCKET; + } + return sock; +} + +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ) +{ + int newSock = accept ( sock, pAddr, addrlen ); + if ( newSock < 0 ) { + newSock = INVALID_SOCKET; + } + return newSock; +} + +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( SOCKET s ) +{ + int status = close ( s ); + if ( status < 0 ) { + char buf [ 64 ]; + epicsSocketConvertErrnoToString ( buf, sizeof ( buf ) ); + errlogPrintf ( + "epicsSocketDestroy: failed to " + "close a socket because \"%s\"\n", + buf ); + } +} + +/* + * ipAddrToHostName + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName + (const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + int status; + int errnoCopy = errno; + unsigned len; + + if (bufSize<1) { + return 0; + } + + if (bufSize>MAXHOSTNAMELEN) { + status = hostGetByAddr ((int)pAddr->s_addr, pBuf); + if (status==OK) { + pBuf[MAXHOSTNAMELEN] = '\0'; + len = strlen (pBuf); + } + else { + len = 0; + } + } + else { + char name[MAXHOSTNAMELEN+1]; + status = hostGetByAddr (pAddr->s_addr, name); + if (status==OK) { + strncpy (pBuf, name, bufSize); + pBuf[bufSize-1] = '\0'; + len = strlen (pBuf); + } + else { + len = 0; + } + } + + errno = errnoCopy; + + return len; +} + +/* + * hostToIPAddr () + */ +epicsShareFunc int epicsShareAPI hostToIPAddr + (const char *pHostName, struct in_addr *pIPA) +{ + int addr; + + addr = hostGetByName ((char *)pHostName); + if (addr==ERROR) { + addr = inet_addr ((char *)pHostName); + if (addr==ERROR) { + /* + * return indicating an error + */ + return -1; + } + } + + pIPA->s_addr = (unsigned long) addr; + + /* + * success + */ + return 0; +} + diff --git a/src/libCom/osi/os/vxWorks/osdSock.h b/src/libCom/osi/os/vxWorks/osdSock.h new file mode 100644 index 000000000..3ebc5799a --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdSock.h @@ -0,0 +1,105 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * vxWorks specific socket include + */ + +#ifndef osdSockH +#define osdSockH + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*This following is not defined in any vxWorks header files*/ +int sysClkRateGet(void); + +#ifdef __cplusplus +} +#endif + +typedef int SOCKET; +#define INVALID_SOCKET (-1) +#define SOCKERRNO errno +#ifndef SHUT_RD +# define SHUT_RD 0 +#endif +#ifndef SHUT_WR +# define SHUT_WR 1 +#endif +#ifndef SHUT_RDWR +# define SHUT_RDWR 2 +#endif + +/* + * it is quite lame on WRS's part to assume that + * a ptr is always the same as an int + */ +#define socket_ioctl(A,B,C) ioctl(A,B,(int)C) +typedef int osiSockIoctl_t; +typedef int osiSocklen_t; + +#define FD_IN_FDSET(FD) ((FD)=0) + +#define SOCK_EWOULDBLOCK EWOULDBLOCK +#define SOCK_ENOBUFS ENOBUFS +#define SOCK_ECONNRESET ECONNRESET +#define SOCK_ETIMEDOUT ETIMEDOUT +#define SOCK_EADDRINUSE EADDRINUSE +#define SOCK_ECONNREFUSED ECONNREFUSED +#define SOCK_ECONNABORTED ECONNABORTED +#define SOCK_EINPROGRESS EINPROGRESS +#define SOCK_EISCONN EISCONN +#define SOCK_EALREADY EALREADY +#define SOCK_EINVAL EINVAL +#define SOCK_EINTR EINTR +#define SOCK_EPIPE EPIPE +#define SOCK_EMFILE EMFILE +#define SOCK_SHUTDOWN ESHUTDOWN +#define SOCK_ENOTSOCK ENOTSOCK +#define SOCK_EBADF EBADF + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7F000001 +#endif + +#ifndef INADDR_NONE +# define INADDR_NONE (0xffffffff) +#endif + +#if defined(_SIZEOF_ADDR_IFREQ) +# define ifreq_size(pifreq) _SIZEOF_ADDR_IFREQ(*pifreq) +#elif ( defined (BSD) && ( BSD >= 44 ) ) +# define ifreq_size(pifreq) (pifreq->ifr_addr.sa_len + sizeof(pifreq->ifr_name)) +#else +# define ifreq_size(pifreq) sizeof(*pifreq) +#endif + +#endif /*osdSockH*/ + + diff --git a/src/libCom/osi/os/vxWorks/osdStdio.c b/src/libCom/osi/os/vxWorks/osdStdio.c new file mode 100644 index 000000000..219cdc5e2 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdStdio.c @@ -0,0 +1,57 @@ +/* osdStdio.c */ +/*************************************************************************\ +* 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 "epicsStdio.h" +#include "fioLib.h" +#include "string.h" +#include "dbDefs.h" + +struct outStr_s { + char *str; + int free; +}; + +static STATUS outRoutine(char *buffer, int nchars, int outarg) { + struct outStr_s *poutStr = (struct outStr_s *) outarg; + int free = poutStr->free; + int len; + + if (free < 1) { /*let fioFormatV continue to count length*/ + return OK; + } else if (free > 1) { + len = min(free-1, nchars); + strncpy(poutStr->str, buffer, len); + poutStr->str += len; + poutStr->free -= len; + } + /*make sure final string is null terminated*/ + *poutStr->str = 0; + return OK; +} + +int epicsVsnprintf(char *str, size_t size, const char *format, va_list ap) { + struct outStr_s outStr; + + outStr.str = str; + outStr.free = size; + return fioFormatV(format, ap, outRoutine, (int) &outStr); +} + +int epicsSnprintf(char *str, size_t size, const char *format, ...) +{ + int nchars; + va_list pvar; + + va_start(pvar,format); + nchars = epicsVsnprintf(str,size,format,pvar); + va_end (pvar); + return(nchars); +} diff --git a/src/libCom/osi/os/vxWorks/osdStrtod.h b/src/libCom/osi/os/vxWorks/osdStrtod.h new file mode 100644 index 000000000..28fd36b9d --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdStrtod.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ + +/* + * This header fragment is intended to be included as part of epicsString.h + */ + +/* + * epicsStrtod() for systems with broken strtod() routine + */ +epicsShareFunc double epicsStrtod(const char *str, char **endp); diff --git a/src/libCom/osi/os/vxWorks/osdThread.c b/src/libCom/osi/os/vxWorks/osdThread.c new file mode 100644 index 000000000..09d451fab --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdThread.c @@ -0,0 +1,404 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* osi/os/vxWorks/epicsThread.c */ + +/* Author: Marty Kraimer Date: 25AUG99 */ + +/* This is needed for vxWorks 6.8 to prevent an obnoxious compiler warning */ +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "errlog.h" +#include "ellLib.h" +#include "epicsThread.h" +#include "cantProceed.h" +#include "epicsAssert.h" +#include "vxLib.h" +#include "epicsExit.h" + +#if CPU_FAMILY == MC680X0 +#define ARCH_STACK_FACTOR 1 +#elif CPU_FAMILY == SPARC +#define ARCH_STACK_FACTOR 2 +#else +#define ARCH_STACK_FACTOR 2 +#endif +static const unsigned stackSizeTable[epicsThreadStackBig+1] = + {4000*ARCH_STACK_FACTOR, 6000*ARCH_STACK_FACTOR, 11000*ARCH_STACK_FACTOR}; + +/*The following forces atReboot to be loaded*/ +extern int atRebootExtern; +static struct pext { + int *pExtern; + struct pext *pext; +} pext = {&atRebootExtern, &pext}; + +/* definitions for implementation of epicsThreadPrivate */ +static void **papTSD = 0; +static int nepicsThreadPrivate = 0; + +static SEM_ID epicsThreadOnceMutex = 0; + +/* Just map osi 0 to 99 into vx 100 to 199 */ +/* remember that for vxWorks lower number means higher priority */ +/* vx = 100 + (99 -osi) = 199 - osi*/ +/* osi = 199 - vx */ + +static unsigned int getOsiPriorityValue(int ossPriority) +{ + if ( ossPriority < 100 ) { + return epicsThreadPriorityMax; + } + else if ( ossPriority > 199 ) { + return epicsThreadPriorityMin; + } + else { + return ( 199u - (unsigned int) ossPriority ); + } +} + +static int getOssPriorityValue(unsigned int osiPriority) +{ + if ( osiPriority > 99 ) { + return 100; + } + else { + return ( 199 - (signed int) osiPriority ); + } +} + +static void epicsThreadInit(void) +{ + static int lock = 0; + + while(!vxTas(&lock)) taskDelay(1); + if(epicsThreadOnceMutex==0) { + epicsThreadOnceMutex = semMCreate( + SEM_DELETE_SAFE|SEM_INVERSION_SAFE|SEM_Q_PRIORITY); + assert(epicsThreadOnceMutex); + } + lock = 0; +} + +unsigned int epicsThreadGetStackSize (epicsThreadStackSizeClass stackSizeClass) +{ + + if (stackSizeClassepicsThreadStackBig) { + errlogPrintf("epicsThreadGetStackSize illegal argument (too large)"); + return stackSizeTable[epicsThreadStackBig]; + } + + return stackSizeTable[stackSizeClass]; +} + +struct epicsThreadOSD {}; + /* Strictly speaking this should be a WIND_TCB, but we only need it to + * be able to create an epicsThreadId that is guaranteed never to be + * the same as any current TID, and since TIDs are pointers this works. + */ + +void epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg) +{ + static struct epicsThreadOSD threadOnceComplete; + #define EPICS_THREAD_ONCE_DONE &threadOnceComplete + int result; + + epicsThreadInit(); + result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); + assert(result == OK); + if (*id != EPICS_THREAD_ONCE_DONE) { + if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */ + *id = epicsThreadGetIdSelf(); /* mark active */ + semGive(epicsThreadOnceMutex); + func(arg); + result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); + assert(result == OK); + *id = EPICS_THREAD_ONCE_DONE; /* mark done */ + } else if (*id == epicsThreadGetIdSelf()) { + semGive(epicsThreadOnceMutex); + cantProceed("Recursive epicsThreadOnce() initialization\n"); + } else + while (*id != EPICS_THREAD_ONCE_DONE) { + /* Another thread is in the above func(arg) call. */ + semGive(epicsThreadOnceMutex); + epicsThreadSleep(epicsThreadSleepQuantum()); + result = semTake(epicsThreadOnceMutex, WAIT_FOREVER); + assert(result == OK); + } + } + semGive(epicsThreadOnceMutex); +} + +static void createFunction(EPICSTHREADFUNC func, void *parm) +{ + int tid = taskIdSelf(); + + taskVarAdd(tid,(int *)(char *)&papTSD); + /*Make sure that papTSD is still 0 after that call to taskVarAdd*/ + papTSD = 0; + (*func)(parm); + epicsExitCallAtThreadExits (); + free(papTSD); + taskVarDelete(tid,(int *)(char *)&papTSD); +} + +#ifdef ALTIVEC + #define TASK_FLAGS (VX_FP_TASK | VX_ALTIVEC_TASK) +#else + #define TASK_FLAGS (VX_FP_TASK) +#endif +epicsThreadId epicsThreadCreate(const char *name, + unsigned int priority, unsigned int stackSize, + EPICSTHREADFUNC funptr,void *parm) +{ + int tid; + if(epicsThreadOnceMutex==0) epicsThreadInit(); + if(stackSize<100) { + errlogPrintf("epicsThreadCreate %s illegal stackSize %d\n",name,stackSize); + return(0); + } + tid = taskSpawn((char *)name,getOssPriorityValue(priority), + TASK_FLAGS, stackSize, + (FUNCPTR)createFunction,(int)funptr,(int)parm, + 0,0,0,0,0,0,0,0); + if(tid==ERROR) { + errlogPrintf("epicsThreadCreate %s failure %s\n", + name,strerror(errno)); + return(0); + } + return((epicsThreadId)tid); +} + +void epicsThreadSuspendSelf() +{ + STATUS status; + + status = taskSuspend(taskIdSelf()); + if(status) errlogPrintf("epicsThreadSuspendSelf failed\n"); +} + +void epicsThreadResume(epicsThreadId id) +{ + int tid = (int)id; + STATUS status; + + status = taskResume(tid); + if(status) errlogPrintf("epicsThreadResume failed\n"); +} + +void epicsThreadExitMain(void) +{ + errlogPrintf("epicsThreadExitMain was called for vxWorks. Why?\n"); +} + +unsigned int epicsThreadGetPriority(epicsThreadId id) +{ + int tid = (int)id; + STATUS status; + int priority = 0; + + status = taskPriorityGet(tid,&priority); + if(status) errlogPrintf("epicsThreadGetPriority failed\n"); + return(getOsiPriorityValue(priority)); +} + +unsigned int epicsThreadGetPrioritySelf(void) +{ + return(epicsThreadGetPriority(epicsThreadGetIdSelf())); +} + +void epicsThreadSetPriority(epicsThreadId id,unsigned int osip) +{ + int tid = (int)id; + STATUS status; + int priority = 0; + + priority = getOssPriorityValue(osip); + status = taskPrioritySet(tid,priority); + if(status) errlogPrintf("epicsThreadSetPriority failed\n"); +} + +epicsThreadBooleanStatus epicsThreadHighestPriorityLevelBelow( + unsigned int priority, unsigned *pPriorityJustBelow) +{ + unsigned newPriority = priority - 1; + if (newPriority <= 99) { + *pPriorityJustBelow = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +epicsThreadBooleanStatus epicsThreadLowestPriorityLevelAbove( + unsigned int priority, unsigned *pPriorityJustAbove) +{ + unsigned newPriority = priority + 1; + + newPriority = priority + 1; + if (newPriority <= 99) { + *pPriorityJustAbove = newPriority; + return epicsThreadBooleanStatusSuccess; + } + return epicsThreadBooleanStatusFail; +} + +int epicsThreadIsEqual(epicsThreadId id1, epicsThreadId id2) +{ + return((id1==id2) ? 1 : 0); +} + +int epicsThreadIsSuspended(epicsThreadId id) +{ + int tid = (int)id; + return((int)taskIsSuspended(tid)); +} + +void epicsThreadSleep(double seconds) +{ + STATUS status; + int ticks; + + if(seconds<=0.0) { + ticks = 0; + } else { + ticks = seconds*sysClkRateGet() + 0.5; + if(ticks<=0) ticks = 1; + } + status = taskDelay(ticks); + if(status) errlogPrintf("epicsThreadSleep\n"); +} + +epicsThreadId epicsThreadGetIdSelf(void) +{ + return((epicsThreadId)taskIdSelf()); +} + +epicsThreadId epicsThreadGetId(const char *name) +{ + int tid = taskNameToId((char *)name); + return((epicsThreadId)(tid==ERROR?0:tid)); +} + +const char *epicsThreadGetNameSelf(void) +{ + return taskName(taskIdSelf()); +} + +void epicsThreadGetName (epicsThreadId id, char *name, size_t size) +{ + int tid = (int)id; + strncpy(name,taskName(tid),size-1); + name[size-1] = '\0'; +} + +void epicsThreadShowAll(unsigned int level) +{ + taskShow(0,2); +} + +void epicsThreadShow(epicsThreadId id, unsigned int level) +{ + int tid = (int)id; + + if (level > 1) level = 1; + if (tid) + taskShow(tid, level); +} + +/* The following algorithm was thought of by Andrew Johnson APS/ASD . + * The basic idea is to use a single vxWorks task variable. + * The task variable is papTSD, which is an array of pointers to the TSD + * The array size is equal to the number of epicsThreadPrivateIds created + 1 + * when epicsThreadPrivateSet is called. + * Until the first call to epicsThreadPrivateCreate by a application papTSD=0 + * After first call papTSD[0] is value of nepicsThreadPrivate when + * epicsThreadPrivateSet was last called by the thread. This is also + * the value of epicsThreadPrivateId. + * The algorithm allows for epicsThreadPrivateCreate being called after + * the first call to epicsThreadPrivateSet. + */ +epicsThreadPrivateId epicsThreadPrivateCreate() +{ + static int lock = 0; + epicsThreadPrivateId id; + + epicsThreadInit(); + /*lock is necessary because ++nepicsThreadPrivate may not be indivisible*/ + while(!vxTas(&lock)) taskDelay(1); + id = (epicsThreadPrivateId)++nepicsThreadPrivate; + lock = 0; + return(id); +} + +void epicsThreadPrivateDelete(epicsThreadPrivateId id) +{ + /*nothing to delete */ + return; +} + +/* + * No mutex is necessary here because by definition + * thread variables are local to a single thread. + */ +void epicsThreadPrivateSet (epicsThreadPrivateId id, void *pvt) +{ + int int_id = (int)id; + int nepicsThreadPrivateOld = 0; + + if (papTSD) nepicsThreadPrivateOld = (int)papTSD[0]; + + if (!papTSD || nepicsThreadPrivateOld < int_id) { + void **papTSDold = papTSD; + int i; + + papTSD = callocMustSucceed(int_id + 1,sizeof(void *), + "epicsThreadPrivateSet"); + papTSD[0] = (void *)(int_id); + for (i = 1; i <= nepicsThreadPrivateOld; i++) { + papTSD[i] = papTSDold[i]; + } + free (papTSDold); + } + papTSD[int_id] = pvt; +} + +void *epicsThreadPrivateGet(epicsThreadPrivateId id) +{ + int int_id = (int)id; + + /* + * If epicsThreadPrivateGet() is called before epicsThreadPrivateSet() + * for any particular thread, the value returned is always NULL. + */ + if ( papTSD && int_id <= (int) papTSD[0] ) { + return papTSD[int_id]; + } + return NULL; +} + +double epicsThreadSleepQuantum () +{ + double HZ = sysClkRateGet (); + return 1.0 / HZ; +} diff --git a/src/libCom/osi/os/vxWorks/osdThread.h b/src/libCom/osi/os/vxWorks/osdThread.h new file mode 100644 index 000000000..2ee9f2d46 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdThread.h @@ -0,0 +1,13 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef osdThreadh +#define osdThreadh + +#endif /* osdThreadh */ diff --git a/src/libCom/osi/os/vxWorks/osdTime.cpp b/src/libCom/osi/os/vxWorks/osdTime.cpp new file mode 100644 index 000000000..a0e2422e9 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdTime.cpp @@ -0,0 +1,80 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include +#include +#include + +#include "epicsTime.h" +#include "osiNTPTime.h" +#include "osiClockTime.h" +#include "generalTimeSup.h" +#include "envDefs.h" + +#define NTP_REQUEST_TIMEOUT 4 /* seconds */ + +static const char *pserverAddr = NULL; +extern char* sysBootLine; + +static int timeRegister(void) +{ + /* If TIMEZONE not defined, set it from EPICS_TIMEZONE */ + if (getenv("TIMEZONE") == NULL) { + const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE); + if (timezone == NULL) { + printf("NTPTime_Init: No Time Zone Information\n"); + } else { + epicsEnvSet("TIMEZONE", timezone); + } + } + + NTPTime_Init(100); /* init NTP first so it can be used to sync SysTime */ + ClockTime_Init(CLOCKTIME_SYNC); + return 1; +} +static int done = timeRegister(); + +int osdNTPGet(struct timespec *ts) +{ + return sntpcTimeGet(const_cast(pserverAddr), + NTP_REQUEST_TIMEOUT * sysClkRateGet(), ts); +} + +void osdNTPInit(void) +{ + pserverAddr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); + if (!pserverAddr) { /* use the boot host */ + BOOT_PARAMS bootParms; + static char host_addr[BOOT_ADDR_LEN]; + bootStringToStruct(sysBootLine, &bootParms); + /* bootParms.had = host IP address */ + strncpy(host_addr, bootParms.had, BOOT_ADDR_LEN); + pserverAddr = host_addr; + } +} + +void osdNTPReport(void) +{ + printf("NTP Server = %s\n", pserverAddr); +} + + +// vxWorks localtime_r interface does not match POSIX standards +int epicsTime_localtime(const time_t *clock, struct tm *result) +{ + return localtime_r(clock, result) == OK ? epicsTimeOK : epicsTimeERROR; +} + +// vxWorks gmtime_r interface does not match POSIX standards +int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) +{ + return gmtime_r(pAnsiTime, pTM) == OK ? epicsTimeOK : epicsTimeERROR; +} diff --git a/src/libCom/osi/os/vxWorks/osdTime.h b/src/libCom/osi/os/vxWorks/osdTime.h new file mode 100644 index 000000000..a816c7ae3 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdTime.h @@ -0,0 +1,33 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* Following needed for struct timeval */ +#include + +#ifndef osdTimeh +#define osdTimeh + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void osdNTPInit(void); +int osdNTPGet(struct timespec *); +void osdNTPReport(void); + +#define osdTickRateGet sysClkRateGet +#define osdTickGet tickGet + +#ifdef __cplusplus +} +#endif +#endif /* ifndef osdTimeh */ diff --git a/src/libCom/osi/os/vxWorks/osdVME.h b/src/libCom/osi/os/vxWorks/osdVME.h new file mode 100644 index 000000000..e616f2f3c --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdVME.h @@ -0,0 +1,15 @@ +/*************************************************************************\ +* 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 */ + +/* + * OS-dependent VME support + */ +#include diff --git a/src/libCom/osi/os/vxWorks/osdWireConfig.h b/src/libCom/osi/os/vxWorks/osdWireConfig.h new file mode 100644 index 000000000..b1aa6e232 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdWireConfig.h @@ -0,0 +1,25 @@ +/* + * vxWorks version of + * osdWireConfig.h + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osdWireConfig_h +#define osdWireConfig_h + +#include + +#if _BYTE_ORDER == _LITTLE_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_LITTLE +#elif _BYTE_ORDER == _BIG_ENDIAN +# define EPICS_BYTE_ORDER EPICS_ENDIAN_BIG +#else +# error EPICS hasnt been ported to _BYTE_ORDER specified by vxWorks +#endif + +/* for now, assume that vxWorks doesnt run on weird arch like ARM NWFP */ +#define EPICS_FLOAT_WORD_ORDER EPICS_BYTE_ORDER + +#endif /* ifdef osdWireConfig_h */ diff --git a/src/libCom/osi/os/vxWorks/osiFileName.h b/src/libCom/osi/os/vxWorks/osiFileName.h new file mode 100644 index 000000000..b79203992 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osiFileName.h @@ -0,0 +1,21 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * osiFileName.h + * Author: Jeff Hill + * + * + */ +#ifndef osiFileNameH +#define osiFileNameH + +#include "unixFileName.h" + +#endif /* osiFileNameH */ diff --git a/src/libCom/osi/os/vxWorks/task_params.h b/src/libCom/osi/os/vxWorks/task_params.h new file mode 100644 index 000000000..246c2b660 --- /dev/null +++ b/src/libCom/osi/os/vxWorks/task_params.h @@ -0,0 +1,185 @@ +/*************************************************************************\ +* 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 */ + +/* Parameters for tasks on IOC */ +/* + * Authors: Andy Kozubal, Jeff Hill, and Bob Dalesio + * Date: 2-24-89 + */ + +#ifndef INCtaskLibh +#include +#endif + +#define VXTASKIDSELF 0 + +/* Task Names */ +#define EVENTSCAN_NAME "scanEvent" +#define SCANONCE_NAME "scanOnce" +#define SMCMD_NAME "smCommand" +#define SMRESP_NAME "smResponse" +#define ABDONE_NAME "abDone" +#define ABSCAN_NAME "abScan" +#define ABCOS_NAME "abBiCosScanner" +#define MOMENTARY_NAME "momentary" +#define WFDONE_NAME "wfDone" +#define SEQUENCER_NAME "sequencer" +#define BKPT_CONT_NAME "bkptCont" +#define SCANNER_NAME "scanner" +#define REQ_SRVR_NAME "CA_TCP" +#define CA_CLIENT_NAME "CA_client" +#define CA_EVENT_NAME "CA_event" +#define CAST_SRVR_NAME "CA_UDP" +#define CA_REPEATER_NAME "CA_repeater" +#define CA_ONLINE_NAME "CA_online" +#define TASKWD_NAME "taskwd" +#define SMIOTEST_NAME "smInout" +#define SMROTTEST_NAME "smRotate" +#define EVENT_PEND_NAME "event_task" +#define XY240_NAME "xy_240_scan" +#define GPIBLINK_NAME "gpibLink" +#define BBLINK_NAME "bbLinkTask" +#define BBTXLINK_NAME "bbTx" +#define BBRXLINK_NAME "bbRx" +#define BBWDTASK_NAME "bbwd" +#define ERRLOG_NAME "errlog" +#define LOG_RESTART_NAME "logRestart" + +/* Task priorities */ +#define SCANONCE_PRI 129 /* scan one time */ +/*DO NOT RUN ANY RECORD PROCESSING TASKS AT HIGHER PRIORITY THAN _netTask=50*/ +#define CALLBACK_PRI_LOW 140 /* callback task - generall callback task */ +#define CALLBACK_PRI_MEDIUM 135 /* callback task - generall callback task */ +#define CALLBACK_PRI_HIGH 128 /* callback task - generall callback task */ +#define EVENTSCAN_PRI 129 /* Event Scanner - Runs on a global event */ +#define SMCMD_PRI 120 /* Stepper Motor Command Task - Waits for cmds */ +#define SMRESP_PRI 121 /* Stepper Motor Resp Task - Waits for resps */ +#define ABCOS_PRI 121 /* Allen-Bradley Binary Input COS io_event wakeup */ +#define ABDONE_PRI 122 /* Allen-Bradley Resp Task - Interrupt Driven */ +#define ABSCAN_PRI 123 /* Allen-Bradley Scan Task - Base Rate .1 secs */ +#define BBLINK_PRI 124 +#define BBWDTASK_PRI 123 /* BitBus watchdog task */ +#define BBRXLINK_PRI 124 /* BitBus link task */ +#define BBTXLINK_PRI 125 /* BitBus link task */ +#define GPIBLINK_PRI 125 /* GPIB link task */ +#define MOMENTARY_PRI 126 /* Momentary output - posted from watchdog */ +#define WFDONE_PRI 127 /* Waveform Task - Base Rate of .1 second */ +#define PERIODSCAN_PRI 139 /* Periodic Scanners - Slowest rate */ +#define DB_CA_PRI 149 /*database to channel access*/ +#define SEQUENCER_PRI 151 +#define XY240_PRI 160 /* xy 240 dio scanner */ +#define SCANNER_PRI 170 +#define REQ_SRVR_PRI 181 /* Channel Access TCP request server*/ +#define CA_CLIENT_PRI 180 /* Channel Access clients */ +#define CA_REPEATER_PRI 181 /* Channel Access repeater */ +#define ERRLOG_PRI CA_REPEATER_PRI /*error logger task*/ +#define CAST_SRVR_PRI 182 /* Channel Access broadcast server */ +#define CA_ONLINE_PRI 183 /* Channel Access server online notify */ +#define TASKWD_PRI 200 /* Watchdog Scan Task - runs every 6 seconds */ +#define SMIOTEST_PRI 205 /* Stepper Mtr in/out test - runs every .1 sec */ +#define SMROTTEST_PRI 205 /* Stepper Mtr rotate test - runs every .1 sec */ +#define LOG_RESTART_PRI 200 /* Log server connection watch dog */ + +/* Task delay times (seconds) */ +#define TASKWD_DELAY 6 + +/* Task delay times (tics) */ +#define ABSCAN_RATE (sysClkRateGet()/6) +#define SEQUENCER_DELAY (sysClkRateGet()/5) +#define SCANNER_DELAY (sysClkRateGet()/5) +#define CA_ONLINE_DELAY (sysClkRateGet()*15) +#define LOG_RESTART_DELAY (sysClkRateGet()*30) + +/* Task creation options */ +#define ERRLOG_OPT VX_FP_TASK +#define EVENTSCAN_OPT VX_FP_TASK +#define SCANONCE_OPT VX_FP_TASK +#define CALLBACK_OPT VX_FP_TASK +#define SMCMD_OPT VX_FP_TASK +#define SMRESP_OPT VX_FP_TASK +#define ABDONE_OPT VX_FP_TASK +#define ABCOS_OPT VX_FP_TASK +#define ABSCAN_OPT VX_FP_TASK +#define MOMENTARY_OPT VX_FP_TASK +#define PERIODSCAN_OPT VX_FP_TASK +#define WFDONE_OPT VX_FP_TASK +#define SEQUENCER_OPT VX_FP_TASK | VX_STDIO +#define BKPT_CONT_OPT VX_FP_TASK +#define SCANNER_OPT VX_FP_TASK +#define REQ_SRVR_OPT VX_FP_TASK +#define CAST_SRVR_OPT VX_FP_TASK +#define CA_CLIENT_OPT VX_FP_TASK +#define CA_REPEATER_OPT VX_FP_TASK +#define CA_ONLINE_OPT VX_FP_TASK +#define TASKWD_OPT VX_FP_TASK +#define SMIOTEST_OPT VX_FP_TASK +#define SMROTTEST_OPT VX_FP_TASK +#define EVENT_PEND_OPT VX_FP_TASK +#define GPIBLINK_OPT VX_FP_TASK|VX_STDIO +#define BBLINK_OPT VX_FP_TASK|VX_STDIO +#define BBTXLINK_OPT VX_FP_TASK|VX_STDIO +#define BBRXLINK_OPT VX_FP_TASK|VX_STDIO +#define BBWDTASK_OPT VX_FP_TASK|VX_STDIO +#define DB_CA_OPT (VX_FP_TASK | VX_STDIO) +#define XY_240_OPT (0) /* none */ +#define LOG_RESTART_OPT (VX_FP_TASK) + + +/* + * Task stack sizes + * + * (original stack sizes are appropriate for the 68k) + * ARCH_STACK_FACTOR allows scaling the stacks on particular + * processor architectures + */ +#if CPU_FAMILY == MC680X0 +#define ARCH_STACK_FACTOR 1 +#elif CPU_FAMILY == SPARC +#define ARCH_STACK_FACTOR 2 +#else +#define ARCH_STACK_FACTOR 2 +#endif + +#define ERRLOG_STACK (4000*ARCH_STACK_FACTOR) +#define EVENTSCAN_STACK (11000*ARCH_STACK_FACTOR) +#define SCANONCE_STACK (11000*ARCH_STACK_FACTOR) +#define CALLBACK_STACK (11000*ARCH_STACK_FACTOR) +#define SMCMD_STACK (3000*ARCH_STACK_FACTOR) +#define SMRESP_STACK (3000*ARCH_STACK_FACTOR) +#define ABCOS_STACK (3000*ARCH_STACK_FACTOR) +#define ABDONE_STACK (3000*ARCH_STACK_FACTOR) +#define ABSCAN_STACK (3000*ARCH_STACK_FACTOR) +#define MOMENTARY_STACK (2000*ARCH_STACK_FACTOR) +#define PERIODSCAN_STACK (11000*ARCH_STACK_FACTOR) +#define WFDONE_STACK (9000*ARCH_STACK_FACTOR) +#define SEQUENCER_STACK (5500*ARCH_STACK_FACTOR) +#define BKPT_CONT_STACK (11000*ARCH_STACK_FACTOR) +#define SCANNER_STACK (3048*ARCH_STACK_FACTOR) +#define RSP_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define REQ_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define CAST_SRVR_STACK (5096*ARCH_STACK_FACTOR) +#define CA_CLIENT_STACK (11000*ARCH_STACK_FACTOR) +#define CA_REPEATER_STACK (5096*ARCH_STACK_FACTOR) +#define CA_ONLINE_STACK (3048*ARCH_STACK_FACTOR) +#define TASKWD_STACK (2000*ARCH_STACK_FACTOR) +#define SMIOTEST_STACK (2000*ARCH_STACK_FACTOR) +#define SMROTTEST_STACK (2000*ARCH_STACK_FACTOR) +#define EVENT_PEND_STACK (5096*ARCH_STACK_FACTOR) +#define TIMESTAMP_STACK (4000*ARCH_STACK_FACTOR) +#define GPIBLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBRXLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBTXLINK_STACK (5000*ARCH_STACK_FACTOR) +#define BBWDTASK_STACK (5000*ARCH_STACK_FACTOR) +#define DB_CA_STACK (11000*ARCH_STACK_FACTOR) +#define XY_240_STACK (4096*ARCH_STACK_FACTOR) +#define LOG_RESTART_STACK (0x1000*ARCH_STACK_FACTOR) + diff --git a/src/libCom/osi/os/vxWorks/veclist.c b/src/libCom/osi/os/vxWorks/veclist.c new file mode 100644 index 000000000..7cd22883f --- /dev/null +++ b/src/libCom/osi/os/vxWorks/veclist.c @@ -0,0 +1,227 @@ +/*************************************************************************\ +* 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 + * @(#)veclist.c 1.10 + * + * list fuctions attached to the interrupt vector table + * + * Created 28Mar89 Jeffrey O. Hill + * johill@lanl.gov + * (505) 665 1831 + * + */ + +/* + * + * makefile + * + * + * V5VW = /.../vx/v502b + * + * veclist.o: + * cc68k -c -DCPU_FAMILY=MC680X0 -I$(V5VW)/h veclist.c + * + * + */ + +#include "vxWorks.h" +#include "stdio.h" +#include "string.h" +#include "intLib.h" +#include "vxLib.h" +#include "iv.h" +#include "ctype.h" +#include "sysSymTbl.h" + +static const char sccsID[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd"; + +/* + * + * VME bus dependent + * + */ +#define NVEC 0x100 + +static char *ignore_list[] = {"_excStub","_excIntStub"}; + +int veclist(int); +int cISRTest(FUNCPTR proutine, FUNCPTR *ppisr, void **pparam); +static void *fetch_pointer(unsigned char *); + + +/* + * + * veclist() + * + */ +int veclist(int all) +{ + int vec; + int value; + SYM_TYPE type; + char name[MAX_SYS_SYM_LEN]; + char function_type[10]; + FUNCPTR proutine; + FUNCPTR pCISR; + int cRoutine; + void *pparam; + int status; + unsigned i; + + for(vec=0; vec +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "errlog.h" +#include "epicsGeneralTime.h" +#include "generalTimeSup.h" +#include "iocsh.h" +#include "osiClockTime.h" +#include "taskwd.h" + +#define NSEC_PER_SEC 1000000000 +#define ClockTimeSyncInterval 60.0 + + +static struct { + int synchronize; + int synchronized; + epicsEventId loopEvent; + epicsTimeStamp syncTime; + int syncFromPriority; + epicsMutexId lock; +} ClockTimePvt; + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + +#ifdef CLOCK_REALTIME +/* This code is not used on systems without Posix CLOCK_REALTIME such + * as Darwin, but the only way to detect that is from the OS headers, + * so the Makefile can't exclude building this file on those systems. + */ + +/* Forward references */ + +static int ClockTimeGetCurrent(epicsTimeStamp *pDest); +static void ClockTimeSync(void *dummy); + + +/* ClockTime_Report iocsh command */ +static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; +static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; +static const iocshFuncDef ReportFuncDef = {"ClockTime_Report", 1, ReportArgs}; +static void ReportCallFunc(const iocshArgBuf *args) +{ + ClockTime_Report(args[0].ival); +} + +/* ClockTime_Shutdown iocsh command */ +static const iocshFuncDef ShutdownFuncDef = {"ClockTime_Shutdown", 0, NULL}; +static void ShutdownCallFunc(const iocshArgBuf *args) +{ + ClockTime_Shutdown(NULL); +} + + +/* Initialization */ + +static void ClockTime_InitOnce(void *psync) +{ + ClockTimePvt.synchronize = *(int *)psync; + ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); + ClockTimePvt.lock = epicsMutexCreate(); + + if (ClockTimePvt.synchronize) { + /* Start the sync thread */ + epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + ClockTimeSync, NULL); + } + + epicsAtExit(ClockTime_Shutdown, NULL); + + /* Register the iocsh commands */ + iocshRegister(&ReportFuncDef, ReportCallFunc); + if (ClockTimePvt.synchronize) + iocshRegister(&ShutdownFuncDef, ShutdownCallFunc); + + /* Finally register as a time provider */ + generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY, + ClockTimeGetCurrent); +} + +void ClockTime_Init(int synchronize) +{ + epicsThreadOnce(&onceId, ClockTime_InitOnce, &synchronize); +} + + +/* Shutdown */ + +void ClockTime_Shutdown(void *dummy) +{ + ClockTimePvt.synchronize = 0; + epicsEventSignal(ClockTimePvt.loopEvent); +} + + +/* Synchronization thread */ + +static void ClockTimeSync(void *dummy) +{ + taskwdInsert(0, NULL, NULL); + + for (epicsEventWaitWithTimeout(ClockTimePvt.loopEvent, + ClockTimeSyncInterval); + ClockTimePvt.synchronize; + epicsEventWaitWithTimeout(ClockTimePvt.loopEvent, + ClockTimeSyncInterval)) { + epicsTimeStamp timeNow; + int priority; + + if (generalTimeGetExceptPriority(&timeNow, &priority, + LAST_RESORT_PRIORITY) == epicsTimeOK) { + struct timespec clockNow; + + epicsTimeToTimespec(&clockNow, &timeNow); + if (clock_settime(CLOCK_REALTIME, &clockNow)) { + errlogPrintf("ClockTimeSync: clock_settime failed\n"); + continue; + } + + epicsMutexMustLock(ClockTimePvt.lock); + ClockTimePvt.synchronized = 1; + ClockTimePvt.syncFromPriority = priority; + ClockTimePvt.syncTime = timeNow; + epicsMutexUnlock(ClockTimePvt.lock); + } + } + + ClockTimePvt.synchronized = 0; + taskwdRemove(0); +} + + +/* Time Provider Routine */ + +static int ClockTimeGetCurrent(epicsTimeStamp *pDest) +{ + struct timespec clockNow; + + /* If a Hi-Res clock is available and works, use it */ + #ifdef CLOCK_REALTIME_HR + clock_gettime(CLOCK_REALTIME_HR, &clockNow) && + #endif + clock_gettime(CLOCK_REALTIME, &clockNow); + + if (!ClockTimePvt.synchronized && + clockNow.tv_sec < POSIX_TIME_AT_EPICS_EPOCH) { + clockNow.tv_sec = POSIX_TIME_AT_EPICS_EPOCH + 86400; + clockNow.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &clockNow); + errlogPrintf("WARNING: OS Clock time was read before being set.\n" + "Using 1990-01-02 00:00:00.000000 UTC\n"); + } + + epicsTimeFromTimespec(pDest, &clockNow); + return 0; +} + + +#endif /* CLOCK_REALTIME */ +/* Allow the following report routine to be compiled anyway + * to avoid getting a build warning from ranlib. + */ + +/* Status Report */ + +int ClockTime_Report(int level) +{ + if (onceId == EPICS_THREAD_ONCE_INIT) { + printf("OS Clock driver not initialized.\n"); + } else if (ClockTimePvt.synchronize) { + epicsMutexMustLock(ClockTimePvt.lock); + if (ClockTimePvt.synchronized) { + printf("OS Clock driver has synchronized to a priority=%d provider\n", + ClockTimePvt.syncFromPriority); + if (level) { + char lastSync[32]; + epicsTimeToStrftime(lastSync, sizeof(lastSync), + "%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.syncTime); + printf("Last successful sync was at %s\n", lastSync); + } + printf("Syncronization interval = %.0f seconds\n", + ClockTimeSyncInterval); + } else + printf("OS Clock driver has *not* yet synchronized\n"); + + epicsMutexUnlock(ClockTimePvt.lock); + } else { + printf("OS Clock synchronization thread not running.\n"); + } + return 0; +} diff --git a/src/libCom/osi/osiClockTime.h b/src/libCom/osi/osiClockTime.h new file mode 100644 index 000000000..17eacab3e --- /dev/null +++ b/src/libCom/osi/osiClockTime.h @@ -0,0 +1,26 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_osiClockTime_H +#define INC_osiClockTime_H + +#define CLOCKTIME_NOSYNC 0 +#define CLOCKTIME_SYNC 1 + +#ifdef __cplusplus +extern "C" { +#endif + +void ClockTime_Init(int synchronize); +void ClockTime_Shutdown(void *dummy); +int ClockTime_Report(int level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/osi/osiNTPTime.c b/src/libCom/osi/osiNTPTime.c new file mode 100644 index 000000000..d085cca31 --- /dev/null +++ b/src/libCom/osi/osiNTPTime.c @@ -0,0 +1,284 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Original Author: Marty Kraimer + * Date: 16JUN2000 + */ + +#include +#include +#include +#include +#include +#include + +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsTypes.h" +#include "cantProceed.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "epicsGeneralTime.h" +#include "generalTimeSup.h" +#include "iocsh.h" +#include "osdTime.h" +#include "osiNTPTime.h" +#include "taskwd.h" + +#define NSEC_PER_SEC 1000000000 +#define NTPTimeSyncInterval 60.0 +#define NTPTimeSyncRetries 4 + + +static struct { + int synchronize; + int synchronized; + epicsEventId loopEvent; + int syncsFailed; + epicsMutexId lock; + epicsTimeStamp syncTime; + epicsUInt32 syncTick; + epicsTimeStamp clockTime; + epicsUInt32 clockTick; + epicsUInt32 nsecsPerTick; + epicsUInt32 ticksPerSecond; + epicsUInt32 ticksToSkip; + double tickRate; +} NTPTimePvt; + +static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + +/* Forward references */ + +static int NTPTimeGetCurrent(epicsTimeStamp *pDest); +static void NTPTimeSync(void *dummy); + + +/* NTPTime_Report iocsh command */ +static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; +static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; +static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs}; +static void ReportCallFunc(const iocshArgBuf *args) +{ + NTPTime_Report(args[0].ival); +} + +/* NTPTime_Shutdown iocsh command */ +static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL}; +static void ShutdownCallFunc(const iocshArgBuf *args) +{ + NTPTime_Shutdown(NULL); +} + + +/* Initialization */ + +static void NTPTime_InitOnce(void *pprio) +{ + struct timespec timespecNow; + + NTPTimePvt.synchronize = 1; + NTPTimePvt.synchronized = 0; + NTPTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty); + NTPTimePvt.syncsFailed = 0; + NTPTimePvt.lock = epicsMutexCreate(); + NTPTimePvt.ticksPerSecond = osdTickRateGet(); + NTPTimePvt.nsecsPerTick = NSEC_PER_SEC / NTPTimePvt.ticksPerSecond; + + /* Initialize OS-dependent code */ + osdNTPInit(); + + /* Try to sync with NTP server */ + if (!osdNTPGet(×pecNow)) { + NTPTimePvt.syncTick = osdTickGet(); + if (timespecNow.tv_sec > POSIX_TIME_AT_EPICS_EPOCH && epicsTimeOK == + epicsTimeFromTimespec(&NTPTimePvt.syncTime, ×pecNow)) { + NTPTimePvt.clockTick = NTPTimePvt.syncTick; + NTPTimePvt.clockTime = NTPTimePvt.syncTime; + NTPTimePvt.synchronized = 1; + } + } + + /* Start the sync thread */ + epicsThreadCreate("NTPTimeSync", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + NTPTimeSync, NULL); + + epicsAtExit(NTPTime_Shutdown, NULL); + + /* Register the iocsh commands */ + iocshRegister(&ReportFuncDef, ReportCallFunc); + iocshRegister(&ShutdownFuncDef, ShutdownCallFunc); + + /* Finally register as a time provider */ + generalTimeRegisterCurrentProvider("NTP", *(int *)pprio, NTPTimeGetCurrent); +} + +void NTPTime_Init(int priority) +{ + epicsThreadOnce(&onceId, NTPTime_InitOnce, &priority); +} + + +/* Shutdown */ + +void NTPTime_Shutdown(void *dummy) +{ + NTPTimePvt.synchronize = 0; + epicsEventSignal(NTPTimePvt.loopEvent); +} + + +/* Synchronization thread */ + +static void NTPTimeSync(void *dummy) +{ + taskwdInsert(0, NULL, NULL); + + for (epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval); + NTPTimePvt.synchronize; + epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval)) { + int status; + struct timespec timespecNow; + epicsTimeStamp timeNow; + epicsUInt32 tickNow; + double diff; + double ntpDelta; + + status = osdNTPGet(×pecNow); + tickNow = osdTickGet(); + + if (status) { + if (++NTPTimePvt.syncsFailed > NTPTimeSyncRetries && + NTPTimePvt.synchronized) { + errlogPrintf("NTPTimeSync: NTP requests failing - %s\n", + strerror(errno)); + NTPTimePvt.synchronized = 0; + } + continue; + } + + if (timespecNow.tv_sec <= POSIX_TIME_AT_EPICS_EPOCH || + epicsTimeFromTimespec(&timeNow, ×pecNow) == epicsTimeERROR) { + errlogPrintf("NTPTimeSync: Bad time received from NTP server\n"); + NTPTimePvt.synchronized = 0; + continue; + } + + ntpDelta = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.syncTime); + if (ntpDelta <= 0.0 && NTPTimePvt.synchronized) { + errlogPrintf("NTPTimeSync: NTP time not increasing, delta = %g\n", + ntpDelta); + NTPTimePvt.synchronized = 0; + continue; + } + + NTPTimePvt.syncsFailed = 0; + if (!NTPTimePvt.synchronized) { + errlogPrintf("NTPTimeSync: Sync recovered.\n"); + } + + epicsMutexMustLock(NTPTimePvt.lock); + diff = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.clockTime); + if (diff >= 0.0) { + NTPTimePvt.ticksToSkip = 0; + } else { /* dont go back in time */ + NTPTimePvt.ticksToSkip = -diff * NTPTimePvt.ticksPerSecond; + } + NTPTimePvt.clockTick = tickNow; + NTPTimePvt.clockTime = timeNow; + NTPTimePvt.synchronized = 1; + epicsMutexUnlock(NTPTimePvt.lock); + + NTPTimePvt.tickRate = (tickNow - NTPTimePvt.syncTick) / ntpDelta; + NTPTimePvt.syncTick = tickNow; + NTPTimePvt.syncTime = timeNow; + } + + NTPTimePvt.synchronized = 0; + taskwdRemove(0); +} + + +/* Time Provider Routine */ + +static int NTPTimeGetCurrent(epicsTimeStamp *pDest) +{ + epicsUInt32 tickNow; + epicsUInt32 ticksSince; + + if (!NTPTimePvt.synchronized) + return epicsTimeERROR; + + epicsMutexMustLock(NTPTimePvt.lock); + + tickNow = osdTickGet(); + ticksSince = tickNow - NTPTimePvt.clockTick; + + if (NTPTimePvt.ticksToSkip <= ticksSince) { + if (NTPTimePvt.ticksToSkip) { + ticksSince -= NTPTimePvt.ticksToSkip; + NTPTimePvt.ticksToSkip = 0; + } + + if (ticksSince) { + epicsUInt32 secsSince = ticksSince / NTPTimePvt.ticksPerSecond; + ticksSince -= secsSince * NTPTimePvt.ticksPerSecond; + + NTPTimePvt.clockTime.nsec += ticksSince * NTPTimePvt.nsecsPerTick; + if (NTPTimePvt.clockTime.nsec >= NSEC_PER_SEC) { + secsSince++; + NTPTimePvt.clockTime.nsec -= NSEC_PER_SEC; + } + NTPTimePvt.clockTime.secPastEpoch += secsSince; + NTPTimePvt.clockTick = tickNow; + } + } + + *pDest = NTPTimePvt.clockTime; + + epicsMutexUnlock(NTPTimePvt.lock); + return 0; +} + + +/* Status Report */ + +int NTPTime_Report(int level) +{ + if (onceId == EPICS_THREAD_ONCE_INIT) { + printf("NTP driver not initialized\n"); + } else if (NTPTimePvt.synchronize) { + printf("NTP driver %s synchronized with server\n", + NTPTimePvt.synchronized ? "is" : "is *not*"); + if (NTPTimePvt.syncsFailed) { + printf("Last successful sync was %.1f minutes ago\n", + NTPTimePvt.syncsFailed * NTPTimeSyncInterval / 60.0); + } + if (level) { + char lastSync[32]; + epicsTimeToStrftime(lastSync, sizeof(lastSync), + "%Y-%m-%d %H:%M:%S.%06f", &NTPTimePvt.syncTime); + printf("Syncronization interval = %.1f seconds\n", + NTPTimeSyncInterval); + printf("Last synchronized at %s\n", + lastSync); + printf("OS tick rate = %u Hz (nominal)\n", + NTPTimePvt.ticksPerSecond); + printf("Measured tick rate = %.3f Hz\n", + NTPTimePvt.tickRate); + osdNTPReport(); + } + } else { + printf("NTP synchronization thread not running.\n"); + } + return 0; +} diff --git a/src/libCom/osi/osiNTPTime.h b/src/libCom/osi/osiNTPTime.h new file mode 100644 index 000000000..21b29d118 --- /dev/null +++ b/src/libCom/osi/osiNTPTime.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_osiNTPTime_H +#define INC_osiNTPTime_H + +#ifdef __cplusplus +extern "C" { +#endif + +void NTPTime_Init(int priority); +void NTPTime_Shutdown(void *dummy); +int NTPTime_Report(int level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/osi/osiPoolStatus.h b/src/libCom/osi/osiPoolStatus.h new file mode 100644 index 000000000..24cd9c4a6 --- /dev/null +++ b/src/libCom/osi/osiPoolStatus.h @@ -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. +\*************************************************************************/ + +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * + * Author: Jeff Hill + * + * Functions which interrogate the state of the system wide pool + * + */ + +#include +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * tests to see if there is sufficent space for a block of the requested size + * along with whatever additional free space is necessary to keep the system running + * reliably + * + * this routine is called quite frequently so an efficent implementation is important + */ +epicsShareFunc int epicsShareAPI osiSufficentSpaceInPool ( size_t contiguousBlockSize ); + +#ifdef __cplusplus +} +#endif + +#include "osdPoolStatus.h" + diff --git a/src/libCom/osi/osiProcess.h b/src/libCom/osi/osiProcess.h new file mode 100644 index 000000000..ddf375a57 --- /dev/null +++ b/src/libCom/osi/osiProcess.h @@ -0,0 +1,46 @@ +/*************************************************************************\ +* 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 + * + * Operating System Independent Interface to Process Environment + * + * Author: Jeff Hill + * + */ +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum osiGetUserNameReturn { + osiGetUserNameFail, + osiGetUserNameSuccess} osiGetUserNameReturn; +epicsShareFunc osiGetUserNameReturn epicsShareAPI osiGetUserName (char *pBuf, unsigned bufSize); + +/* + * Spawn detached process with named executable, but return + * osiSpawnDetachedProcessNoSupport if the local OS does not + * support heavy weight processes. + */ +typedef enum osiSpawnDetachedProcessReturn { + osiSpawnDetachedProcessFail, + osiSpawnDetachedProcessSuccess, + osiSpawnDetachedProcessNoSupport} osiSpawnDetachedProcessReturn; + +epicsShareFunc osiSpawnDetachedProcessReturn epicsShareAPI osiSpawnDetachedProcess + (const char *pProcessName, const char *pBaseExecutableName); + +#ifdef __cplusplus +} +#endif + diff --git a/src/libCom/osi/osiSock.c b/src/libCom/osi/osiSock.c new file mode 100644 index 000000000..76eb3730e --- /dev/null +++ b/src/libCom/osi/osiSock.c @@ -0,0 +1,191 @@ +/*************************************************************************\ +* 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 + * + * socket support library generic code + * + * 7-1-97 -joh- + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsAssert.h" +#include "epicsSignal.h" +#include "epicsStdio.h" +#include "osiSock.h" + +#define nDigitsDottedIP 4u +#define chunkSize 8u + +#define makeMask(NBITS) ( ( 1u << ( (unsigned) NBITS) ) - 1u ) + +/* + * sockAddrAreIdentical() + * (returns true if addresses are identical) + */ +int epicsShareAPI sockAddrAreIdentical + ( const osiSockAddr *plhs, const osiSockAddr *prhs ) +{ + int match; + + if ( plhs->sa.sa_family != prhs->sa.sa_family ) { + match = 0; + } + else if ( plhs->sa.sa_family != AF_INET ) { + match = 0; + } + else if ( plhs->ia.sin_addr.s_addr != prhs->ia.sin_addr.s_addr ) { + match = 0; + } + else if ( plhs->ia.sin_port != prhs->ia.sin_port ) { + match = 0; + } + else { + match = 1; + } + return match; +} + +/* + * sockAddrToA() + * (convert socket address to ASCII host name) + */ +unsigned epicsShareAPI sockAddrToA ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ) +{ + if ( bufSize < 1 ) { + return 0; + } + + if ( paddr->sa_family != AF_INET ) { + static const char * pErrStr = ""; + unsigned len = strlen ( pErrStr ); + if ( len < bufSize ) { + strcpy ( pBuf, pErrStr ); + return len; + } + else { + strncpy ( pBuf, "", bufSize-1 ); + pBuf[bufSize-1] = '\0'; + return bufSize-1; + } + } + else { + const struct sockaddr_in * paddr_in = + (const struct sockaddr_in *) paddr; + return ipAddrToA ( paddr_in, pBuf, bufSize ); + } +} + +/* + * ipAddrToA() + * (convert IP address to ASCII host name) + */ +unsigned epicsShareAPI ipAddrToA ( + const struct sockaddr_in * paddr, char * pBuf, unsigned bufSize ) +{ + unsigned len = ipAddrToHostName ( + & paddr->sin_addr, pBuf, bufSize ); + if ( len == 0 ) { + len = ipAddrToDottedIP ( paddr, pBuf, bufSize ); + } + else { + unsigned reducedSize = bufSize - len; + int status = epicsSnprintf ( + &pBuf[len], reducedSize, ":%hu", + ntohs (paddr->sin_port) ); + if ( status > 0 ) { + unsigned portSize = (unsigned) status; + if ( portSize < reducedSize ) { + len += portSize; + } + } + } + return len; +} + +/* + * sockAddrToDottedIP () + */ +unsigned epicsShareAPI sockAddrToDottedIP ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ) +{ + if ( paddr->sa_family != AF_INET ) { + const char * pErrStr = ""; + unsigned errStrLen = strlen ( pErrStr ); + if ( errStrLen < bufSize ) { + strcpy ( pBuf, pErrStr ); + return errStrLen; + } + else { + unsigned reducedSize = bufSize - 1u; + strncpy ( pBuf, pErrStr, reducedSize ); + pBuf[reducedSize] = '\0'; + return reducedSize; + } + } + else { + const struct sockaddr_in *paddr_in = ( const struct sockaddr_in * ) paddr; + return ipAddrToDottedIP ( paddr_in, pBuf, bufSize ); + } +} + +/* + * ipAddrToDottedIP () + */ +unsigned epicsShareAPI ipAddrToDottedIP ( + const struct sockaddr_in *paddr, char *pBuf, unsigned bufSize ) +{ + static const char * pErrStr = ""; + unsigned chunk[nDigitsDottedIP]; + unsigned addr = ntohl ( paddr->sin_addr.s_addr ); + unsigned strLen; + unsigned i; + int status; + + if ( bufSize == 0u ) { + return 0u; + } + + for ( i = 0; i < nDigitsDottedIP; i++ ) { + chunk[i] = addr & makeMask ( chunkSize ); + addr >>= chunkSize; + } + + /* + * inet_ntoa() isnt used because it isnt thread safe + * (and the replacements are not standardized) + */ + status = epicsSnprintf ( + pBuf, bufSize, "%u.%u.%u.%u:%hu", + chunk[3], chunk[2], chunk[1], chunk[0], + ntohs ( paddr->sin_port ) ); + if ( status > 0 ) { + strLen = ( unsigned ) status; + if ( strLen < bufSize - 1 ) { + return strLen; + } + } + strLen = strlen ( pErrStr ); + if ( strLen < bufSize ) { + strcpy ( pBuf, pErrStr ); + return strLen; + } + else { + strncpy ( pBuf, pErrStr, bufSize ); + pBuf[bufSize-1] = '\0'; + return bufSize - 1u; + } +} + diff --git a/src/libCom/osi/osiSock.h b/src/libCom/osi/osiSock.h new file mode 100644 index 000000000..46050678c --- /dev/null +++ b/src/libCom/osi/osiSock.h @@ -0,0 +1,208 @@ +/*************************************************************************\ +* 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 is 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 + * + * socket support library API def + * + * 7-1-97 -joh- + */ +#ifndef osiSockh +#define osiSockh + +#include "shareLib.h" +#include "osdSock.h" +#include "ellLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct sockaddr; +struct sockaddr_in; +struct in_addr; + +epicsShareFunc SOCKET epicsShareAPI epicsSocketCreate ( + int domain, int type, int protocol ); +epicsShareFunc int epicsShareAPI epicsSocketAccept ( + int sock, struct sockaddr * pAddr, osiSocklen_t * addrlen ); +epicsShareFunc void epicsShareAPI epicsSocketDestroy ( + SOCKET ); +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressReuseDuringTimeWaitState ( SOCKET s ); +epicsShareFunc void epicsShareAPI + epicsSocketEnableAddressUseForDatagramFanout ( SOCKET s ); + +/* + * Fortunately, on most systems the combination of a shutdown of both + * directions and or a signal is sufficent to interrupt a blocking send, + * receive, or connect call. For odd ball systems this is stubbed out in the + * osi area. + */ +enum epicsSocketSystemCallInterruptMechanismQueryInfo { + esscimqi_socketCloseRequired, + esscimqi_socketBothShutdownRequired, + esscimqi_socketSigAlarmRequired /* NO LONGER USED/SUPPORTED */ +}; +epicsShareFunc enum epicsSocketSystemCallInterruptMechanismQueryInfo + epicsSocketSystemCallInterruptMechanismQuery (); + + +/* + * convert socket address to ASCII in this order + * 1) look for matching host name and typically add trailing IP port + * 2) failing that, convert to raw ascii address (typically this is a + * dotted IP address with trailing port) + * 3) failing that, writes "" into pBuf + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI sockAddrToA ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ); + +/* + * convert IP address to ASCII in this order + * 1) look for matching host name and add trailing port + * 2) convert to raw dotted IP address with trailing port + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToA ( + const struct sockaddr_in * pInetAddr, char * pBuf, unsigned bufSize ); + +/* + * sockAddrToDottedIP () + * typically convert to raw dotted IP address with trailing port + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI sockAddrToDottedIP ( + const struct sockaddr * paddr, char * pBuf, unsigned bufSize ); + +/* + * ipAddrToDottedIP () + * convert to raw dotted IP address with trailing port + * + * returns the number of character elements stored in buffer not + * including the null termination, but always writes at least a + * null ternminater in the string (if bufSize >= 1) + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToDottedIP ( + const struct sockaddr_in * paddr, char * pBuf, unsigned bufSize ); + +/* + * convert inet address to a host name string + * + * returns the number of character elements stored in buffer not + * including the null termination. This will be zero if a matching + * host name cant be found. + * + * there are many OS specific implementation stubs for this routine + */ +epicsShareFunc unsigned epicsShareAPI ipAddrToHostName ( + const struct in_addr * pAddr, char * pBuf, unsigned bufSize ); + +/* + * attempt to convert ASCII string to an IP address in this order + * 1) look for traditional doted ip with optional port + * 2) look for raw number form of ip address with optional port + * 3) look for valid host name with optional port + */ +epicsShareFunc int epicsShareAPI aToIPAddr + ( const char * pAddrString, unsigned short defaultPort, struct sockaddr_in * pIP); + +/* + * attempt to convert ASCII host name string with optional port to an IP address + */ +epicsShareFunc int epicsShareAPI hostToIPAddr + (const char *pHostName, struct in_addr *pIPA); +/* + * attach to BSD socket library + */ +epicsShareFunc int epicsShareAPI osiSockAttach (void); /* returns T if success, else F */ + +/* + * release BSD socket library + */ +epicsShareFunc void epicsShareAPI osiSockRelease (void); + +/* + * convert socket error number to a string + */ +epicsShareFunc void epicsSocketConvertErrnoToString ( + char * pBuf, unsigned bufSize ); + +typedef union osiSockAddr { + struct sockaddr_in ia; + struct sockaddr sa; +} osiSockAddr; + +typedef struct osiSockAddrNode { + ELLNODE node; + osiSockAddr addr; +} osiSockAddrNode; + +/* + * sockAddrAreIdentical() + * (returns true if addresses are identical) + */ +epicsShareFunc int epicsShareAPI sockAddrAreIdentical + ( const osiSockAddr * plhs, const osiSockAddr * prhs ); + +/* + * osiSockDiscoverBroadcastAddresses () + * Returns the broadcast addresses of each network interface found. + * + * This routine is provided with the address of an ELLLIST, a socket, + * a destination port number, and a match address. When the + * routine returns there will be one additional entry + * (an osiSockAddrNode) in the list for each network interface found that + * is up and isnt a loop back interface (match addr is INADDR_ANY), + * or only the interfaces that match the specified addresses (match addr + * is other than INADDR_ANY). If the interface supports broadcasting + * then add its broadcast address to the list. If the interface is a + * point to point link then add the destination address of the point to + * point link to the list. + * + * Any mutex locking required to protect pList is applied externally. + * + */ +epicsShareFunc void epicsShareAPI osiSockDiscoverBroadcastAddresses + (ELLLIST *pList, SOCKET socket, const osiSockAddr *pMatchAddr); + +/* + * osiLocalAddr () + * Returns the osiSockAddr of the first non-loopback interface found + * that is operational (up flag is set). If no valid address can be + * located then return an osiSockAddr with the address family set to + * unspecified (AF_UNSPEC). + * + * Unfortunately in EPICS 3.13 beta 11 and before the CA + * repeater would not always allow the loopback address + * as a local client address so current clients alternate + * between the address of the first non-loopback interface + * found and the loopback addresss when subscribing with + * the CA repeater until all CA repeaters have been updated + * to current code. After all CA repeaters have been restarted + * this osi interface can be eliminated. + */ +epicsShareFunc osiSockAddr epicsShareAPI osiLocalAddr (SOCKET socket); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef osiSockh */ diff --git a/src/libCom/osi/osiWireFormat.h b/src/libCom/osi/osiWireFormat.h new file mode 100644 index 000000000..1ba8b905d --- /dev/null +++ b/src/libCom/osi/osiWireFormat.h @@ -0,0 +1,264 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * L O S A L A M O S + * Los Alamos National Laboratory + * Los Alamos, New Mexico 87545 + * + * Copyright, 2000, The Regents of the University of California. + * + * + * Author Jeffrey O. Hill + * johill@lanl.gov + */ + +#ifndef osiWireFormat +#define osiWireFormat + +#include "epicsTypes.h" + +// +// With future CA protocols user defined payload composition will be +// supported and we will need to move away from a naturally aligned +// protocol (because pad byte overhead will probably be excessive when +// maintaining 8 byte natural alignment if the user isnt thinking about +// placing like sized elements together). +// +// Nevertheless, the R3.14 protocol continues to be naturally aligned, +// and all of the fields within the DBR_XXXX types are naturally aligned. +// Therefore we support here two wire transfer interfaces (naturally +// aligned and otherwise) because there are important optimizations +// specific to each of them. +// +// At some point in the future the naturally aligned interfaces might +// be eliminated (or unbundled from base) should they be no-longer needed. +// + +template < class T > +void WireGet ( const epicsUInt8 * pWireSrc, T & ); + +template < class T > +void WireSet ( const T &, epicsUInt8 * pWireDst ); + +template < class T > +void AlignedWireGet ( const T &, T & ); + +template < class T > +void AlignedWireSet ( const T &, T & ); + +template < class T > +class AlignedWireRef { +public: + AlignedWireRef ( T & ref ); + operator T () const; + AlignedWireRef < T > & operator = ( const T & ); +private: + T & _ref; + AlignedWireRef ( const AlignedWireRef & ); + AlignedWireRef & operator = ( const AlignedWireRef & ); +}; + +template < class T > +class AlignedWireRef < const T > { +public: + AlignedWireRef ( const T & ref ); + operator T () const; +private: + const T & _ref; + AlignedWireRef ( const AlignedWireRef & ); + AlignedWireRef & operator = ( const AlignedWireRef & ); +}; + +template < class T > +inline AlignedWireRef < T > :: AlignedWireRef ( T & ref ) : + _ref ( ref ) +{ +} + +template < class T > +inline AlignedWireRef < T > :: operator T () const +{ + T tmp; + AlignedWireGet ( _ref, tmp ); + return tmp; +} + +template < class T > +inline AlignedWireRef < T > & AlignedWireRef < T > :: operator = ( const T & src ) +{ + AlignedWireSet ( src, _ref ); + return *this; +} + +template < class T > +inline AlignedWireRef < const T > :: AlignedWireRef ( const T & ref ) : + _ref ( ref ) +{ +} + +template < class T > +inline AlignedWireRef < const T > :: operator T () const +{ + T tmp; + AlignedWireGet ( _ref, tmp ); + return tmp; +} + +// may be useful when creating support for little endian +inline epicsUInt16 byteSwap ( const epicsUInt16 & src ) +{ + return static_cast < epicsUInt16 > + ( ( src << 8u ) | ( src >> 8u ) ); +} + +// may be useful when creating support for little endian +inline epicsUInt32 byteSwap ( const epicsUInt32 & src ) +{ + epicsUInt32 tmp0 = byteSwap ( + static_cast < epicsUInt16 > ( src >> 16u ) ); + epicsUInt32 tmp1 = byteSwap ( + static_cast < epicsUInt16 > ( src ) ); + return static_cast < epicsUInt32 > + ( ( tmp1 << 16u ) | tmp0 ); +} + +template < class T > union WireAlias; + +template <> +union WireAlias < epicsInt8 > { + epicsUInt8 _u; + epicsInt8 _o; +}; + +template <> +union WireAlias < epicsInt16 > { + epicsUInt16 _u; + epicsInt16 _o; +}; + +template <> +union WireAlias < epicsInt32 > { + epicsUInt32 _u; + epicsInt32 _o; +}; + +template <> +union WireAlias < epicsFloat32 > { + epicsUInt32 _u; + epicsFloat32 _o; +}; + +// +// Missaligned unsigned wire format get/set can be implemented generically +// w/o performance penalty. Attempts to improve this on architectures that +// dont have alignement requirements will probably get into trouble with +// over-aggressive optimization under strict aliasing rules. +// + +template < class T > +inline void WireGet ( const epicsUInt8 * pWireSrc, T & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > tmp; + WireGet ( pWireSrc, tmp._u ); + dst = tmp._o; +} + +template <> +inline void WireGet < epicsUInt8 > ( + const epicsUInt8 * pWireSrc, epicsUInt8 & dst ) +{ + dst = pWireSrc[0]; +} + +template <> +inline void WireGet < epicsUInt16 > ( + const epicsUInt8 * pWireSrc, epicsUInt16 & dst ) +{ + dst = static_cast < epicsUInt16 > ( + ( pWireSrc[0] << 8u ) | pWireSrc[1] ); +} + +template <> +inline void WireGet < epicsUInt32 > ( + const epicsUInt8 * pWireSrc, epicsUInt32 & dst ) +{ + dst = static_cast < epicsUInt32 > ( + ( pWireSrc[0] << 24u ) | + ( pWireSrc[1] << 16u ) | + ( pWireSrc[2] << 8u ) | + pWireSrc[3] ); +} + +template < class T > +inline void WireSet ( const T & src, epicsUInt8 * pWireDst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > tmp; + tmp._o = src; + WireSet ( tmp._u, pWireDst ); +} + +template <> +inline void WireSet < epicsUInt8 > ( + const epicsUInt8 & src, epicsUInt8 * pWireDst ) +{ + pWireDst[0] = src; +} + +template <> +inline void WireSet < epicsUInt16 > ( + const epicsUInt16 & src, epicsUInt8 * pWireDst ) +{ + pWireDst[0] = static_cast < epicsUInt8 > ( src >> 8u ); + pWireDst[1] = static_cast < epicsUInt8 > ( src ); +} + +template <> +inline void WireSet < epicsUInt32 > ( + const epicsUInt32 & src, epicsUInt8 * pWireDst ) +{ + pWireDst[0] = static_cast < epicsUInt8 > ( src >> 24u ); + pWireDst[1] = static_cast < epicsUInt8 > ( src >> 16u ); + pWireDst[2] = static_cast < epicsUInt8 > ( src >> 8u ); + pWireDst[3] = static_cast < epicsUInt8 > ( src ); +} + +template < class T > +inline void AlignedWireGet ( const T & src, T & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > srcu, dstu; + srcu._o = src; + AlignedWireGet ( srcu._u, dstu._u ); + dst = dstu._o; +} + +template < class T > +inline void AlignedWireSet ( const T & src, T & dst ) +{ + // copy through union here + // a) prevents over-aggressive optimization under strict aliasing rules + // b) doesnt preclude extra copy operation being optimized away + WireAlias < T > srcu, dstu; + srcu._o = src; + AlignedWireSet ( srcu._u, dstu._u ); + dst = dstu._o; +} + +#include "osdWireFormat.h" + +#endif // osiWireFormat diff --git a/src/libCom/ring/epicsRingBytes.c b/src/libCom/ring/epicsRingBytes.c new file mode 100644 index 000000000..4ab883fd7 --- /dev/null +++ b/src/libCom/ring/epicsRingBytes.c @@ -0,0 +1,176 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* epicsRingBytes.cd */ + +/* Author: Eric Norum & Marty Kraimer Date: 15JUL99 */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "cantProceed.h" +#include "epicsRingBytes.h" + +/* + * Need at least one extra byte to be able to distinguish a completely + * full buffer from a completely empty one. Allow for a little extra + * space to try and keep good alignment and avoid multiple calls to + * memcpy for a single put/get operation. + */ +#define SLOP 16 + +typedef struct ringPvt { + volatile int nextPut; + volatile int nextGet; + int size; + volatile char *buffer; +}ringPvt; + +epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size) +{ + ringPvt *pring = mallocMustSucceed(sizeof(ringPvt),"epicsRingBytesCreate"); + pring->size = size + SLOP; + pring->buffer = mallocMustSucceed(pring->size,"ringCreate"); + pring->nextGet = 0; + pring->nextPut = 0; + return((void *)pring); +} + +epicsShareFunc void epicsShareAPI epicsRingBytesDelete(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + free((void *)pring->buffer); + free((void *)pring); +} + +epicsShareFunc int epicsShareAPI epicsRingBytesGet( + epicsRingBytesId id, char *value,int nbytes) +{ + ringPvt *pring = (ringPvt *)id; + int nextGet = pring->nextGet; + int nextPut = pring->nextPut; + int size = pring->size; + int count; + + if (nextGet <= nextPut) { + count = nextPut - nextGet; + if (count < nbytes) + nbytes = count; + if (nbytes) + memcpy (value, (void *)&pring->buffer[nextGet], nbytes); + nextGet += nbytes; + } + else { + count = size - nextGet; + if (count > nbytes) + count = nbytes; + memcpy (value, (void *)&pring->buffer[nextGet], count); + nextGet += count; + if (nextGet == size) { + int nLeft = nbytes - count; + if (nLeft > nextPut) + nLeft = nextPut; + memcpy (value+count, (void *)&pring->buffer[0], nLeft); + nextGet = nLeft; + nbytes = count + nLeft; + } + else { + nbytes = count; + } + } + pring->nextGet = nextGet; + return nbytes; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesPut( + epicsRingBytesId id, char *value,int nbytes) +{ + ringPvt *pring = (ringPvt *)id; + int nextGet = pring->nextGet; + int nextPut = pring->nextPut; + int size = pring->size; + int freeCount, copyCount, topCount; + + if (nextPut < nextGet) { + freeCount = nextGet - nextPut - SLOP; + if (nbytes > freeCount) + return 0; + if (nbytes) + memcpy ((void *)&pring->buffer[nextPut], value, nbytes); + nextPut += nbytes; + } + else { + freeCount = size - nextPut + nextGet - SLOP; + if (nbytes > freeCount) + return 0; + topCount = size - nextPut; + copyCount = (nbytes > topCount) ? topCount : nbytes; + if (copyCount) + memcpy ((void *)&pring->buffer[nextPut], value, copyCount); + nextPut += copyCount; + if (nextPut == size) { + int nLeft = nbytes - copyCount; + if (nLeft) + memcpy ((void *)&pring->buffer[0], value+copyCount, nLeft); + nextPut = nLeft; + } + } + pring->nextPut = nextPut; + return nbytes; +} + +epicsShareFunc void epicsShareAPI epicsRingBytesFlush(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + + pring->nextGet = pring->nextPut; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesFreeBytes(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + int nextGet = pring->nextGet; + int nextPut = pring->nextPut; + + if (nextPut < nextGet) + return nextGet - nextPut - SLOP; + else + return pring->size - nextPut + nextGet - SLOP; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + + return pring->size - epicsRingBytesFreeBytes(id) - SLOP; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + + return pring->size - SLOP; +} + +epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + + return (pring->nextPut == pring->nextGet); +} + +epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) +{ + return (epicsRingBytesFreeBytes(id) <= 0); +} diff --git a/src/libCom/ring/epicsRingBytes.h b/src/libCom/ring/epicsRingBytes.h new file mode 100644 index 000000000..9e5220513 --- /dev/null +++ b/src/libCom/ring/epicsRingBytes.h @@ -0,0 +1,47 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*epicsRingBytes.h */ + +/* Author: Eric Norum & Marty Kraimer Date: 15JUL99 */ + +#ifndef INCepicsRingBytesh +#define INCepicsRingBytesh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" + +typedef void *epicsRingBytesId; + +epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes); +epicsShareFunc void epicsShareAPI epicsRingBytesDelete(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesGet( + epicsRingBytesId id, char *value,int nbytes); +epicsShareFunc int epicsShareAPI epicsRingBytesPut( + epicsRingBytesId id, char *value,int nbytes); +epicsShareFunc void epicsShareAPI epicsRingBytesFlush(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesFreeBytes(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id); + +#ifdef __cplusplus +} +#endif + +/* NOTES + If there is only one writer it is not necessary to lock for put + If there is a single reader it is not necessary to lock for puts +*/ + +#endif /* INCepicsRingBytesh */ diff --git a/src/libCom/ring/epicsRingPointer.cpp b/src/libCom/ring/epicsRingPointer.cpp new file mode 100644 index 000000000..5118c9347 --- /dev/null +++ b/src/libCom/ring/epicsRingPointer.cpp @@ -0,0 +1,82 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*epicsRingPointer.cpp*/ +/* Author: Marty Kraimer Date: 13OCT2000 */ + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsRingPointer.h" +typedef epicsRingPointer voidPointer; + + +epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size) +{ + voidPointer *pvoidPointer = new voidPointer(size); + return(reinterpret_cast(pvoidPointer)); +} + +epicsShareFunc void epicsShareAPI epicsRingPointerDelete(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + delete pvoidPointer; +} + +epicsShareFunc void* epicsShareAPI epicsRingPointerPop(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return pvoidPointer->pop(); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerPush(epicsRingPointerId id, void *p) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return((pvoidPointer->push(p) ? 1 : 0)); +} + +epicsShareFunc void epicsShareAPI epicsRingPointerFlush(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + pvoidPointer->flush(); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerGetFree(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getFree()); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getUsed()); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerSize(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getSize()); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return((pvoidPointer->isEmpty()) ? 1 : 0); +} + +epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + return((pvoidPointer->isFull()) ? 1 : 0); +} diff --git a/src/libCom/ring/epicsRingPointer.h b/src/libCom/ring/epicsRingPointer.h new file mode 100644 index 000000000..57534586a --- /dev/null +++ b/src/libCom/ring/epicsRingPointer.h @@ -0,0 +1,157 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/*epicsRingPointer.h */ + +/* Author: Marty Kraimer Date: 15JUL99 */ + +#ifndef INCepicsRingPointerh +#define INCepicsRingPointerh + +/* NOTES + * If there is only one writer it is not necessary to lock push + * If there is a single reader it is not necessary to lock pop + */ + +#include "shareLib.h" + +#ifdef __cplusplus +template +class epicsRingPointer { +public: /* Functions */ + epicsRingPointer(int size); + ~epicsRingPointer(); + bool push(T *p); + T* pop(); + void flush(); + int getFree() const; + int getUsed() const; + int getSize() const; + bool isEmpty() const; + bool isFull() const; + +private: /* Prevent compiler-generated member functions */ + /* default constructor, copy constructor, assignment operator */ + epicsRingPointer(); + epicsRingPointer(const epicsRingPointer &); + epicsRingPointer& operator=(const epicsRingPointer &); + +private: /* Data */ + volatile int nextPush; + volatile int nextPop; + int size; + T * volatile * buffer; +}; + +extern "C" { +#endif /*__cplusplus */ + +typedef void *epicsRingPointerId; + +epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size); +epicsShareFunc void epicsShareAPI epicsRingPointerDelete(epicsRingPointerId id); +/*ringPointerPush returns (0,1) if p (was not, was) put on ring*/ +epicsShareFunc int epicsShareAPI epicsRingPointerPush(epicsRingPointerId id,void *p); +/*ringPointerPop returns 0 if ring is empty*/ +epicsShareFunc void* epicsShareAPI epicsRingPointerPop(epicsRingPointerId id) ; +epicsShareFunc void epicsShareAPI epicsRingPointerFlush(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetFree(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); + +#ifdef __cplusplus +} +#endif +/* END OF DECLARATIONS */ + +/* INLINE FUNCTIONS */ + +/* Algorithm note + * Space is allocated for one additional element. + * A put request is rejected if the it would cause nextPush to equal nextPop + * The algorithm does not require locking puts for a single writer + * or locking of gets for a single reader + */ +#ifdef __cplusplus + +template +inline epicsRingPointer::epicsRingPointer(int sz) : + nextPush(0), nextPop(0), size(sz+1), buffer(new T* [sz+1]) {} + +template +inline epicsRingPointer::~epicsRingPointer() +{ delete [] buffer;} + +template +inline bool epicsRingPointer::push(T *p) +{ + int next = nextPush; + int newNext = next + 1; + if(newNext>=size) newNext=0; + if(newNext==nextPop) return(false); + buffer[next] = p; + nextPush = newNext; + return(true); +} + +template +inline T* epicsRingPointer::pop() +{ + int next = nextPop; + if(next == nextPush) return(0); + T*p = buffer[next]; + ++next; + if(next >=size) next = 0; + nextPop = next; + return(p); +} + +template +inline void epicsRingPointer::flush() +{ + nextPop = 0; + nextPush = 0; +} + +template +inline int epicsRingPointer::getFree() const +{ + int n = nextPop - nextPush - 1; + if (n < 0) n += size; + return n; +} + +template +inline int epicsRingPointer::getUsed() const +{ + int n = nextPush - nextPop; + if (n < 0) n += size; + return n; +} + +template +inline int epicsRingPointer::getSize() const +{ return(size-1);} + +template +inline bool epicsRingPointer::isEmpty() const +{ return(nextPush==nextPop);} + +template +inline bool epicsRingPointer::isFull() const +{ + int count = nextPush - nextPop +1; + return((count == 0) || (count == size)); +} + +#endif /* __cplusplus */ + +#endif /* INCepicsRingPointerh */ diff --git a/src/libCom/taskwd/taskwd.c b/src/libCom/taskwd/taskwd.c new file mode 100644 index 000000000..b77361236 --- /dev/null +++ b/src/libCom/taskwd/taskwd.c @@ -0,0 +1,417 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* taskwd.c */ + +/* tasks and subroutines for a general purpose task watchdog */ +/* + * Original Author: Marty Kraimer + * Date: 07-18-91 +*/ + +#include +#include + +#define epicsExportSharedSymbols +#include "cantProceed.h" +#include "dbDefs.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsStdio.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "errlog.h" +#include "ellLib.h" +#include "errMdef.h" +#include "taskwd.h" + +struct tNode { + ELLNODE node; + epicsThreadId tid; + TASKWDFUNC callback; + void *usr; + int suspended; +}; + +struct mNode { + ELLNODE node; + const taskwdMonitor *funcs; + void *usr; +}; + +struct aNode { + void *key; + TASKWDANYFUNC callback; + void *usr; +}; + +union twdNode { + struct tNode t; + struct mNode m; + struct aNode a; +}; + +/* Registered Tasks */ +static epicsMutexId tLock; +static ELLLIST tList = ELLLIST_INIT; + +/* Active Monitors */ +static epicsMutexId mLock; +static ELLLIST mList = ELLLIST_INIT; + +/* Free List */ +static epicsMutexId fLock; +static ELLLIST fList = ELLLIST_INIT; + +/* Watchdog task control */ +static enum { + twdctlRun, twdctlDisable, twdctlExit +} twdCtl; +static epicsEventId loopEvent; +static epicsEventId exitEvent; + +/* Task delay times (seconds) */ +#define TASKWD_DELAY 6.0 + + +/* forward definitions */ +static union twdNode *allocNode(void); +static void freeNode(union twdNode *); + +/* Initialization, lazy */ + +static void twdTask(void *arg) +{ + struct tNode *pt; + struct mNode *pm; + + while (twdCtl != twdctlExit) { + if (twdCtl == twdctlRun) { + epicsMutexMustLock(tLock); + pt = (struct tNode *)ellFirst(&tList); + while (pt) { + int susp = epicsThreadIsSuspended(pt->tid); + if (susp != pt->suspended) { + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs->notify) { + pm->funcs->notify(pm->usr, pt->tid, susp); + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + if (susp) { + char tName[40]; + epicsThreadGetName(pt->tid, tName, sizeof(tName)); + errlogPrintf("Thread %s (%p) suspended\n", + tName, (void *)pt->tid); + if (pt->callback) { + pt->callback(pt->usr); + } + } + pt->suspended = susp; + } + pt = (struct tNode *)ellNext(&pt->node); + } + epicsMutexUnlock(tLock); + } + epicsEventWaitWithTimeout(loopEvent, TASKWD_DELAY); + } + epicsEventSignal(exitEvent); +} + + +static void twdShutdown(void *arg) +{ + twdCtl = twdctlExit; + epicsEventSignal(loopEvent); + epicsEventWait(exitEvent); +} + +static void twdInitOnce(void *arg) +{ + epicsThreadId tid; + + tLock = epicsMutexMustCreate(); + mLock = epicsMutexMustCreate(); + fLock = epicsMutexMustCreate(); + + twdCtl = twdctlRun; + loopEvent = epicsEventMustCreate(epicsEventEmpty); + exitEvent = epicsEventMustCreate(epicsEventEmpty); + + tid = epicsThreadCreate("taskwd", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), + twdTask, NULL); + if (tid == 0) + cantProceed("Failed to spawn task watchdog thread\n"); + + epicsAtExit(twdShutdown, NULL); +} + +void taskwdInit(void) +{ + static epicsThreadOnceId twdOnceFlag = EPICS_THREAD_ONCE_INIT; + epicsThreadOnce(&twdOnceFlag, twdInitOnce, NULL); +} + + +/* For tasks to be monitored */ + +void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr) +{ + struct tNode *pt; + struct mNode *pm; + + taskwdInit(); + if (tid == 0) + tid = epicsThreadGetIdSelf(); + + pt = &allocNode()->t; + pt->tid = tid; + pt->callback = callback; + pt->usr = usr; + pt->suspended = FALSE; + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs->insert) { + pm->funcs->insert(pm->usr, tid); + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + epicsMutexMustLock(tLock); + ellAdd(&tList, (void *)pt); + epicsMutexUnlock(tLock); +} + +void taskwdRemove(epicsThreadId tid) +{ + struct tNode *pt; + struct mNode *pm; + char tName[40]; + + taskwdInit(); + + if (tid == 0) + tid = epicsThreadGetIdSelf(); + + epicsMutexMustLock(tLock); + pt = (struct tNode *)ellFirst(&tList); + while (pt != NULL) { + if (tid == pt->tid) { + ellDelete(&tList, (void *)pt); + epicsMutexUnlock(tLock); + freeNode((union twdNode *)pt); + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs->remove) { + pm->funcs->remove(pm->usr, tid); + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + return; + } + pt = (struct tNode *)ellNext(&pt->node); + } + epicsMutexUnlock(tLock); + + epicsThreadGetName(tid, tName, sizeof(tName)); + errlogPrintf("taskwdRemove: Thread %s (%p) not registered!\n", + tName, (void *)tid); +} + + +/* Monitoring API */ + +void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr) +{ + struct mNode *pm; + + if (funcs == NULL) return; + + taskwdInit(); + + pm = &allocNode()->m; + pm->funcs = funcs; + pm->usr = usr; + + epicsMutexMustLock(mLock); + ellAdd(&mList, (void *)pm); + epicsMutexUnlock(mLock); +} + +void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr) +{ + struct mNode *pm; + + if (funcs == NULL) return; + + taskwdInit(); + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs == funcs && pm->usr == usr) { + ellDelete(&mList, (void *)pm); + freeNode((union twdNode *)pm); + epicsMutexUnlock(mLock); + return; + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + errlogPrintf("taskwdMonitorDel: Unregistered!\n"); +} + + +/* Support old API for backwards compatibility */ + +static void anyNotify(void *usr, epicsThreadId tid, int suspended) +{ + struct aNode *pa = (struct aNode *)usr; + + if (suspended) { + pa->callback(pa->usr, tid); + } +} + +static taskwdMonitor anyFuncs = { + NULL, anyNotify, NULL +}; + +void taskwdAnyInsert(void *key, TASKWDANYFUNC callback, void *usr) +{ + struct mNode *pm; + struct aNode *pa; + + if (callback == NULL) return; + + taskwdInit(); + + pa = &allocNode()->a; + pa->key = key; + pa->callback = callback; + pa->usr = usr; + + pm = &allocNode()->m; + pm->funcs = &anyFuncs; + pm->usr = pa; + + epicsMutexMustLock(mLock); + ellAdd(&mList, (void *)pm); + epicsMutexUnlock(mLock); +} + +void taskwdAnyRemove(void *key) +{ + struct mNode *pm; + struct aNode *pa; + + taskwdInit(); + + epicsMutexMustLock(mLock); + pm = (struct mNode *)ellFirst(&mList); + while (pm) { + if (pm->funcs == &anyFuncs) { + pa = (struct aNode *)pm->usr; + if (pa->key == key) { + ellDelete(&mList, (void *)pm); + freeNode((union twdNode *)pa); + freeNode((union twdNode *)pm); + epicsMutexUnlock(mLock); + return; + } + } + pm = (struct mNode *)ellNext(&pm->node); + } + epicsMutexUnlock(mLock); + + errlogPrintf("taskwdAnyRemove: Unregistered key %p\n", key); +} + + +/* Report function */ + +epicsShareFunc void taskwdShow(int level) +{ + struct tNode *pt; + int mCount, fCount, tCount; + char tName[40]; + + epicsMutexMustLock(mLock); + mCount = ellCount(&mList); + epicsMutexUnlock(mLock); + + epicsMutexMustLock(fLock); + fCount = ellCount(&fList); + epicsMutexUnlock(fLock); + + epicsMutexMustLock(tLock); + tCount = ellCount(&tList); + printf("%d monitors, %d threads registered, %d free nodes\n", + mCount, tCount, fCount); + if (level) { + printf("%16.16s %9s %12s %12s %12s\n", + "THREAD NAME", "STATE", "EPICS TID", "CALLBACK", "USR ARG"); + pt = (struct tNode *)ellFirst(&tList); + while (pt != NULL) { + epicsThreadGetName(pt->tid, tName, sizeof(tName)); + printf("%16.16s %9s %12p %12p %12p\n", + tName, pt->suspended ? "Suspended" : "Ok ", + (void *)pt->tid, (void *)pt->callback, pt->usr); + pt = (struct tNode *)ellNext(&pt->node); + } + } + epicsMutexUnlock(tLock); +} + + +/* Free list management */ + +static union twdNode *newNode(void) +{ + union twdNode *pn; + + epicsMutexMustLock(fLock); + pn = (union twdNode *)ellFirst(&fList); + if (pn) { + ellDelete(&fList, (void *)pn); + epicsMutexUnlock(fLock); + return pn; + } + epicsMutexUnlock(fLock); + return calloc(1, sizeof(union twdNode)); +} + +static union twdNode *allocNode(void) +{ + union twdNode *pn = newNode(); + while (!pn) { + errlogPrintf("Thread taskwd suspending: out of memory\n"); + epicsThreadSuspendSelf(); + pn = newNode(); + } + return pn; +} + +static void freeNode(union twdNode *pn) +{ + epicsMutexMustLock(fLock); + ellAdd(&fList, (void *)pn); + epicsMutexUnlock(fLock); +} diff --git a/src/libCom/taskwd/taskwd.h b/src/libCom/taskwd/taskwd.h new file mode 100644 index 000000000..2011932ee --- /dev/null +++ b/src/libCom/taskwd/taskwd.h @@ -0,0 +1,68 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* General purpose task watchdog */ +/* + * Original Author: Marty Kraimer + * Date: 07-18-91 +*/ + +#ifndef INC_taskwd_H +#define INC_taskwd_H + +#include "epicsThread.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Initialization, optional */ +epicsShareFunc void taskwdInit(void); + + +/* For tasks to be monitored */ +typedef void (*TASKWDFUNC)(void *usr); + +epicsShareFunc void taskwdInsert(epicsThreadId tid, + TASKWDFUNC callback, void *usr); +epicsShareFunc void taskwdRemove(epicsThreadId tid); + + +/* Monitoring API */ +typedef struct { + void (*insert)(void *usr, epicsThreadId tid); + void (*notify)(void *usr, epicsThreadId tid, int suspended); + void (*remove)(void *usr, epicsThreadId tid); +} taskwdMonitor; + +epicsShareFunc void taskwdMonitorAdd(const taskwdMonitor *funcs, void *usr); +epicsShareFunc void taskwdMonitorDel(const taskwdMonitor *funcs, void *usr); + + +/* Old monitoring API, deprecated */ +typedef void (*TASKWDANYFUNC)(void *usr, epicsThreadId tid); + +epicsShareFunc void taskwdAnyInsert(void *key, + TASKWDANYFUNC callback, void *usr); +epicsShareFunc void taskwdAnyRemove(void *key); + + +/* Report function */ +epicsShareFunc void taskwdShow(int level); + + +#ifdef __cplusplus +} +#endif + +#endif /* INC_taskwd_H */ diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile new file mode 100644 index 000000000..9bf92f528 --- /dev/null +++ b/src/libCom/test/Makefile @@ -0,0 +1,185 @@ +#************************************************************************* +# Copyright (c) 2006 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../.. + +include $(TOP)/configure/CONFIG + +PROD_LIBS += Com + +TESTPROD_HOST += epicsUnitTestTest +epicsUnitTestTest_SRCS += epicsUnitTestTest.c +# Not much point running this on vxWorks or RTEMS... +TESTS += epicsUnitTestTest + +TESTPROD_HOST += epicsCalcTest +epicsCalcTest_SRCS += epicsCalcTest.cpp +testHarness_SRCS += epicsCalcTest.cpp +TESTS += epicsCalcTest + +TESTPROD_HOST += epicsAlgorithmTest +epicsAlgorithmTest_SRCS += epicsAlgorithmTest.cpp +testHarness_SRCS += epicsAlgorithmTest.cpp +TESTS += epicsAlgorithmTest + +TESTPROD_HOST += epicsMathTest +epicsMathTest_SRCS += epicsMathTest.c +testHarness_SRCS += epicsMathTest.c +TESTS += epicsMathTest + +TESTPROD_HOST += epicsEllTest +epicsEllTest_SRCS += epicsEllTest.c +testHarness_SRCS += epicsEllTest.c +TESTS += epicsEllTest + +TESTPROD_HOST += epicsErrlogTest +epicsErrlogTest_SRCS += epicsErrlogTest.c +testHarness_SRCS += epicsErrlogTest.c +TESTS += epicsErrlogTest + +TESTPROD_HOST += epicsStdioTest +epicsStdioTest_SRCS += epicsStdioTest.c +testHarness_SRCS += epicsStdioTest.c +TESTS += epicsStdioTest + +TESTPROD_HOST += epicsStringTest +epicsStringTest_SRCS += epicsStringTest.c +testHarness_SRCS += epicsStringTest.c +TESTS += epicsStringTest + +TESTPROD_HOST += epicsTimeTest +epicsTimeTest_SRCS += epicsTimeTest.cpp +testHarness_SRCS += epicsTimeTest.cpp +TESTS += epicsTimeTest + +TESTPROD_HOST += epicsThreadTest +epicsThreadTest_SRCS += epicsThreadTest.cpp +testHarness_SRCS += epicsThreadTest.cpp +TESTS += epicsThreadTest + +TESTPROD_HOST += epicsThreadOnceTest +epicsThreadOnceTest_SRCS += epicsThreadOnceTest.c +testHarness_SRCS += epicsThreadOnceTest.c +TESTS += epicsThreadOnceTest + +TESTPROD_HOST += epicsThreadPriorityTest +epicsThreadPriorityTest_SRCS += epicsThreadPriorityTest.cpp +testHarness_SRCS += epicsThreadPriorityTest.cpp +TESTS += epicsThreadPriorityTest + +TESTPROD_HOST += epicsThreadPrivateTest +epicsThreadPrivateTest_SRCS += epicsThreadPrivateTest.cpp +testHarness_SRCS += epicsThreadPrivateTest.cpp +TESTS += epicsThreadPrivateTest + +TESTPROD_HOST += epicsExitTest +epicsExitTest_SRCS += epicsExitTest.c +testHarness_SRCS += epicsExitTest.c +TESTS += epicsExitTest + +TESTPROD_HOST += epicsTimerTest +epicsTimerTest_SRCS += epicsTimerTest.cpp +testHarness_SRCS += epicsTimerTest.cpp +TESTS += epicsTimerTest + +TESTPROD_HOST += ringPointerTest +ringPointerTest_SRCS += ringPointerTest.c +testHarness_SRCS += ringPointerTest.c +TESTS += ringPointerTest + +TESTPROD_HOST += ringBytesTest +ringBytesTest_SRCS += ringBytesTest.c +testHarness_SRCS += ringBytesTest.c +TESTS += ringBytesTest + +TESTPROD_HOST += epicsEventTest +epicsEventTest_SRCS += epicsEventTest.cpp +testHarness_SRCS += epicsEventTest.cpp +TESTS += epicsEventTest + +TESTPROD_HOST += epicsMutexTest +epicsMutexTest_SRCS += epicsMutexTest.cpp +testHarness_SRCS += epicsMutexTest.cpp +TESTS += epicsMutexTest + +TESTPROD_HOST += epicsExceptionTest +epicsExceptionTest_SRCS += epicsExceptionTest.cpp +testHarness_SRCS += epicsExceptionTest.cpp +TESTS += epicsExceptionTest + +TESTPROD_HOST += macEnvExpandTest +macEnvExpandTest_SRCS += macEnvExpandTest.c +testHarness_SRCS += macEnvExpandTest.c +TESTS += macEnvExpandTest + +TESTPROD_HOST += macLibTest +macLibTest_SRCS += macLibTest.c +testHarness_SRCS += macLibTest.c +TESTS += macLibTest + +TESTPROD_HOST += taskwdTest +taskwdTest_SRCS += taskwdTest.c +testHarness_SRCS += taskwdTest.c +TESTS += taskwdTest + +TESTPROD_HOST += blockingSockTest +blockingSockTest_SRCS += blockingSockTest.cpp +testHarness_SRCS += blockingSockTest.cpp +# needed when its an object library build +blockingSockTest_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 +blockingSockTest_SYS_LIBS_solaris = socket +TESTS += blockingSockTest + +TESTPROD_HOST += epicsMessageQueueTest +epicsMessageQueueTest_SRCS += epicsMessageQueueTest.cpp +testHarness_SRCS += epicsMessageQueueTest.cpp +TESTS += epicsMessageQueueTest + + +# The testHarness runs all the test programs in a known working order. +testHarness_SRCS += epicsRunLibComTests.c + +PROD_vxWorks = vxTestHarness +vxTestHarness_SRCS += $(testHarness_SRCS) +vxTestHarness_OBJS += $(INSTALL_BIN)/vxComLibrary +TESTSPEC_vxWorks = vxTestHarness.munch; epicsRunLibComTests + +PROD_RTEMS += rtemsTestHarness +rtemsTestHarness_SRCS += rtemsTestHarness.c +rtemsTestHarness_SRCS += $(testHarness_SRCS) +TESTSPEC_RTEMS = rtemsTestHarness.boot; epicsRunLibComTests + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + + +# The following are not test programs, they measure performance. +# They should not be added to TESTS or to epicsRunLibComTests.c + +TESTPROD_HOST += epicsThreadPerform +epicsThreadPerform_SRCS += epicsThreadPerform.cpp +testHarness_SRCS += epicsThreadPerform.cpp + +TESTPROD_HOST += epicsMaxThreads +epicsMaxThreads_SRCS += epicsMaxThreads.c +testHarness_SRCS += epicsMaxThreads.c + +TESTPROD_HOST += buckTest +buckTest_SRCS += buckTest.c +testHarness_SRCS += buckTest.c + +#TESTPROD_HOST += fdmgrTest +fdmgrTest_SRCS += fdmgrTest.c +fdmgrTest_LIBS += ca +# FIXME: program never exits. + +TESTPROD_HOST += cvtFastPerform +cvtFastPerform_SRCS += cvtFastPerform.cpp +testHarness_SRCS += cvtFastPerform.cpp + +include $(TOP)/configure/RULES + diff --git a/src/libCom/test/blockingSockTest.cpp b/src/libCom/test/blockingSockTest.cpp new file mode 100644 index 000000000..0dd4dc4b1 --- /dev/null +++ b/src/libCom/test/blockingSockTest.cpp @@ -0,0 +1,290 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include +#include + +#include "osiSock.h" +#include "osiWireFormat.h" +#include "epicsThread.h" +#include "epicsSignal.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +union address { + struct sockaddr_in ia; + struct sockaddr sa; +}; + +class circuit { +public: + circuit ( SOCKET ); + void recvTest (); + void shutdown (); + void signal (); + void close (); + bool recvWakeupDetected () const; + bool sendWakeupDetected () const; + virtual const char * pName () = 0; +protected: + SOCKET sock; + epicsThreadId id; + bool recvWakeup; + bool sendWakeup; +protected: + virtual ~circuit() {} +}; + +class serverCircuit : public circuit { +public: + serverCircuit ( SOCKET ); +private: + const char * pName (); +}; + +class clientCircuit : public circuit { +public: + clientCircuit ( const address & ); +private: + const char * pName (); +}; + +class server { +public: + server ( const address & ); + void start (); + void daemon (); +protected: + SOCKET sock; + epicsThreadId id; + bool exit; +}; + +circuit::circuit ( SOCKET sockIn ) : + sock ( sockIn ), + id ( 0 ), + recvWakeup ( false ), + sendWakeup ( false ) +{ + verify ( this->sock != INVALID_SOCKET ); +} + +bool circuit::recvWakeupDetected () const +{ + return this->recvWakeup; +} + +bool circuit::sendWakeupDetected () const +{ + return this->sendWakeup; +} + +void circuit::shutdown () +{ + int status = ::shutdown ( this->sock, SHUT_RDWR ); + verify ( status == 0 ); +} + +void circuit::signal () +{ + epicsSignalRaiseSigAlarm ( this->id ); +} + +void circuit::close () +{ + epicsSocketDestroy ( this->sock ); +} + +void circuit::recvTest () +{ + epicsSignalInstallSigAlarmIgnore (); + char buf [1]; + while ( true ) { + int status = recv ( this->sock, + buf, (int) sizeof ( buf ), 0 ); + if ( status == 0 ) { + testDiag ( "%s was disconnected", this->pName () ); + this->recvWakeup = true; + break; + } + else if ( status > 0 ) { + testDiag ( "client received %i characters", status ); + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + testDiag ( "%s socket recv() error was \"%s\"\n", + this->pName (), sockErrBuf ); + this->recvWakeup = true; + break; + } + } +} + +extern "C" void socketRecvTest ( void * pParm ) +{ + circuit * pCir = reinterpret_cast < circuit * > ( pParm ); + pCir->recvTest (); +} + +clientCircuit::clientCircuit ( const address & addrIn ) : + circuit ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) +{ + address tmpAddr = addrIn; + int status = ::connect ( + this->sock, & tmpAddr.sa, sizeof ( tmpAddr ) ); + verify ( status == 0 ); + + circuit * pCir = this; + this->id = epicsThreadCreate ( + "client circuit", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + socketRecvTest, pCir ); + verify ( this->id ); +} + + +const char * clientCircuit::pName () +{ + return "client circuit"; +} + +extern "C" void serverDaemon ( void * pParam ) { + server * pSrv = reinterpret_cast < server * > ( pParam ); + pSrv->daemon (); +} + +server::server ( const address & addrIn ) : + sock ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ), + id ( 0 ), exit ( false ) +{ + verify ( this->sock != INVALID_SOCKET ); + + // setup server side + address tmpAddr = addrIn; + int status = bind ( this->sock, + & tmpAddr.sa, sizeof ( tmpAddr ) ); + if ( status ) { + testDiag ( "bind to server socket failed, status = %d", status ); + testAbort ( "Stop all CA servers before running this test." ); + } + status = listen ( this->sock, 10 ); + verify ( status == 0 ); +} + +void server::start () +{ + this->id = epicsThreadCreate ( + "server daemon", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + serverDaemon, this ); + verify ( this->id ); +} + +void server::daemon () +{ + while ( ! this->exit ) { + // accept client side + address addr; + osiSocklen_t addressSize = sizeof ( addr ); + SOCKET ns = accept ( this->sock, + & addr.sa, & addressSize ); + verify ( ns != INVALID_SOCKET ); + circuit * pCir = new serverCircuit ( ns ); + verify ( pCir ); + } +} + +serverCircuit::serverCircuit ( SOCKET sockIn ) : + circuit ( sockIn ) +{ + circuit * pCir = this; + epicsThreadId threadId = epicsThreadCreate ( + "server circuit", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + socketRecvTest, pCir ); + verify ( threadId ); +} + +const char * serverCircuit::pName () +{ + return "server circuit"; +} + +static const char *mechName(int mech) +{ + static const struct { + int mech; + const char *name; + } mechs[] = { + {-1, "Unknown shutdown mechanism" }, + {esscimqi_socketCloseRequired, "esscimqi_socketCloseRequired" }, + {esscimqi_socketBothShutdownRequired, "esscimqi_socketBothShutdownRequired" }, + {esscimqi_socketSigAlarmRequired, "esscimqi_socketSigAlarmRequired" } + }; + + for (unsigned i=0; i < (sizeof(mechs) / sizeof(mechs[0])); ++i) { + if (mech == mechs[i].mech) + return mechs[i].name; + } + return "Unknown shutdown mechanism value"; +} + +MAIN(blockingSockTest) +{ + testPlan(1); + + address addr; + memset ( (char *) & addr, 0, sizeof ( addr ) ); + addr.ia.sin_family = AF_INET; + addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK ); + addr.ia.sin_port = htons ( 5064 ); // CA + + server srv ( addr ); + srv.start (); + clientCircuit client ( addr ); + + epicsThreadSleep ( 1.0 ); + verify ( ! client.recvWakeupDetected () ); + + client.shutdown (); + epicsThreadSleep ( 1.0 ); + int mech = -1; + if ( client.recvWakeupDetected () ) { + mech = esscimqi_socketBothShutdownRequired; + } + else { + client.signal (); + epicsThreadSleep ( 1.0 ); + if ( client.recvWakeupDetected () ) { + mech = esscimqi_socketSigAlarmRequired; + } + else { + client.close (); + epicsThreadSleep ( 1.0 ); + if ( client.recvWakeupDetected () ) { + mech = esscimqi_socketCloseRequired; + } + } + } + testDiag("This OS behaves like \"%s\".", mechName(mech)); + + int query = epicsSocketSystemCallInterruptMechanismQuery (); + if (! testOk(mech == query, "Socket shutdown mechanism") ) + testDiag("epicsSocketSystemCallInterruptMechanismQuery returned \"%s\"", + mechName(query)); + + return testDone(); +} + diff --git a/src/libCom/test/buckTest.c b/src/libCom/test/buckTest.c new file mode 100644 index 000000000..6fc34954e --- /dev/null +++ b/src/libCom/test/buckTest.c @@ -0,0 +1,82 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "epicsTime.h" +#include "epicsAssert.h" +#include "bucketLib.h" +#include "testMain.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +MAIN(buckTest) +{ + unsigned id1; + unsigned id2; + char * pValSave1; + char * pValSave2; + int s; + BUCKET * pb; + char * pVal; + unsigned i; + epicsTimeStamp start, finish; + double duration; + const int LOOPS = 500000; + + pb = bucketCreate(8); + if (!pb) { + return -1; + } + + id1 = 0x1000a432; + pValSave1 = "fred"; + s = bucketAddItemUnsignedId(pb, &id1, pValSave1); + verify (s == S_bucket_success); + + pValSave2 = "jane"; + id2 = 0x0000a432; + s = bucketAddItemUnsignedId(pb, &id2, pValSave2); + verify (s == S_bucket_success); + + epicsTimeGetCurrent(&start); + for (i=0; i +#include +#include +#include + +#include "epicsStdio.h" +#include "cvtFast.h" +#include "epicsTime.h" +#include "testMain.h" + +using namespace std; + +typedef void ( * PTestFunc ) ( const double &, char * pBug, size_t bufSize ); + +class Test { +public: + Test (); + virtual ~Test (); + void execute (); +protected: + char _pDst[128]; + double _srcVal; + unsigned short _prec; + static unsigned const _nUnrolled = 10; + static const unsigned _uSecPerSec = 1000000; + static unsigned const _nIterations = 10000; + virtual void _target () = 0; + void _measure (); + Test ( const Test & ); + Test & operator = ( Test & ); +}; + +class TestCvtFastDouble : public Test { +protected: + void _target (); +}; + +class TestSNPrintf : public Test { +protected: + void _target (); +}; + +Test :: + Test () : + _srcVal ( 0.0 ), _prec ( 0 ) +{ +} + +Test :: ~Test () +{ +} + +void Test :: execute () +{ + static const unsigned lowPrecision = 2; + static const unsigned highPrecision = DBL_MANT_DIG; + + for ( unsigned i = 0; i < 3; i++ ) { + double mVal = rand (); + mVal /= (RAND_MAX + 1); + double fEVal = rand (); + fEVal /= (RAND_MAX + 1); + fEVal *= DBL_MAX_EXP - DBL_MIN_EXP; + fEVal += DBL_MIN_EXP; + int eVal = static_cast < int > ( fEVal + 0.5 ); + _srcVal = ldexp ( mVal, eVal ); + for ( _prec = lowPrecision; + _prec <= highPrecision; _prec += 4u ) { + _measure (); + } + _srcVal = rand (); + _srcVal /= (RAND_MAX + 1); + _srcVal *= 10.0; + _srcVal -= 5.0; + for ( _prec = lowPrecision; + _prec <= highPrecision; _prec += 4u ) { + _measure (); + } + } +} + +void Test :: _measure () +{ + epicsTime beg = epicsTime :: getCurrent (); + for ( unsigned i = 0; i < _nIterations; i++ ) { + _target (); + } + epicsTime end = epicsTime :: getCurrent (); + double elapsed = end - beg; + elapsed /= _nIterations * _nUnrolled; + elapsed *= _uSecPerSec; + printf ( " %4.4f usec, prec=%i, val=%4.4g, for %s\n", + elapsed, _prec, _srcVal, typeid ( *this ).name () ); +} + +void TestCvtFastDouble :: _target () +{ + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); + + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); + cvtDoubleToString ( _srcVal, _pDst, _prec ); +} + +void TestSNPrintf :: _target () +{ + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); + epicsSnprintf ( _pDst, sizeof ( _pDst ), "%.*g", + static_cast < int > ( _prec ), _srcVal ); +} + +MAIN(cvtFastPerform) +{ + TestCvtFastDouble testCvtFastDouble; + TestSNPrintf testSNPrintf; + + testCvtFastDouble.execute (); + testSNPrintf.execute (); + + return 0; +} diff --git a/src/libCom/test/epicsAlgorithmTest.cpp b/src/libCom/test/epicsAlgorithmTest.cpp new file mode 100644 index 000000000..fa8c0c03f --- /dev/null +++ b/src/libCom/test/epicsAlgorithmTest.cpp @@ -0,0 +1,63 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// epicsAlgorithmTest.cpp +// Authors: Jeff Hill & Andrew Johnson + +#include "epicsUnitTest.h" +#include "epicsAlgorithm.h" +#include "epicsMath.h" +#include "testMain.h" + +MAIN(epicsAlgorithm) +{ + testPlan(22); + + float f1 = 3.3f; + float f2 = 3.4f; + float Inf = epicsINF; + float NaN = epicsNAN; + + testOk(epicsMin(f1, f2) == f1, "epicsMin(f1, f2)"); + testOk(epicsMin(f1, -Inf) == -Inf, "epicsMin(f1, -Inf)"); + testOk(isnan(epicsMin(f1, NaN)), "epicsMin(f1, NaN)"); + testOk(epicsMin(f1, Inf) == f1, "epicsMin(f1, Inf)"); + + testOk(epicsMin(f2, f1) == f1, "epicsMin(f2, f1)"); + testOk(epicsMin(-Inf, f1) == -Inf, "epicsMin(-Inf, f1)"); + testOk(isnan(epicsMin(NaN, f1)), "epicsMin(NaN, f1)"); + testOk(epicsMin(Inf, f1) == f1, "epicsMin(Inf, f1)"); + + testOk(epicsMax(f2, f1) == f2, "epicsMax(f2, f1)"); + testOk(epicsMax(-Inf, f1) == f1, "epicsMax(-Inf, f1)"); + testOk(isnan(epicsMax(NaN, f1)), "epicsMax(NaN, f1)"); + testOk(epicsMax(Inf, f1) == Inf, "epicsMax(Inf, f1)"); + + testOk(epicsMax(f1, f2) == f2, "epicsMax(f1, f2)"); + testOk(epicsMax(f1, -Inf) == f1, "epicsMax(f1, -Inf)"); + testOk(isnan(epicsMax(f1, NaN)), "epicsMax(f1, NaN)"); + testOk(epicsMax(f1, Inf) == Inf, "epicsMax(f1, Inf)"); + + epicsSwap(f1, f2); + testOk(f1==3.4f && f2==3.3f, "epicsSwap(f1, f2)"); + + int i1 = 3; + int i2 = 4; + + testOk(epicsMin(i1, i2) == i1, "epicsMin(i1,i2)"); + testOk(epicsMin(i2, i1) == i1, "epicsMin(i2,i1)"); + + testOk(epicsMax(i1, i2) == i2, "epicsMax(i1,i2)"); + testOk(epicsMax(i2, i1) == i2, "epicsMax(i2,i1)"); + + epicsSwap(i1, i2); + testOk(i1 == 4 && i2 == 3, "epicsSwap(i1, i2)"); + + return testDone(); +} + diff --git a/src/libCom/test/epicsCalcTest.cpp b/src/libCom/test/epicsCalcTest.cpp new file mode 100644 index 000000000..929c04a60 --- /dev/null +++ b/src/libCom/test/epicsCalcTest.cpp @@ -0,0 +1,862 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +// Revision-Id: anj@aps.anl.gov-20101007191624-sqws79ec9gxn7reb +// Author: Andrew Johnson + +#include "epicsUnitTest.h" +#include "epicsMath.h" +#include "epicsAlgorithm.h" +#include "postfix.h" +#include "testMain.h" + +/* Infrastructure for running tests */ + +double doCalc(const char *expr) { + /* Evaluate expression, return result */ + double args[CALCPERFORM_NARGS] = { + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + }; + char rpn[MAX_POSTFIX_SIZE]; + short err; + double result = 0.0; + result /= result; /* Start as NaN */ + + if (postfix(expr, rpn, &err)) { + testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); + } else + if (calcPerform(args, &result, rpn) && finite(result)) { + testDiag("calcPerform: error evaluating '%s'", expr); + } + return result; +} + +void testCalc(const char *expr, double expected) { + /* Evaluate expression, test against expected result */ + bool pass = false; + double args[CALCPERFORM_NARGS] = { + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 + }; + char rpn[MAX_POSTFIX_SIZE]; + short err; + double result = 0.0; + result /= result; /* Start as NaN */ + + if (postfix(expr, rpn, &err)) { + testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr); + } else + if (calcPerform(args, &result, rpn) && finite(result)) { + testDiag("calcPerform: error evaluating '%s'", expr); + } + + if (finite(expected) && finite(result)) { + pass = fabs(expected - result) < 1e-8; + } else if (isnan(expected)) { + pass = (bool) isnan(result); + } else { + pass = (result == expected); + } + if (!testOk(pass, "%s", expr)) { + testDiag("Expected result is %g, actually got %g", expected, result); + calcExprDump(rpn); + } + return; +} + +void testArgs(const char *expr, unsigned long einp, unsigned long eout) { + char rpn[MAX_POSTFIX_SIZE]; + short err = 0; + unsigned long vinp, vout; + + if (postfix(expr, rpn, &err)) { + testFail("postfix: %s in expression '%s'", calcErrorStr(err), expr); + return; + } + if (calcArgUsage(rpn, &vinp, &vout)) { + testFail("calcArgUsage returned error for '%s'", expr); + return; + } + if (!testOk(vinp == einp && vout == eout, "Args for '%s'", expr)) { + testDiag("Expected (%lx, %lx) got (%lx, %lx)", einp, eout, vinp, vout); + } +} + +void testBadExpr(const char *expr, short expected_err) { + /* Parse an invalid expression, test against expected error code */ + char rpn[MAX_POSTFIX_SIZE]; + short err = 0; + + postfix(expr, rpn, &err); + if (!testOk(err == expected_err, "Bad expression '%s'", expr)) { + testDiag("Expected '%s', actually got '%s'", + calcErrorStr(expected_err), calcErrorStr(err)); + calcExprDump(rpn); + } + return; +} + +/* Test an expression that is also valid C code */ +#define testExpr(expr) testCalc(#expr, expr); + +/* These are the argument bits for testArgs */ +#define A_A 0x001 +#define A_B 0x002 +#define A_C 0x004 +#define A_D 0x008 +#define A_E 0x010 +#define A_F 0x020 +#define A_G 0x040 +#define A_H 0x080 +#define A_I 0x100 +#define A_J 0x200 +#define A_K 0x400 +#define A_L 0x800 + + +/* Macros and functions to make some expressions into valid C code */ + +#ifndef PI +#define PI 3.14159265358979 +#endif +#define Inf epicsINF +#define NaN epicsNAN +#define D2R (PI/180.) +#define R2D (180./PI) +#define ABS(x) fabs(x) +#define AND & +#define ATAN2(x,y) atan2(y,x) +#define LN(x) log(x) +#define LOG(x) log10(x) +#define LOGE(x) log(x) +#define NINT(x) (double)(long)((x) >= 0 ? (x)+0.5 : (x)-0.5) +#define OR | +#define SQR(x) sqrt(x) +#define XOR ^ + +static inline double MAX(double a) { + return a; +} +static inline double MAX(double a, double b) { + return epicsMax(a,b); +} +static inline double MAX(double a, double b, double c) { + return MAX(MAX(a,b),c); +} +static inline double MAX(double a, double b, double c, double d) { + return MAX(MAX(a,b,c),d); +} +static inline double MAX(double a, double b, double c, double d, double e) { + return MAX(MAX(a,b,c,d),e); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f) { + return MAX(MAX(a,b,c,d,e),f); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g) { + return MAX(MAX(a,b,c,d,e,f),g); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h) { + return MAX(MAX(a,b,c,d,e,f,g),h); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i) { + return MAX(MAX(a,b,c,d,e,f,g,h),i); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j) { + return MAX(MAX(a,b,c,d,e,f,g,h,i),j); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k) { + return MAX(MAX(a,b,c,d,e,f,g,h,i,j),k); +} +static inline double MAX(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k, double l) { + return MAX(MAX(a,b,c,d,e,f,g,h,i,j,k),l); +} + +static inline double MIN(double a) { + return a; +} +static inline double MIN(double a, double b) { + return epicsMin(a,b); +} +static inline double MIN(double a, double b, double c) { + return MIN(MIN(a,b),c); +} +static inline double MIN(double a, double b, double c, double d) { + return MIN(MIN(a,b,c),d); +} +static inline double MIN(double a, double b, double c, double d, double e) { + return MIN(MIN(a,b,c,d),e); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f) { + return MIN(MIN(a,b,c,d,e),f); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g) { + return MIN(MIN(a,b,c,d,e,f),g); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h) { + return MIN(MIN(a,b,c,d,e,f,g),h); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i) { + return MIN(MIN(a,b,c,d,e,f,g,h),i); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j) { + return MIN(MIN(a,b,c,d,e,f,g,h,i),j); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k) { + return MIN(MIN(a,b,c,d,e,f,g,h,i,j),k); +} +static inline double MIN(double a, double b, double c, double d, double e, + double f, double g, double h, double i, double j, double k, double l) { + return MIN(MIN(a,b,c,d,e,f,g,h,i,j,k),l); +} + +MAIN(epicsCalcTest) +{ + int repeat; + const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0, + g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0; + + testPlan(566); + + /* LITERAL_OPERAND elements */ + testExpr(0); + testExpr(1); + testExpr(2); + testExpr(3); + testExpr(4); + testExpr(5); + testExpr(6); + testExpr(7); + testExpr(8); + testExpr(9); + testExpr(.1); + testExpr(0.1); + testExpr(Inf); + testCalc("Infinity", Inf); + testExpr(NaN); + + /* OPERAND elements */ + testExpr(a); + testExpr(b); + testExpr(c); + testExpr(d); + testExpr(e); + testExpr(f); + testExpr(g); + testExpr(h); + testExpr(i); + testExpr(j); + testExpr(k); + testExpr(l); + testExpr(PI); + testExpr(D2R); + testExpr(R2D); + + for (repeat=0; repeat<100; repeat++) { + double res = doCalc("rndm"); + if (res<0 || res >1) { + testDiag("rndm returned %g", res); + break; + } + } + testOk(repeat == 100, "rndm"); + + /* UNARY_MINUS element */ + testExpr(-1); + testExpr(-Inf); + testExpr(- -1); + + /* UNARY_OPERATOR elements */ + testExpr((1)); + testExpr(!0); + testExpr(!1); + testExpr(!!0); + testExpr(ABS(1.0)); + testExpr(ABS(-1.)); + testExpr(acos(1.)); + testExpr(asin(0.5)); + testExpr(atan(0.5)); + testExpr(ATAN2(1., 2.)); + testExpr(ceil(0.5)); + testExpr(cos(0.5)); + testExpr(cosh(0.5)); + testExpr(exp(1.)); + testExpr(floor(1.5)); + + testExpr(finite(0)); + testExpr(finite(Inf)); + testExpr(finite(-Inf)); + testExpr(finite(NaN)); + testCalc("finite(0,1,2)", 1); + testCalc("finite(0,1,NaN)", 0); + testCalc("finite(0,NaN,2)", 0); + testCalc("finite(NaN,1,2)", 0); + testCalc("finite(0,1,Inf)", 0); + testCalc("finite(0,Inf,2)", 0); + testCalc("finite(Inf,1,2)", 0); + testCalc("finite(0,1,-Inf)", 0); + testCalc("finite(0,-Inf,2)", 0); + testCalc("finite(-Inf,1,2)", 0); + testExpr(isinf(0)); + testExpr(isinf(Inf)); + testExpr(!!isinf(-Inf)); // Some GCCs return -1/0/+1 rather than 0/+1 + testExpr(isinf(NaN)); + testExpr(isnan(0)); + testExpr(isnan(Inf)); + testExpr(isnan(NaN)); + testCalc("isnan(0,1,2)", 0); + testCalc("isnan(0,1,NaN)", 1); + testCalc("isnan(0,NaN,2)", 1); + testCalc("isnan(NaN,1,2)", 1); + testCalc("isnan(0,1,-Inf)", 0); + testCalc("isnan(0,-Inf,2)", 0); + testCalc("isnan(-Inf,1,2)", 0); + + testExpr(LN(5.)); + testExpr(LOG(5.)); + testExpr(LOGE(2.)); + + testExpr(MAX(-99)); + testExpr(MAX( 1., 2.)); + testExpr(MAX( 1., Inf)); + testExpr(MAX( 1.,-Inf)); + testExpr(MAX( 1., NaN)); + testExpr(MAX( Inf, 1.)); + testExpr(MAX(-Inf, 1.)); + testExpr(MAX( NaN, 1.)); + testExpr(MAX( 1., 2.,3.)); + testExpr(MAX( 1., 3.,2.)); + testExpr(MAX( 2., 1.,3.)); + testExpr(MAX( 2., 3.,1.)); + testExpr(MAX( 3., 1.,2.)); + testExpr(MAX( 3., 2.,1.)); + testExpr(MAX( 1., 2., Inf)); + testExpr(MAX( 1., 2.,-Inf)); + testExpr(MAX( 1., 2., NaN)); + testExpr(MAX( 1., Inf,2.)); + testExpr(MAX( 1.,-Inf,2.)); + testExpr(MAX( 1., NaN,2.)); + testExpr(MAX( Inf, 1.,2.)); + testExpr(MAX(-Inf, 1.,2.)); + testExpr(MAX( NaN, 1.,2.)); + testExpr(MAX( 1., 2., 3., 4.)); + testExpr(MAX( 1., 2., 4., 3.)); + testExpr(MAX( 1., 4., 3., 2.)); + testExpr(MAX( 4., 2., 3., 1.)); + testExpr(MAX( 1., 2., 3.,NaN)); + testExpr(MAX( 1., 2.,NaN, 3.)); + testExpr(MAX( 1.,NaN, 3., 2.)); + testExpr(MAX(NaN, 2., 3., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5.)); + testExpr(MAX( 1., 2., 3., 5., 4.)); + testExpr(MAX( 1., 2., 5., 4., 3.)); + testExpr(MAX( 1., 5., 3., 4., 2.)); + testExpr(MAX( 5., 2., 3., 4., 1.)); + testExpr(MAX( 1., 2., 3., 4.,NaN)); + testExpr(MAX( 1., 2., 3.,NaN, 4.)); + testExpr(MAX( 1., 2.,NaN, 4., 3.)); + testExpr(MAX( 1.,NaN, 3., 4., 2.)); + testExpr(MAX(NaN, 2., 3., 4., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5., 6.)); + testExpr(MAX( 1., 2., 3., 4., 6., 5.)); + testExpr(MAX( 1., 2., 3., 6., 5., 4.)); + testExpr(MAX( 1., 2., 6., 4., 5., 3.)); + testExpr(MAX( 1., 6., 3., 4., 5., 2.)); + testExpr(MAX( 6., 2., 3., 4., 5., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5.,NaN)); + testExpr(MAX( 1., 2., 3., 4.,NaN, 5.)); + testExpr(MAX( 1., 2., 3.,NaN, 5., 4.)); + testExpr(MAX( 1., 2.,NaN, 4., 5., 3.)); + testExpr(MAX( 1.,NaN, 3., 4., 5., 2.)); + testExpr(MAX(NaN, 2., 3., 4., 5., 1.)); + testExpr(MAX( 1., 2., 3., 4., 5.,Inf)); + testExpr(MAX( 1., 2., 3., 4.,Inf, 5.)); + testExpr(MAX( 1., 2., 3.,Inf, 5., 4.)); + testExpr(MAX( 1., 2.,Inf, 4., 5., 3.)); + testExpr(MAX( 1.,Inf, 3., 4., 5., 2.)); + testExpr(MAX(Inf, 2., 3., 4., 5., 1.)); + testExpr(MAX(1,2,3,4,5,6,7,8,9,10)); + testExpr(MAX(5,4,3,2,1,0,-1,-2,-3,-4)); + testExpr(MAX(-1,1,0)); + + testExpr(MIN(99)); + testExpr(MIN(1.,2.)); + testExpr(MIN(1.,Inf)); + testExpr(MIN(1.,-Inf)); + testExpr(MIN(1.,NaN)); + testExpr(MIN(NaN,1.)); + testExpr(MIN( 1., 2.,3.)); + testExpr(MIN( 1., 3.,2.)); + testExpr(MIN( 2., 1.,3.)); + testExpr(MIN( 2., 3.,1.)); + testExpr(MIN( 3., 1.,2.)); + testExpr(MIN( 3., 2.,1.)); + testExpr(MIN( 1., 2., Inf)); + testExpr(MIN( 1., 2.,-Inf)); + testExpr(MIN( 1., 2., NaN)); + testExpr(MIN( 1., Inf,2.)); + testExpr(MIN( 1.,-Inf,2.)); + testExpr(MIN( 1., NaN,2.)); + testExpr(MIN( Inf, 1.,2.)); + testExpr(MIN(-Inf, 1.,2.)); + testExpr(MIN( NaN, 1.,2.)); + testExpr(MIN( 1., 2., 3., 4.)); + testExpr(MIN( 1., 2., 4., 3.)); + testExpr(MIN( 1., 4., 3., 2.)); + testExpr(MIN( 4., 2., 3., 1.)); + testExpr(MIN( 1., 2., 3.,NaN)); + testExpr(MIN( 1., 2.,NaN, 3.)); + testExpr(MIN( 1.,NaN, 3., 2.)); + testExpr(MIN(NaN, 2., 3., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5.)); + testExpr(MIN( 1., 2., 3., 5., 4.)); + testExpr(MIN( 1., 2., 5., 4., 3.)); + testExpr(MIN( 1., 5., 3., 4., 2.)); + testExpr(MIN( 5., 2., 3., 4., 1.)); + testExpr(MIN( 1., 2., 3., 4.,NaN)); + testExpr(MIN( 1., 2., 3.,NaN, 4.)); + testExpr(MIN( 1., 2.,NaN, 4., 3.)); + testExpr(MIN( 1.,NaN, 3., 4., 2.)); + testExpr(MIN(NaN, 2., 3., 4., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5., 6.)); + testExpr(MIN( 2., 1., 3., 4., 5., 6.)); + testExpr(MIN( 3., 2., 1., 4., 5., 6.)); + testExpr(MIN( 4., 2., 3., 1., 5., 6.)); + testExpr(MIN( 5., 2., 3., 4., 1., 6.)); + testExpr(MIN( 6., 2., 3., 4., 5., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5.,NaN)); + testExpr(MIN( 1., 2., 3., 4.,NaN, 5.)); + testExpr(MIN( 1., 2., 3.,NaN, 5., 4.)); + testExpr(MIN( 1., 2.,NaN, 4., 5., 3.)); + testExpr(MIN( 1.,NaN, 3., 4., 5., 2.)); + testExpr(MIN(NaN, 2., 3., 4., 5., 1.)); + testExpr(MIN( 1., 2., 3., 4., 5.,-Inf)); + testExpr(MIN( 1., 2., 3., 4.,-Inf, 5.)); + testExpr(MIN( 1., 2., 3.,-Inf, 5., 4.)); + testExpr(MIN( 1., 2.,-Inf, 4., 5., 3.)); + testExpr(MIN( 1.,-Inf, 3., 4., 5., 2.)); + testExpr(MIN(-Inf, 2., 3., 4., 5., 1.)); + testExpr(MIN(1,2,3,4,5,6,7,8,9,10)); + testExpr(MIN(5,4,3,2,1,0,-1,-2,-3,-4)); + testExpr(MIN(1,-1,0)); + testExpr(MAX(MIN(0,2),MAX(0),MIN(3,2,1))); + + testExpr(NINT(0.4)); + testExpr(NINT(0.6)); + testExpr(NINT(-0.4)); + testExpr(NINT(-0.6)); + testExpr(sin(0.5)); + testExpr(sinh(0.5)); + testExpr(SQR(10.)); + testExpr(sqrt(16.)); + testExpr(tan(0.5)); + testExpr(tanh(0.5)); + testExpr(~5); + testExpr(~~5); + + /* BINARY_OPERATOR elements */ + testExpr(0 != 1); + testExpr(0 != 0); + testExpr(1 != 0); + testExpr(1 != 0 != 2); + testExpr(0.0 != Inf); + testExpr(0.0 != -Inf); + testExpr(0.0 != NaN); + testExpr(Inf != 0.0); + testExpr(Inf != Inf); + testExpr(Inf != -Inf); + testExpr(Inf != NaN); + testExpr(-Inf != 0.0); + testExpr(-Inf != Inf); + testExpr(-Inf != -Inf); + testExpr(-Inf != NaN); + testExpr(NaN != 0.0); + testExpr(NaN != Inf); + testExpr(NaN != -Inf); + testExpr(NaN != NaN); + + testCalc("0 # 1", 0 != 1); + testCalc("0 # 0", 0 != 0); + testCalc("1 # 0", 1 != 0); + testCalc("1 # 0 # 2", 1 != 0 != 2); + + testExpr(7 % 4); + testExpr(-7 % 4); + testExpr(63 % 16 % 6) + testCalc("1 % 0", NaN); + + testExpr(7 & 4); + + testExpr(0 && 0); + testExpr(0 && 1); + testExpr(1 && 0); + testExpr(1 && 1); + + testExpr(2 * 2); + testExpr(0.0 * Inf); + testExpr(0.0 * -Inf); + testExpr(0.0 * NaN); + testExpr(Inf * 0.0); + testExpr(Inf * Inf); + testExpr(Inf * -Inf); + testExpr(Inf * NaN); + testExpr(-Inf * 0.0); + testExpr(-Inf * Inf); + testExpr(-Inf * -Inf); + testExpr(-Inf * NaN); + testExpr(NaN * 0.0); + testExpr(NaN * Inf); + testExpr(NaN * -Inf); + testExpr(NaN * NaN); + + testCalc("2 ** 0.2", pow(2., 0.2)); + testCalc("2 ** -0.2", pow(2., -0.2)); + testCalc("-0.2 ** 2", pow(-0.2, 2.)); + testCalc("-0.2 ** -2", pow(-0.2, -2)); + testCalc("2 ** 2 ** 3", pow(pow(2., 2.), 3.)); + + testExpr(0 + 1); + testExpr(0.0 + Inf); + testExpr(0.0 + -Inf); + testExpr(0.0 + NaN); + testExpr(Inf + 0.0); + testExpr(Inf + Inf); + testExpr(Inf + -Inf); + testExpr(Inf + NaN); + testExpr(-Inf + 0.0); + testExpr(-Inf + Inf); + testExpr(-Inf + -Inf); + testExpr(-Inf + NaN); + testExpr(NaN + 0.0); + testExpr(NaN + Inf); + testExpr(NaN + -Inf); + testExpr(NaN + NaN); + + testExpr(0 - 1); + testExpr(0 - 1 - 2); + testExpr(0.0 - Inf); + testExpr(0.0 - -Inf); + testExpr(0.0 - NaN); + testExpr(Inf - 0.0); + testExpr(Inf - Inf); + testExpr(Inf - -Inf); + testExpr(Inf - NaN); + testExpr(-Inf - 0.0); + testExpr(-Inf - Inf); + testExpr(-Inf - -Inf); + testExpr(-Inf - NaN); + testExpr(NaN - 0.0); + testExpr(NaN - Inf); + testExpr(NaN - -Inf); + testExpr(NaN - NaN); + + testExpr(2.0 / 3.0); + testExpr(1.0 / 2.0 / 3.0); + testExpr(0.0 / Inf); + testExpr(0.0 / -Inf); + testExpr(0.0 / NaN); + testExpr(Inf / 1.0); + testExpr(Inf / Inf); + testExpr(Inf / -Inf); + testExpr(Inf / NaN); + testExpr(-Inf / 1.0); + testExpr(-Inf / Inf); + testExpr(-Inf / -Inf); + testExpr(-Inf / NaN); + testExpr(NaN / 1.0); + testExpr(NaN / Inf); + testExpr(NaN / -Inf); + testExpr(NaN / NaN); + + testExpr(0 < 1); + testExpr(0 < 0); + testExpr(1 < 0); + testExpr(2 < 0 < 2) + testExpr(0.0 < Inf); + testExpr(0.0 < -Inf); + testExpr(0.0 < NaN); + testExpr(Inf < 0.0); + testExpr(Inf < Inf); + testExpr(Inf < -Inf); + testExpr(Inf < NaN); + testExpr(-Inf < 0.0); + testExpr(-Inf < Inf); + testExpr(-Inf < -Inf); + testExpr(-Inf < NaN); + testExpr(NaN < 0.0); + testExpr(NaN < Inf); + testExpr(NaN < -Inf); + testExpr(NaN < NaN); + + testExpr(1 << 2); + testExpr(1 << 3 << 2) + + testExpr(0 <= 1); + testExpr(0 <= 0); + testExpr(1 <= 0); + testExpr(3 <= 2 <= 3) + testExpr(0.0 <= Inf); + testExpr(0.0 <= -Inf); + testExpr(0.0 <= NaN); + testExpr(Inf <= 0.0); + testExpr(Inf <= Inf); + testExpr(Inf <= -Inf); + testExpr(Inf <= NaN); + testExpr(-Inf <= 0.0); + testExpr(-Inf <= Inf); + testExpr(-Inf <= -Inf); + testExpr(-Inf <= NaN); + testExpr(NaN <= 0.0); + testExpr(NaN <= Inf); + testExpr(NaN <= -Inf); + testExpr(NaN <= NaN); + + testCalc("0 = 1", 0 == 1); + testCalc("0 = 0", 0 == 0); + testCalc("1 = 0", 1 == 0); + testCalc("2 = 2 = 1", 2 == 2 == 1); + + testExpr(0 == 1); + testExpr(0 == 0); + testExpr(1 == 0); + testExpr(2 == 2 == 1); + testExpr(0.0 == Inf); + testExpr(0.0 == -Inf); + testExpr(0.0 == NaN); + testExpr(Inf == 0.0); + testExpr(Inf == Inf); + testExpr(Inf == -Inf); + testExpr(Inf == NaN); + testExpr(-Inf == 0.0); + testExpr(-Inf == Inf); + testExpr(-Inf == -Inf); + testExpr(-Inf == NaN); + testExpr(NaN == 0.0); + testExpr(NaN == Inf); + testExpr(NaN == -Inf); + testExpr(NaN == NaN); + + testExpr(0 > 1); + testExpr(0 > 0); + testExpr(1 > 0); + testExpr(2 > 0 > 2); + testExpr(0.0 > Inf); + testExpr(0.0 > -Inf); + testExpr(0.0 > NaN); + testExpr(Inf > 0.0); + testExpr(Inf > Inf); + testExpr(Inf > -Inf); + testExpr(Inf > NaN); + testExpr(-Inf > 0.0); + testExpr(-Inf > Inf); + testExpr(-Inf > -Inf); + testExpr(-Inf > NaN); + testExpr(NaN > 0.0); + testExpr(NaN > Inf); + testExpr(NaN > -Inf); + testExpr(NaN > NaN); + + testExpr(0 >= 1); + testExpr(0 >= 0); + testExpr(1 >= 0); + testExpr(3 >= 2 >= 3); + testExpr(0.0 >= Inf); + testExpr(0.0 >= -Inf); + testExpr(0.0 >= NaN); + testExpr(Inf >= 0.0); + testExpr(Inf >= Inf); + testExpr(Inf >= -Inf); + testExpr(Inf >= NaN); + testExpr(-Inf >= 0.0); + testExpr(-Inf >= Inf); + testExpr(-Inf >= -Inf); + testExpr(-Inf >= NaN); + testExpr(NaN >= 0.0); + testExpr(NaN >= Inf); + testExpr(NaN >= -Inf); + testExpr(NaN >= NaN); + + testExpr(8 >> 1); + testExpr(64 >> 2 >> 1); + + testExpr(7 AND 4); + + testExpr(1 OR 8); + + testExpr(3 XOR 9); + + testCalc("2 ^ 0.2", pow(2., 0.2)); + testCalc("2 ^ -0.2", pow(2., -0.2)); + testCalc("(-0.2) ^ 2", pow(-0.2, 2.)); + testCalc("(-0.2) ^ -2", pow(-0.2, -2.)); + testCalc("2 ^ 2 ^ 3", pow(pow(2., 2.), 3.)); + + testExpr(1 | 8); + + testExpr(0 || 0); + testExpr(0 || 1); + testExpr(1 || 0); + testExpr(1 || 1); + + /* CONDITIONAL elements */ + testExpr(0 ? 1 : 2); + testExpr(1 ? 1 : 2); + testExpr(Inf ? 1 : 2); + testExpr(NaN ? 1 : 2); + testExpr(0 ? 0 ? 2 : 3 : 4); + testExpr(0 ? 1 ? 2 : 3 : 4); + testExpr(1 ? 0 ? 2 : 3 : 4); + testExpr(1 ? 1 ? 2 : 3 : 4); + testExpr(0 ? 2 : 0 ? 3 : 4); + testExpr(0 ? 2 : 1 ? 3 : 4); + testExpr(1 ? 2 : 0 ? 3 : 4); + testExpr(1 ? 2 : 1 ? 3 : 4); + + /* STORE_OPERATOR and EXPR_TERM elements*/ + testCalc("a := 0; a", 0); + testCalc("b := 0; b", 0); + testCalc("c := 0; c", 0); + testCalc("d := 0; d", 0); + testCalc("e := 0; e", 0); + testCalc("f := 0; f", 0); + testCalc("g := 0; g", 0); + testCalc("h := 0; h", 0); + testCalc("i := 0; i", 0); + testCalc("j := 0; j", 0); + testCalc("k := 0; k", 0); + testCalc("l := 0; l", 0); + + testCalc("a; a := 0", a); + testCalc("b; b := 0", b); + testCalc("c; c := 0", c); + testCalc("d; d := 0", d); + testCalc("e; e := 0", e); + testCalc("f; f := 0", f); + testCalc("g; g := 0", g); + testCalc("h; h := 0", h); + testCalc("i; i := 0", i); + testCalc("j; j := 0", j); + testCalc("k; k := 0", k); + testCalc("l; l := 0", l); + + // Check relative precedences. + testExpr(0 ? 1 : 2 | 4); // 0 1 + testExpr(1 ? 1 : 2 | 4); // 0 1 + testExpr(0 ? 2 | 4 : 1); // 0 1 + testExpr(1 ? 2 | 4 : 1); // 0 1 + testExpr(0 ? 1 : 2 & 3); // 0 2 + testExpr(1 ? 1 : 2 & 3); // 0 2 + testExpr(0 ? 2 & 3 : 1); // 0 2 + testExpr(1 ? 2 & 3 : 1); // 0 2 + testExpr(0 ? 2 : 3 >= 1); // 0 3 + testExpr(0 ? 3 >= 1 : 2); // 0 3 + testExpr(1 ? 0 == 1 : 2); // 0 3 + testExpr(1 ? 2 : 0 == 1); // 0 3 + testExpr(0 ? 1 : 2 + 4); // 0 4 + testExpr(1 ? 1 : 2 + 4); // 0 4 + testExpr(0 ? 2 + 4 : 1); // 0 4 + testExpr(1 ? 2 + 4 : 1); // 0 4 + testExpr(0 ? 1 : 2 * 4); // 0 5 + testExpr(1 ? 1 : 2 * 4); // 0 5 + testExpr(0 ? 2 * 4 : 1); // 0 5 + testExpr(1 ? 2 * 4 : 1); // 0 5 + testCalc("0 ? 1 : 2 ** 3", 8); // 0 6 + testCalc("1 ? 1 : 2 ** 3", 1); // 0 6 + testCalc("0 ? 2 ** 3 : 1", 1); // 0 6 + testCalc("1 ? 2 ** 3 : 1", 8); // 0 6 + testCalc("1 | 3 XOR 1", (1 | 3) ^ 1); // 1 1 + testExpr(1 XOR 3 | 1); // 1 1 + testExpr(3 | 1 & 2); // 1 2 + testExpr(2 | 4 > 3); // 1 3 + testExpr(2 OR 4 > 3); // 1 3 + testExpr(2 XOR 3 >= 0); // 1 3 + testExpr(2 | 1 - 3); // 1 4 + testExpr(2 | 4 / 2); // 1 5 + testCalc("1 | 2 ** 3", 1 | (int) pow(2., 3.));// 1 6 + testExpr(3 << 2 & 10); // 2 2 + testCalc("18 & 6 << 2", (18 & 6) << 2); // 2 2 + testExpr(36 >> 2 & 10); // 2 2 + testCalc("18 & 20 >> 2", (18 & 20) >> 2); // 2 2 + testExpr(3 & 4 == 4); // 2 3 + testExpr(3 AND 4 == 4); // 2 3 + testCalc("1 << 2 != 4", 1 << (2 != 4)); // 2 3 + testCalc("16 >> 2 != 4", 16 >> (2 != 4)); // 2 3 + testExpr(3 AND -2); // 2 8 + testExpr(0 < 1 ? 2 : 3); // 3 0 + testExpr(1 <= 0 ? 2 : 3); // 3 0 + testExpr(0 + -1); // 4 8 + testExpr(0 - -1); // 4 8 + testExpr(10 + 10 * 2); // 4 5 + testExpr(20 + 20 / 2); // 4 5 + testExpr(-1 + 1); // 7 4 + testExpr(-1 - 2); // 7 4 + testCalc("-2 ** 2", pow(-2., 2.)); // 7 6 + testCalc("-2 ^ 2", pow(-2., 2.)); // 7 6 + + // Check parentheses + testCalc("(1 | 2) ** 3", pow((double) (1 | 2), 3.));// 8 6 + testCalc("1+(1|2)**3", 1+pow((double) (1 | 2), 3.));// 8 6 + testExpr(1+(1?(1<2):(1>2))*2); + + testArgs("a", A_A, 0); + testArgs("A", A_A, 0); + testArgs("B", A_B, 0); + testArgs("C", A_C, 0); + testArgs("D", A_D, 0); + testArgs("E", A_E, 0); + testArgs("F", A_F, 0); + testArgs("G", A_G, 0); + testArgs("H", A_H, 0); + testArgs("I", A_I, 0); + testArgs("J", A_J, 0); + testArgs("K", A_K, 0); + testArgs("L", A_L, 0); + testArgs("A+B+C+D+E+F+G+H+I+J+K+L", + A_A|A_B|A_C|A_D|A_E|A_F|A_G|A_H|A_I|A_J|A_K|A_L, 0); + testArgs("0.1;A:=0", 0, A_A); + testArgs("1.1;B:=0", 0, A_B); + testArgs("2.1;C:=0", 0, A_C); + testArgs("3.1;D:=0", 0, A_D); + testArgs("4.1;E:=0", 0, A_E); + testArgs("5.1;F:=0", 0, A_F); + testArgs("6.1;G:=0", 0, A_G); + testArgs("7.1;H:=0", 0, A_H); + testArgs("8.1;I:=0", 0, A_I); + testArgs("9.1;J:=0", 0, A_J); + testArgs("10.1;K:=0", 0, A_K); + testArgs("11.1;L:=0", 0, A_L); + testArgs("12.1;A:=0;B:=A;C:=B;D:=C", 0, A_A|A_B|A_C|A_D); + testArgs("13.1;B:=A;A:=B;C:=D;D:=C", A_A|A_D, A_A|A_B|A_C|A_D); + + // Malformed expressions + testBadExpr("1*", CALC_ERR_INCOMPLETE); + testBadExpr("*1", CALC_ERR_SYNTAX); + testBadExpr("MIN", CALC_ERR_INCOMPLETE); + testBadExpr("MIN()", CALC_ERR_SYNTAX); + testBadExpr("MIN(A,)", CALC_ERR_SYNTAX); + testBadExpr("MIN(A,B,)", CALC_ERR_SYNTAX); + testBadExpr("MAX", CALC_ERR_INCOMPLETE); + testBadExpr("MAX()", CALC_ERR_SYNTAX); + testBadExpr("MAX(A,)", CALC_ERR_SYNTAX); + testBadExpr("MAX(A,B,)", CALC_ERR_SYNTAX); + testBadExpr("1?", CALC_ERR_CONDITIONAL); + testBadExpr("1?1", CALC_ERR_CONDITIONAL); + testBadExpr(":1", CALC_ERR_SYNTAX); + + return testDone(); +} + diff --git a/src/libCom/test/epicsEllTest.c b/src/libCom/test/epicsEllTest.c new file mode 100644 index 000000000..b608d1cc3 --- /dev/null +++ b/src/libCom/test/epicsEllTest.c @@ -0,0 +1,197 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include "ellLib.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +struct myItem { + ELLNODE node; + int list; + int num; +}; + +MAIN(epicsEllTest) +{ + ELLLIST list1; + ELLLIST list2 = ELLLIST_INIT; + int i1 = 1; + struct myItem *pitem, *pfirst, *pick; + + testPlan(70); + + list1.count = 27; + list1.node.next = (ELLNODE *) 0x01010101; + list1.node.previous = (ELLNODE *) 0x10101010; + + ellInit(&list1); + testOk1(list1.count == 0); + testOk1(list1.node.next == NULL); + testOk1(list1.node.previous == NULL); + + testOk1(list2.count == 0); + testOk1(list2.node.next == NULL); + testOk1(list2.node.previous == NULL); + + /* First build a couple lists and fill them with nodes. */ + pitem = (struct myItem *) malloc(sizeof(struct myItem)); + pitem->node.next = (ELLNODE *) 0x10101010; + pitem->node.previous = (ELLNODE *) 0x10101010; + pitem->list = 1; + pitem->num = i1++; + + ellAdd(&list1, &pitem->node); + + testOk1(list1.count == 1); + testOk1(list1.node.next == &pitem->node); + testOk1(list1.node.previous == &pitem->node); + testOk1(pitem->node.next == NULL); + testOk1(pitem->node.previous == NULL); + + pfirst = pitem; + while (i1 <= 21) { /* put 21 nodes into LIST1 */ + pitem = (struct myItem *) malloc(sizeof(struct myItem)); + pitem->list = 1; + pitem->num = i1++; + ellAdd(&list1, &pitem->node); + } + + testOk1(list1.count == 21); + testOk1(list1.node.next == &pfirst->node); + testOk1(list1.node.previous == &pitem->node); + testOk1(pitem->node.next == NULL); + + testOk1(ellFirst(&list1) == &pfirst->node); + testOk1(ellLast(&list1) == &pitem->node); + testOk1(ellNext(&pitem->node) == NULL); + testOk1(ellNext(&pfirst->node) == pfirst->node.next); + + testOk1(ellNth(&list1, 0) == NULL); + + pick = (struct myItem *)ellNth(&list1, 21); + testOk1(pick == pitem); + + testOk1(ellNth(&list1, 22) == NULL); + testOk1(ellNth(&list1, 1) == &pfirst->node); + testOk1(ellNth(&list1, 2) == pfirst->node.next); + testOk1(ellNth(&list1, 20) == pitem->node.previous); + + testOk1(ellPrevious(&pitem->node) == pitem->node.previous); + testOk1(ellPrevious(&pfirst->node) == NULL); + testOk1(ellPrevious(pfirst->node.next) == &pfirst->node); + + pick = (struct myItem *)ellGet(&list1); + testOk1(pick == pfirst); + testOk1(list1.node.next == pfirst->node.next); + + ellAdd(&list2, &pick->node); + + pick = (struct myItem *)ellGet(&list2); + testOk1(pick == pfirst); + testOk1(list2.node.next == NULL); + testOk1(list2.node.previous == NULL); + + testOk1(ellCount(&list1) == 20); + testOk1(ellCount(&list2) == 0); + + ellConcat(&list1, &list2); + + testOk1(ellCount(&list1) == 20); + testOk1(ellCount(&list2) == 0); + testOk1(list1.node.previous == &pitem->node); + + ellAdd(&list2, &pick->node); + ellConcat(&list1, &list2); + + testOk1(ellCount(&list1) == 21); + testOk1(ellCount(&list2) == 0); + testOk1(list1.node.previous == &pick->node); + testOk1(list2.node.next == NULL); + testOk1(list2.node.previous == NULL); + + ellDelete(&list1, &pick->node); + testOk1(ellCount(&list1) == 20); + + ellAdd(&list2, &pick->node); + ellConcat(&list2, &list1); + testOk1(ellFind(&list1, &pick->node) == -1); /* empty list */ + testOk1(ellFind(&list2, &pick->node) == pick->num); /* first node */ + + pick = (struct myItem *)ellNth(&list2, 18); + testOk1(ellFind(&list2, &pick->node) == 18); /* 18th node */ + + ellDelete(&list2, &pick->node); + ellInsert(&list2, NULL, &pick->node); /* move #18 to top */ + testOk1(ellCount(&list2) == 21); + testOk1(((struct myItem *)list2.node.next)->num == 18); + + testOk1(ellFind(&list2, ellNth(&list2, 21)) == 21); + + pick = (struct myItem *)ellGet(&list2); + pitem = (struct myItem *)ellNth(&list2, 17); + ellInsert(&list2, &pitem->node, &pick->node); + testOk1(ellFind(&list2, ellNth(&list2, 21)) == 21); + + testOk1(((struct myItem *)ellFirst(&list2))->num == 1); + testOk1(((struct myItem *)ellNth(&list2,17))->num == 17); + testOk1(((struct myItem *)ellNth(&list2,18))->num == 18); + + pick = (struct myItem *)ellLast(&list2); + pitem = (struct myItem *) malloc(sizeof(struct myItem)); + ellInsert(&list2, &pick->node, &pitem->node); + testOk1(ellCount(&list2) == 22); + testOk1(ellFind(&list2, ellNth(&list2, 22)) == 22); + + ellDelete(&list2, &pitem->node); + free(pitem); + + ellExtract(&list2, ellNth(&list2,9), ellNth(&list2, 19), &list1); + testOk1(ellCount(&list2) == 10); + testOk1(ellCount(&list1) == 11); + + testOk1(ellFind(&list2, ellNth(&list2, 10)) == 10); + testOk1(ellFind(&list1, ellNth(&list1, 11)) == 11); + + ellFree(&list2); + testOk1(ellCount(&list2) == 0); + + pick = (struct myItem *)ellFirst(&list1); + i1 = 1; + while(pick != NULL) { + pick->num = i1++; + pick = (struct myItem *)ellNext(&pick->node); + } + pick = (struct myItem *)ellFirst(&list1); + testOk1(pick != NULL); + + pitem = (struct myItem *)ellNStep(&pick->node, 3); + testOk1(pitem != NULL); + testOk1(pitem->num == 4); + + pitem = (struct myItem *)ellNStep(&pick->node, 30); + testOk1(pitem == NULL); + + pitem = (struct myItem *)ellNStep(&pick->node, 10); + testOk1(pitem != NULL); + testOk1(pitem->num == 11); + + pitem = (struct myItem *)ellNStep(&pitem->node, 0); + testOk1(pitem->num == 11); + + pitem = (struct myItem *)ellNStep(&pitem->node, -4); + testOk1(pitem->num == 7); + + ellFree2(&list1, free); + testOk1(ellCount(&list1) == 0); + + return testDone(); +} diff --git a/src/libCom/test/epicsErrlogTest.c b/src/libCom/test/epicsErrlogTest.c new file mode 100644 index 000000000..c96013313 --- /dev/null +++ b/src/libCom/test/epicsErrlogTest.c @@ -0,0 +1,291 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven Science Associates, as Operator of +* Brookhaven National Laboratory. +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Michael Davidsaver + * Date: 2010-09-24 + */ + +#include +#include +#include + +#include "epicsAssert.h" +#include "epicsThread.h" +#include "epicsEvent.h" +#include "dbDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define LOGBUFSIZE 2048 + +static +const char longmsg[]="A0123456789abcdef" + "B0123456789abcdef" + "C0123456789abcdef" + "D0123456789abcdef" + "E0123456789abcdef" + "F0123456789abcdef" + "G0123456789abcdef" + "H0123456789abcdef" + "I0123456789abcdef" + "J0123456789abcdef" + "K0123456789abcdef" + "L0123456789abcdef" + "M0123456789abcdef" + "N0123456789abcdef" + "O0123456789abcdef" + "P0123456789abcdef" + "Q0123456789abcdef" + "R0123456789abcdef" + "S0123456789abcdef" + ; +STATIC_ASSERT(NELEMENTS(longmsg)==324); + +static +const char truncmsg[]="A0123456789abcdef" + "B0123456789abcdef" + "C0123456789abcdef" + "D0123456789abcdef" + "E0123456789abcdef" + "F0123456789abcdef" + "G0123456789abcdef" + "H0123456789abcdef" + "I0123456789abcdef" + "J0123456789abcdef" + "K0123456789abcdef" + "L0123456789abcdef" + "M0123456789abcdef" + "N0123456789abcdef" + "O01<>\n" + ; +STATIC_ASSERT(NELEMENTS(truncmsg)==256); + +typedef struct { + unsigned int count; + const char* expect; + size_t checkLen; + epicsEventId jammer; + int jam; +} clientPvt; + +static +void logClient(void* raw, const char* msg) +{ + clientPvt *pvt = raw; + size_t L; + + /* Simulate thread priority on non-realtime + * OSs like Linux. This will cause the logging + * thread to sleep with the buffer lock held. + */ + if (pvt->jam > 0) { + pvt->jam = 0; + epicsEventMustWait(pvt->jammer); + } else if (pvt->jam < 0) { + pvt->jam++; + if (pvt->jam == 0) + epicsEventMustWait(pvt->jammer); + } + + L = strlen(msg); + + if (pvt->checkLen) + if (!testOk(pvt->checkLen == L, "Received %d chars", (int) L)) { + testDiag("Expected %d", (int) pvt->checkLen); + if (!pvt->expect) + testDiag("Message was \"%s\"", msg); + } + if (pvt->expect) + if (!testOk(strcmp(pvt->expect, msg) == 0, "msg is \"%s\"", msg)) + testDiag("Expected \"%s\"", pvt->expect); + + pvt->count++; +} + +MAIN(epicsErrlogTest) +{ + size_t mlen, i, N; + char msg[256]; + clientPvt pvt, pvt2; + + testPlan(25); + + strcpy(msg, truncmsg); + + errlogInit2(LOGBUFSIZE, 256); + + pvt.count = 0; + pvt2.count = 0; + + pvt.expect = NULL; + pvt2.expect = NULL; + + pvt.checkLen = 0; + pvt2.checkLen = 0; + + pvt.jam = 0; + pvt2.jam = 0; + + pvt.jammer = epicsEventMustCreate(epicsEventEmpty); + pvt2.jammer = epicsEventMustCreate(epicsEventEmpty); + + testDiag("Check listener registration"); + + errlogAddListener(&logClient, &pvt); + + pvt.expect = "Testing"; + pvt.checkLen = strlen(pvt.expect); + + errlogPrintfNoConsole(pvt.expect); + errlogFlush(); + + testOk1(pvt.count == 1); + + errlogAddListener(&logClient, &pvt2); + + pvt2.expect = pvt.expect = "Testing2"; + pvt2.checkLen = pvt.checkLen = strlen(pvt.expect); + + errlogPrintfNoConsole(pvt.expect); + errlogFlush(); + + testOk1(pvt.count == 2); + testOk1(pvt2.count == 1); + + /* Removes the first listener, but the second remains */ + errlogRemoveListener(&logClient); + + pvt2.expect = "Testing3"; + pvt2.checkLen = strlen(pvt2.expect); + + errlogPrintfNoConsole(pvt2.expect); + errlogFlush(); + + testOk1(pvt.count == 2); + testOk1(pvt2.count == 2); + + /* Remove the second listener */ + errlogRemoveListener(&logClient); + + errlogPrintfNoConsole("Something different"); + errlogFlush(); + + testOk1(pvt.count == 2); + testOk1(pvt2.count == 2); + + /* Re-add one listener */ + errlogAddListener(&logClient, &pvt); + + testDiag("Check truncation"); + + pvt.expect = truncmsg; + pvt.checkLen = 255; + + errlogPrintfNoConsole(longmsg); + errlogFlush(); + + testOk1(pvt.count == 3); + + pvt.expect = NULL; + + testDiag("Check priority"); + /* For the following tests it is important that + * the buffer should not flush until we request it + */ + pvt.jam = 1; + + errlogPrintfNoConsole(longmsg); + epicsThreadSleep(0.1); + + testOk1(pvt.count == 3); + + epicsEventSignal(pvt.jammer); + errlogFlush(); + + testOk1(pvt.count == 4); + + testDiag("Find buffer capacity (%u theoretical)",LOGBUFSIZE); + + pvt.checkLen = 0; + + for (mlen = 8; mlen <= 255; mlen *= 2) { + double eff; + char save = msg[mlen - 1]; + + N = LOGBUFSIZE / mlen; /* # of of messages to send */ + msg[mlen - 1] = '\0'; + pvt.count = 0; + /* pvt.checkLen = mlen - 1; */ + + pvt.jam = 1; + + for (i = 0; i < N; i++) { + errlogPrintfNoConsole(msg); + } + + epicsEventSignal(pvt.jammer); + errlogFlush(); + + eff = (double) (pvt.count * mlen) / LOGBUFSIZE * 100.0; + testDiag(" For %d messages of length %d got %u (%.1f%% efficient)", + (int) N, (int) mlen, pvt.count, eff); + + msg[mlen - 1] = save; + N = pvt.count; /* Save final count for the test below */ + + /* Clear "errlog: messages were discarded" status */ + pvt.checkLen = 0; + errlogPrintfNoConsole("."); + errlogFlush(); + } + + testDiag("Checking buffer use after partial flush"); + + /* Use the numbers from the largest block size above */ + mlen /= 2; + msg[mlen - 1] = '\0'; + + pvt.jam = 1; + pvt.count = 0; + + testDiag("Filling with %d messages of size %d", (int) N, (int) mlen); + + for (i = 0; i < N; i++) { + errlogPrintfNoConsole(msg); + } + + testOk1(pvt.count == 0); + + epicsThreadSleep(0.1); /* should really be a second Event */ + + pvt.jam = -2; /* Block before #th message */ + epicsEventSignal(pvt.jammer); + epicsThreadSleep(0.1); + + testDiag("Drain %u messages", pvt.count); + testOk1(pvt.count == 2); + + testDiag("Add two more (%d total)", (int) N+2); + errlogPrintfNoConsole(msg); + errlogPrintfNoConsole(msg); + + testOk1(pvt.count == 2); + + epicsEventSignal(pvt.jammer); + errlogFlush(); + testDiag("Logged %u messages", pvt.count); + + testOk1(pvt.count == N+2); + + /* Clean up */ + errlogRemoveListener(&logClient); + + return testDone(); +} diff --git a/src/libCom/test/epicsEventTest.cpp b/src/libCom/test/epicsEventTest.cpp new file mode 100644 index 000000000..31da6208b --- /dev/null +++ b/src/libCom/test/epicsEventTest.cpp @@ -0,0 +1,252 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsEventTest.cpp */ + +/* Author: Marty Kraimer Date: 26JAN2000 */ +/* timeout accuracy tests by Jeff Hill */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsRingPointer.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +typedef struct info { + epicsEventId event; + epicsMutexId lockRing; + int quit; + epicsRingPointerId ring; +} info; + +extern "C" { + +static void consumer(void *arg) +{ + info *pinfo = (info *)arg; + int errors = 0; + + testDiag("consumer: starting"); + while (!pinfo->quit) { + epicsEventWaitStatus status = epicsEventWait(pinfo->event); + if (status != epicsEventWaitOK) { + testDiag("consumer: epicsEventWait returned %d", status); + errors++; + } + while (epicsRingPointerGetUsed(pinfo->ring) >= 2) { + epicsRingPointerId message[2]; + for (int i = 0; i < 2; i++) { + message[i] = epicsRingPointerPop(pinfo->ring); + if (message[i] == 0) { + testDiag("consumer: epicsRingPointerPop returned 0"); + errors++; + } + } + if (message[0] != message[1]) { + testDiag("consumer: message %p %p\n", message[0], message[1]); + errors++; + } + } + } + testOk(errors == 0, "consumer: errors = %d", errors); +} + +static void producer(void *arg) +{ + info *pinfo = (info *)arg; + const char *name = epicsThreadGetNameSelf(); + epicsThreadId myId = epicsThreadGetIdSelf(); + int errors = 0; + int ntimes = 0; + + testDiag("%s: starting", name); + while(!pinfo->quit) { + ++ntimes; + epicsMutexLockStatus status = epicsMutexLock(pinfo->lockRing); + if (status != epicsMutexLockOK) { + testDiag("%s: epicsMutexLock returned %d", name, status); + errors++; + } + if (epicsRingPointerGetFree(pinfo->ring) >= 2) { + for (int i = 0; i < 2; i++) { + if (!epicsRingPointerPush(pinfo->ring, myId)) { + testDiag("%s: epicsRingPointerPush fail", name); + errors++; + } + if (i == 0 && (ntimes % 4 == 0)) + epicsThreadSleep(0.1); + } + } else { + testFail("%s: ring buffer full", name); + errors++; + } + epicsMutexUnlock(pinfo->lockRing); + epicsThreadSleep(1.0); + epicsEventSignal(pinfo->event); + } + testOk(errors == 0, "%s: errors = %d", name, errors); +} + +#define SLEEPERCOUNT 3 +struct wakeInfo { + epicsEventId event; + epicsMutexId countMutex; + int count; +}; +static void sleeper(void *arg) +{ + struct wakeInfo *wp = (struct wakeInfo *)arg; + epicsEventMustWait(wp->event); + epicsMutexLock(wp->countMutex); + wp->count++; + epicsMutexUnlock(wp->countMutex); +} +static void eventWakeupTest(void) +{ + struct wakeInfo wakeInfo, *wp = &wakeInfo; + int i, c; + + wp->event = epicsEventMustCreate(epicsEventEmpty); + wp->countMutex = epicsMutexMustCreate(); + wp->count = 0; + for (i = 0 ; i < SLEEPERCOUNT ; i++) + epicsThreadCreate("Sleeper", + epicsThreadPriorityScanHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + sleeper, + wp); + epicsThreadSleep(0.5); + epicsMutexLock(wp->countMutex); + c = wp->count; + epicsMutexUnlock(wp->countMutex); + testOk(c == 0, "all threads still sleeping"); + for (i = 1 ; i <= SLEEPERCOUNT ; i++) { + epicsEventSignal(wp->event); + epicsThreadSleep(0.5); + epicsMutexLock(wp->countMutex); + c = wp->count; + epicsMutexUnlock(wp->countMutex); + testOk(c == i, "%d thread%s awakened, expected %d", c, c == 1 ? "" : "s", i); + } + epicsEventDestroy(wp->event); + epicsMutexDestroy(wp->countMutex); +} + + +} // extern "C" + +static double eventWaitMeasureDelayError( const epicsEventId &id, const double & delay ) +{ + epicsTime beg = epicsTime::getCurrent(); + epicsEventWaitWithTimeout ( id, delay ); + epicsTime end = epicsTime::getCurrent(); + double meas = end - beg; + double error = fabs ( delay - meas ); + testDiag("epicsEventWaitWithTimeout(%.6f) delay error %.6f sec", + delay, error ); + return error; +} + +static void eventWaitTest() +{ + double errorSum = 0.0; + epicsEventId event = epicsEventMustCreate ( epicsEventEmpty ); + int i; + for ( i = 0u; i < 20; i++ ) { + double delay = ldexp ( 1.0 , -i ); + errorSum += eventWaitMeasureDelayError ( event, delay ); + } + errorSum += eventWaitMeasureDelayError ( event, 0.0 ); + epicsEventDestroy ( event ); + double meanError = errorSum / ( i + 1 ); + testOk(meanError < 0.05, "Average error %.6f sec", meanError); +} + + +MAIN(epicsEventTest) +{ + const int nthreads = 3; + epicsThreadId *id; + char **name; + epicsEventId event; + int status; + + testPlan(12+SLEEPERCOUNT); + + event = epicsEventMustCreate(epicsEventEmpty); + + status = epicsEventWaitWithTimeout(event, 0.0); + testOk(status == epicsEventWaitTimeout, + "epicsEventWaitWithTimeout(event, 0.0) = %d", status); + + status = epicsEventWaitWithTimeout(event, 1.0); + testOk(status == epicsEventWaitTimeout, + "epicsEventWaitWithTimeout(event, 1.0) = %d", status); + + status = epicsEventTryWait(event); + testOk(status == epicsEventWaitTimeout, + "epicsEventTryWait(event) = %d", status); + + epicsEventSignal(event); + status = epicsEventWaitWithTimeout(event, 1.0); + testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout(event, 1.0) = %d", status); + + epicsEventSignal(event); + status = epicsEventWaitWithTimeout(event,DBL_MAX); + testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout(event, DBL_MAX) = %d", status); + + epicsEventSignal(event); + status = epicsEventTryWait(event); + testOk(status == epicsEventWaitOK, + "epicsEventTryWait(event) = %d", status); + + info *pinfo = (info *)calloc(1,sizeof(info)); + pinfo->event = event; + pinfo->lockRing = epicsMutexCreate(); + pinfo->ring = epicsRingPointerCreate(1024*2); + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + + epicsThreadCreate("consumer", 50, stackSize, consumer, pinfo); + id = (epicsThreadId *)calloc(nthreads, sizeof(epicsThreadId)); + name = (char **)calloc(nthreads, sizeof(char *)); + for(int i = 0; i < nthreads; i++) { + name[i] = (char *)calloc(16, sizeof(char)); + sprintf(name[i],"producer %d",i); + id[i] = epicsThreadCreate(name[i], 40, stackSize, producer, pinfo); + epicsThreadSleep(0.1); + } + epicsThreadSleep(5.0); + + testDiag("setting quit"); + pinfo->quit = 1; + epicsThreadSleep(2.0); + + epicsEventSignal(pinfo->event); + epicsThreadSleep(1.0); + + eventWaitTest(); + eventWakeupTest(); + + return testDone(); +} diff --git a/src/libCom/test/epicsExceptionTest.cpp b/src/libCom/test/epicsExceptionTest.cpp new file mode 100644 index 000000000..c87fd65d9 --- /dev/null +++ b/src/libCom/test/epicsExceptionTest.cpp @@ -0,0 +1,114 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +// +// Verify that the local c++ exception mechanism matches the ANSI/ISO standard. +// Author: Jeff Hill +// + +#include +#include +#include +#if defined(__GNUC__) && (__GNUC__<2 || (__GNUC__==2 && __GNUC_MINOR__<=96)) +#include +#else +#include +#endif + +#include "epicsUnitTest.h" +#include "epicsThread.h" +#include "testMain.h" + +using namespace std; + +#if defined(__BORLANDC__) && defined(__linux__) +namespace std { +const nothrow_t nothrow ; +} +#endif + +#if defined ( _MSC_VER ) + // some interesting bugs found in the MS implementation of new +# if _MSC_VER > 1310 /* this gets fixed some release after visual studio 7 we hope */ + static const size_t unsuccessfulNewSize = numeric_limits < size_t > :: max (); +# else + static const size_t unsuccessfulNewSize = numeric_limits < size_t > :: max () - 100; +# endif + // passing a size_t to printf() needs "%zu" on some platforms +# define Z_MODIFIER "" +#elif defined(vxWorks) + // Neither vxWorks 5 or 6 supply true ANSI C++ + static const size_t unsuccessfulNewSize = UINT_MAX - 15u; +# define Z_MODIFIER "" +#else + static const size_t unsuccessfulNewSize = numeric_limits < size_t > :: max (); +# define Z_MODIFIER "z" +#endif + +class exThread : public epicsThreadRunable { +public: + exThread (); + void waitForCompletion (); +private: + epicsThread thread; + bool done; + void run (); +}; + +static void epicsExceptionTestPrivate () +{ + try { + char * p = new char [unsuccessfulNewSize]; + testFail("new char[%" Z_MODIFIER "u] returned %p", unsuccessfulNewSize, p); + } + catch ( const bad_alloc & ) { + testPass("new char[%" Z_MODIFIER "u] threw", unsuccessfulNewSize); + } + catch ( ... ) { + testFail("new: threw wrong type"); + } + try { + char * p = new ( nothrow ) + char [unsuccessfulNewSize]; + testOk(p == 0, "new (nothrow)"); + } + catch( ... ) { + testFail("new (nothrow): threw"); + } +} + +exThread::exThread () : + thread ( *this, "testExceptions", epicsThreadGetStackSize(epicsThreadStackSmall) ), + done ( false ) +{ + this->thread.start (); +} + +void exThread::run () +{ + epicsExceptionTestPrivate (); + this->done = true; +} + +void exThread::waitForCompletion () +{ + while ( ! this->done ) { + epicsThreadSleep ( 0.1 ); + } +} + +MAIN(epicsExceptionTest) +{ + testPlan(4); + epicsExceptionTestPrivate (); + + exThread athread; + athread.waitForCompletion (); + return testDone(); +} diff --git a/src/libCom/test/epicsExitTest.c b/src/libCom/test/epicsExitTest.c new file mode 100644 index 000000000..f464b707c --- /dev/null +++ b/src/libCom/test/epicsExitTest.c @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsExitTest.cpp */ + +/* Author: Marty Kraimer Date: 09JUL2004*/ + +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsAssert.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +typedef struct info { + char name[64]; + epicsEventId terminate; + epicsEventId terminated; +}info; + +static void atExit(void *pvt) +{ + info *pinfo = (info *)pvt; + testPass("%s reached atExit", pinfo->name); + epicsEventSignal(pinfo->terminate); + /*Now wait for thread to terminate*/ + epicsEventMustWait(pinfo->terminated); + testPass("%s destroying pinfo", pinfo->name); + epicsEventDestroy(pinfo->terminate); + epicsEventDestroy(pinfo->terminated); + free(pinfo); +} + +static void atThreadExit(void *pvt) +{ + info *pinfo = (info *)pvt; + testPass("%s terminating", pinfo->name); + epicsEventSignal(pinfo->terminated); +} + +static void thread(void *arg) +{ + info *pinfo = (info *)arg; + + strcpy(pinfo->name, epicsThreadGetNameSelf()); + testDiag("%s starting", pinfo->name); + pinfo->terminate = epicsEventMustCreate(epicsEventEmpty); + pinfo->terminated = epicsEventMustCreate(epicsEventEmpty); + epicsAtExit(atExit, pinfo); + epicsAtThreadExit(atThreadExit, pinfo); + testDiag("%s waiting for atExit", pinfo->name); + epicsEventMustWait(pinfo->terminate); +} + +static void mainExit(void *pvt) +{ + testPass("Reached mainExit"); + testDone(); +} + +MAIN(epicsExitTest) +{ + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + info *pinfoA = (info *)calloc(1, sizeof(info)); + info *pinfoB = (info *)calloc(1, sizeof(info)); + + testPlan(7); + + epicsAtExit(mainExit, NULL); + + epicsThreadCreate("threadA", 50, stackSize, thread, pinfoA); + epicsThreadSleep(0.1); + epicsThreadCreate("threadB", 50, stackSize, thread, pinfoB); + epicsThreadSleep(1.0); + + testDiag("Calling epicsExit\n"); + epicsExit(0); + return 0; +} diff --git a/src/libCom/test/epicsMathTest.c b/src/libCom/test/epicsMathTest.c new file mode 100644 index 000000000..527dd2922 --- /dev/null +++ b/src/libCom/test/epicsMathTest.c @@ -0,0 +1,73 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMathTest.c + * + * Author Marty Kraimer + */ + +#include "epicsUnitTest.h" +#include "epicsMath.h" +#include "testMain.h" + +MAIN(epicsMathTest) +{ + double huge = 1e300; + double tiny = 1e-300; + double c; + + testPlan(35); + + testOk1(!isnan(0.0)); + testOk1(!isinf(0.0)); + + testOk1(!isnan(epicsINF)); + testOk1(isinf(epicsINF)); + testOk1(epicsINF == epicsINF); + testOk1(epicsINF > 0.0); + testOk1(epicsINF - epicsINF != 0.0); + testOk1(epicsINF + -epicsINF != 0.0); + testOk1(-epicsINF + epicsINF != 0.0); + testOk1(isnan(epicsINF - epicsINF)); + testOk1(isnan(epicsINF + -epicsINF)); + testOk1(isnan(-epicsINF + epicsINF)); + + testOk1(isnan(epicsNAN)); + testOk1(!isinf(epicsNAN)); + testOk1(epicsNAN != epicsNAN); + testOk1(!(epicsNAN < epicsNAN)); + testOk1(!(epicsNAN <= epicsNAN)); + testOk1(!(epicsNAN == epicsNAN)); + testOk1(!(epicsNAN >= epicsNAN)); + testOk1(!(epicsNAN > epicsNAN)); + testOk1(isnan(epicsNAN - epicsNAN)); + testOk1(isnan(epicsNAN + -epicsNAN)); + testOk1(isnan(-epicsNAN + epicsNAN)); + + c = huge / tiny; + testOk(!isnan(c), "!isnan(1e300 / 1e-300)"); + testOk(isinf(c), "isinf(1e300 / 1e-300)"); + testOk(c > 0.0, "1e300 / 1e-300 > 0.0"); + + c = (-huge) / tiny; + testOk(!isnan(c), "!isnan(-1e300 / 1e-300)"); + testOk(isinf(c), "isinf(-1e300 / 1e-300)"); + testOk(c < 0.0, "-1e300 / 1e-300 < 0.0"); + + c = huge / huge; + testOk(!isnan(c), "!isnan(1e300 / 1e300)"); + testOk(!isinf(c), "!isinf(1e300 / 1e300)"); + testOk(c == 1.0, "1e300 / 1e300 == 1.0"); + + c = tiny / tiny; + testOk(!isnan(c), "!isnan(1e-300 / 1e-300)"); + testOk(!isinf(c), "!isinf(1e-300 / 1e-300)"); + testOk(c == 1.0, "1e300 / 1e-300 == 1.0"); + + return testDone(); +} diff --git a/src/libCom/test/epicsMaxThreads.c b/src/libCom/test/epicsMaxThreads.c new file mode 100644 index 000000000..8d0d06da5 --- /dev/null +++ b/src/libCom/test/epicsMaxThreads.c @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMaxThreads.cpp */ + +/* Author: Marty Kraimer Date: 09JUL2004*/ + +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "errlog.h" +#include "testMain.h" + +static epicsEventId started; + +static void thread(void *arg) +{ + epicsEventSignal(started); + epicsThreadSuspendSelf(); +} + +MAIN(epicsMaxThreads) +{ + unsigned int stackSize; + epicsThreadId id; + int i = 0; + + stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + printf("stackSize %d\n",stackSize); + + started = epicsEventMustCreate(epicsEventEmpty); + + while(1) { + id = epicsThreadCreate("thread",50,stackSize,thread,0); + if(!id) break; + i++; + if ((i % 100) == 0) + printf ("Reached %d...\n", i); + epicsEventMustWait(started); + } + + printf("Max number of \"Small\" threads on this OS is %d\n", i); + return 0; +} diff --git a/src/libCom/test/epicsMessageQueueTest.cpp b/src/libCom/test/epicsMessageQueueTest.cpp new file mode 100644 index 000000000..2f644ba11 --- /dev/null +++ b/src/libCom/test/epicsMessageQueueTest.cpp @@ -0,0 +1,300 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 W. Eric Norum + * norume@aps.anl.gov + * 630 252 4793 + */ +#include +#include +#include +#include + +#include "epicsMessageQueue.h" +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsEvent.h" +#include "epicsAssert.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static const char *msg1 = "1234567890This is a very long message."; +static volatile int testExit = 0; +static epicsEventId finished; + +/* + * In Numerical Recipes in C: The Art of Scientific Computing (William H. + * Press, Brian P. Flannery, Saul A. Teukolsky, William T. Vetterling; New + * York: Cambridge University Press, 1992 (2nd ed., p. 277)), the follow- + * ing comments are made: + * "If you want to generate a random integer between 1 and 10, you + * should always do it by using high-order bits, as in + * j=1+(int) (10.0*rand()/(RAND_MAX+1.0)); + * and never by anything resembling + * j=1+(rand() % 10); + */ +static int +randBelow(int n) +{ + return (int)((double)n*rand()/(RAND_MAX+1.0)); +} + +extern "C" void +badReceiver(void *arg) +{ + epicsMessageQueue *q = (epicsMessageQueue *)arg; + char cbuf[80]; + int len; + + cbuf[0] = '\0'; + len = q->receive(cbuf, 1); + if (len < 0 && cbuf[0] == '\0') + testPass("receive into undersized buffer returned error (%d)", len); + else if (len == 1 && cbuf[0] == *msg1) + testPass("receive into undersized buffer truncated message"); + else + testFail("receive into undersized buffer returned %d", len); + + epicsThreadSleep(3.0); + + cbuf[0] = '\0'; + len = q->receive(cbuf, 1); + if (len < 0 && cbuf[0] == '\0') + testPass("receive into undersized buffer returned error (%d)", len); + else if (len == 1 && cbuf[0] == *msg1) + testPass("receive into undersized buffer truncated message"); + else + testFail("receive into undersized buffer returned %d", len); +} + +extern "C" void +receiver(void *arg) +{ + epicsMessageQueue *q = (epicsMessageQueue *)arg; + char cbuf[80]; + int expectmsg[4]; + int len; + int sender, msgNum; + int errors = 0; + + for (sender = 1 ; sender <= 4 ; sender++) + expectmsg[sender-1] = 1; + while (!testExit) { + cbuf[0] = '\0'; + len = q->receive(cbuf, sizeof cbuf, 2.0); + if (len < 0 && !testExit) { + testDiag("receiver() received unexpected timeout"); + ++errors; + } + else if (sscanf(cbuf, "Sender %d -- %d", &sender, &msgNum) == 2 && + sender >= 1 && sender <= 4) { + if (expectmsg[sender-1] != msgNum) { + ++errors; + testDiag("%s received %d '%.*s' -- expected %d", epicsThreadGetNameSelf(), len, len, cbuf, expectmsg[sender-1]); + } + expectmsg[sender-1] = msgNum + 1; + epicsThreadSleep(0.001 * (randBelow(20))); + } + } + for (sender = 1 ; sender <= 4 ; sender++) { + if (expectmsg[sender-1] > 1) + testDiag("Sender %d -- %d messages", sender, expectmsg[sender-1]-1); + } + testOk1(errors == 0); + epicsEventSignal(finished); +} + +extern "C" void +sender(void *arg) +{ + epicsMessageQueue *q = (epicsMessageQueue *)arg; + char cbuf[80]; + int len; + int i = 0; + + while (!testExit) { + len = sprintf(cbuf, "%s -- %d.", epicsThreadGetNameSelf(), ++i); + while (q->trySend((void *)cbuf, len) < 0) + epicsThreadSleep(0.005 * (randBelow(5))); + epicsThreadSleep(0.005 * (randBelow(20))); + } +} + +extern "C" void messageQueueTest(void *parm) +{ + unsigned int i; + char cbuf[80]; + int len; + int pass; + int used; + int want; + + epicsMessageQueue *q1 = new epicsMessageQueue(4, 20); + + testDiag("Simple single-thread tests:"); + i = 0; + used = 0; + testOk1(q1->pending() == 0); + while (q1->trySend((void *)msg1, i ) == 0) { + i++; + testOk(q1->pending() == i, "q1->pending() == %d", i); + } + testOk1(q1->pending() == 4); + + want = 0; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 3); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + q1->trySend((void *)msg1, i++); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + q1->trySend((void *)msg1, i++); + testOk1(q1->pending() == 3); + + i = 3; + while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { + --i; + testOk(q1->pending() == i, "q1->pending() == %d", i); + want++; + if (!testOk1((len == want) & (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + } + testOk1(q1->pending() == 0); + + testDiag("Test sender timeout:"); + i = 0; + used = 0; + testOk1(q1->pending() == 0); + while (q1->send((void *)msg1, i, 1.0 ) == 0) { + i++; + testOk(q1->pending() == i, "q1->pending() == %d", i); + } + testOk1(q1->pending() == 4); + + want = 0; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 3); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + q1->send((void *)msg1, i++, 1.0); + + want++; + len = q1->receive(cbuf, sizeof cbuf); + testOk1(q1->pending() == 2); + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + q1->send((void *)msg1, i++, 1.0); + testOk1(q1->pending() == 3); + + i = 3; + while ((len = q1->receive(cbuf, sizeof cbuf, 1.0)) >= 0) { + --i; + testOk(q1->pending() == i, "q1->pending() == %d", i); + want++; + if (!testOk1((len == want) && (strncmp(msg1, cbuf, len) == 0))) + testDiag("wanted:%d '%.*s' got:%d '%.*s'", want, want, msg1, len, len, cbuf); + } + testOk1(q1->pending() == 0); + + testDiag("Test receiver with timeout:"); + for (i = 0 ; i < 4 ; i++) + testOk1 (q1->send((void *)msg1, i, 1.0) == 0); + testOk1(q1->pending() == 4); + for (i = 0 ; i < 4 ; i++) + testOk(q1->receive((void *)cbuf, sizeof cbuf, 1.0) == (int)i, + "q1->receive(...) == %d", i); + testOk1(q1->pending() == 0); + testOk1(q1->receive((void *)cbuf, sizeof cbuf, 1.0) < 0); + testOk1(q1->pending() == 0); + + testDiag("Single receiver with invalid size, single sender tests:"); + epicsThreadCreate("Bad Receiver", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), badReceiver, q1); + epicsThreadSleep(1.0); + testOk(q1->send((void *)msg1, 10) == 0, "Send with waiting receiver"); + epicsThreadSleep(2.0); + testOk(q1->send((void *)msg1, 10) == 0, "Send with no receiver"); + epicsThreadSleep(2.0); + + testDiag("Single receiver, single sender tests:"); + epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityHigh); + epicsThreadCreate("Receiver one", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), receiver, q1); + for (pass = 1 ; pass <= 3 ; pass++) { + for (i = 0 ; i < 10 ; i++) { + if (q1->trySend((void *)msg1, i) < 0) + break; + if (pass >= 3) + epicsThreadSleep(0.5); + } + switch (pass) { + case 1: + if (i<6) + testDiag(" priority-based scheduler, sent %d messages", i); + epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityLow); + break; + case 2: + if (i<10) + testDiag(" scheduler not strict priority, sent %d messages", i); + else + testDiag(" strict priority scheduler, sent 10 messages"); + break; + case 3: + testOk(i == 10, "%d of 10 messages sent with sender pauses", i); + break; + } + epicsThreadSleep(1.0); + } + + /* + * Single receiver, multiple sender tests + */ + testDiag("Single receiver, multiple sender tests:"); + testDiag("This test takes 5 minutes..."); + epicsThreadCreate("Sender 1", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1); + epicsThreadCreate("Sender 2", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1); + epicsThreadCreate("Sender 3", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1); + epicsThreadCreate("Sender 4", epicsThreadPriorityHigh, epicsThreadGetStackSize(epicsThreadStackMedium), sender, q1); + + epicsThreadSleep(300.0); + + testExit = 1; +} + +MAIN(epicsMessageQueueTest) +{ + testPlan(58); + + finished = epicsEventMustCreate(epicsEventEmpty); + + epicsThreadCreate("messageQueueTest", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + messageQueueTest, NULL); + + epicsEventWait(finished); + + return testDone(); +} diff --git a/src/libCom/test/epicsMutexTest.cpp b/src/libCom/test/epicsMutexTest.cpp new file mode 100644 index 000000000..87cf0a460 --- /dev/null +++ b/src/libCom/test/epicsMutexTest.cpp @@ -0,0 +1,283 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsMutexTest.c */ + +/* + * Author: Marty Kraimer Date: 26JAN2000 + * Jeff Hill (added mutex performance test ) + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +typedef struct info { + int threadnum; + epicsMutexId mutex; + int quit; +}info; + +extern "C" void mutexThread(void * arg) +{ + info *pinfo = (info *)arg; + testDiag("mutexThread %d starting", pinfo->threadnum); + while (pinfo->quit--) { + epicsMutexLockStatus status = epicsMutexLock(pinfo->mutex); + testOk(status == epicsMutexLockOK, + "mutexThread %d epicsMutexLock returned %d", + pinfo->threadnum, (int)status); + epicsThreadSleep(.1); + epicsMutexUnlock(pinfo->mutex); + epicsThreadSleep(.9); + } + testDiag("mutexThread %d exiting", pinfo->threadnum); + return; +} + +inline void lockPair ( epicsMutex & mutex ) +{ + mutex.lock (); + mutex.unlock (); +} + +inline void tenLockPairs ( epicsMutex & mutex ) +{ + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); + lockPair ( mutex ); +} + +inline void tenLockPairsSquared ( epicsMutex & mutex ) +{ + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); + tenLockPairs ( mutex ); +} + +inline void doubleRecursiveLockPair ( epicsMutex & mutex ) +{ + mutex.lock (); + mutex.lock (); + mutex.unlock (); + mutex.unlock (); +} + +inline void tenDoubleRecursiveLockPairs ( epicsMutex & mutex ) +{ + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); + doubleRecursiveLockPair ( mutex ); +} + +inline void tenDoubleRecursiveLockPairsSquared ( epicsMutex & mutex ) +{ + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); + tenDoubleRecursiveLockPairs ( mutex ); +} + +inline void quadRecursiveLockPair ( epicsMutex & mutex ) +{ + mutex.lock (); + mutex.lock (); + mutex.lock (); + mutex.lock (); + mutex.unlock (); + mutex.unlock (); + mutex.unlock (); + mutex.unlock (); +} + +inline void tenQuadRecursiveLockPairs ( epicsMutex & mutex ) +{ + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); + quadRecursiveLockPair ( mutex ); +} + +inline void tenQuadRecursiveLockPairsSquared ( epicsMutex & mutex ) +{ + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); + tenQuadRecursiveLockPairs ( mutex ); +} + +void epicsMutexPerformance () +{ + epicsMutex mutex; + unsigned i; + + // test a single lock pair + epicsTime begin = epicsTime::getCurrent (); + static const unsigned N = 1000; + for ( i = 0; i < N; i++ ) { + tenLockPairsSquared ( mutex ); + } + double delay = epicsTime::getCurrent () - begin; + delay /= N * 100u; // convert to delay per lock pair + delay *= 1e6; // convert to micro seconds + testDiag("lock()*1/unlock()*1 takes %f microseconds", delay); + + // test a two times recursive lock pair + begin = epicsTime::getCurrent (); + for ( i = 0; i < N; i++ ) { + tenDoubleRecursiveLockPairsSquared ( mutex ); + } + delay = epicsTime::getCurrent () - begin; + delay /= N * 100u; // convert to delay per lock pair + delay *= 1e6; // convert to micro seconds + testDiag("lock()*2/unlock()*2 takes %f microseconds", delay); + + // test a four times recursive lock pair + begin = epicsTime::getCurrent (); + for ( i = 0; i < N; i++ ) { + tenQuadRecursiveLockPairsSquared ( mutex ); + } + delay = epicsTime::getCurrent () - begin; + delay /= N * 100u; // convert to delay per lock pair + delay *= 1e6; // convert to micro seconds + testDiag("lock()*4/unlock()*4 takes %f microseconds", delay); +} + +struct verifyTryLock { + epicsMutexId mutex; + epicsEventId done; +}; + +extern "C" void verifyTryLockThread ( void *pArg ) +{ + struct verifyTryLock *pVerify = + ( struct verifyTryLock * ) pArg; + + testOk1(epicsMutexTryLock(pVerify->mutex) == epicsMutexLockTimeout); + epicsEventSignal ( pVerify->done ); +} + +void verifyTryLock () +{ + struct verifyTryLock verify; + + verify.mutex = epicsMutexMustCreate (); + verify.done = epicsEventMustCreate ( epicsEventEmpty ); + + testOk1(epicsMutexTryLock(verify.mutex) == epicsMutexLockOK); + + epicsThreadCreate ( "verifyTryLockThread", 40, + epicsThreadGetStackSize(epicsThreadStackSmall), + verifyTryLockThread, &verify ); + + testOk1(epicsEventWait ( verify.done ) == epicsEventWaitOK); + + epicsMutexUnlock ( verify.mutex ); + epicsMutexDestroy ( verify.mutex ); + epicsEventDestroy ( verify.done ); +} + +MAIN(epicsMutexTest) +{ + const int nthreads = 3; + const int nrounds = 5; + unsigned int stackSize; + epicsThreadId *id; + int i; + char **name; + void **arg; + info **pinfo; + epicsMutexId mutex; + int status; + + testPlan(5 + nthreads * nrounds); + + verifyTryLock (); + + mutex = epicsMutexMustCreate(); + status = epicsMutexLock(mutex); + testOk(status == 0, "epicsMutexLock returned %d", status); + status = epicsMutexTryLock(mutex); + testOk(status == 0, "epicsMutexTryLock returned %d", status); + epicsMutexUnlock(mutex); + epicsMutexUnlock(mutex); + + id = (epicsThreadId *)calloc(nthreads,sizeof(epicsThreadId)); + name = (char **)calloc(nthreads,sizeof(char *)); + arg = (void **)calloc(nthreads,sizeof(void *)); + pinfo = (info **)calloc(nthreads,sizeof(info *)); + stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + for(i=0; ithreadnum = i; + pinfo[i]->mutex = mutex; + pinfo[i]->quit = nrounds; + arg[i] = pinfo[i]; + id[i] = epicsThreadCreate(name[i],40,stackSize, + mutexThread, + arg[i]); + } + epicsThreadSleep(2.0 + nrounds); + + epicsMutexPerformance (); + + return testDone(); +} diff --git a/src/libCom/test/epicsRunLibComTests.c b/src/libCom/test/epicsRunLibComTests.c new file mode 100644 index 000000000..80ddbf3b3 --- /dev/null +++ b/src/libCom/test/epicsRunLibComTests.c @@ -0,0 +1,104 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Run libCom tests as a batch. + * + * Do *not* include performance measurements here, they don't help to + * prove functionality (which is the point of this convenience routine). + */ + +#include +#include +#include + +int epicsThreadTest(void); +int epicsTimerTest(void); +int epicsAlgorithm(void); +int epicsEllTest(void); +int epicsErrlogTest(void); +int epicsCalcTest(void); +int epicsEventTest(void); +int epicsExceptionTest(void); +int epicsMathTest(void); +int epicsMessageQueueTest(void); +int epicsMutexTest(void); +int epicsStdioTest(void); +int epicsStringTest(void); +int epicsThreadOnceTest(void); +int epicsThreadPriorityTest(void); +int epicsThreadPrivateTest(void); +int epicsTimeTest(void); +int macLibTest(void); +int macEnvExpandTest(void); +int ringPointerTest(void); +int ringBytesTest(void); +int blockingSockTest(void); +int taskwdTest(void); +int epicsExitTest(void); + +void epicsRunLibComTests(void) +{ + testHarness(); + + /* + * Thread startup sets some internal variables so do it first + */ + runTest(epicsThreadTest); + + /* + * Timer tests get confused if run after some of the other tests + */ + runTest(epicsTimerTest); + + runTest(epicsAlgorithm); + + runTest(epicsEllTest); + + runTest(epicsErrlogTest); + + runTest(epicsCalcTest); + + runTest(epicsEventTest); + + runTest(epicsExceptionTest); + + runTest(epicsMathTest); + + runTest(epicsMessageQueueTest); + + runTest(epicsMutexTest); + + runTest(epicsStdioTest); + + runTest(epicsStringTest); + + runTest(epicsThreadOnceTest); + + runTest(epicsThreadPriorityTest); + + runTest(epicsThreadPrivateTest); + + runTest(epicsTimeTest); + + runTest(macLibTest); + + runTest(macEnvExpandTest); + + runTest(ringPointerTest); + + runTest(ringBytesTest); + + runTest(blockingSockTest); + + runTest(taskwdTest); + + /* + * Exit must come last as it never returns + */ + runTest(epicsExitTest); +} diff --git a/src/libCom/test/epicsStdioTest.c b/src/libCom/test/epicsStdioTest.c new file mode 100644 index 000000000..da4256805 --- /dev/null +++ b/src/libCom/test/epicsStdioTest.c @@ -0,0 +1,128 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsStdioTest.c + * + * Author Marty Kraimer + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsStdio.h" +#include "epicsStdioRedirect.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +#define LINE_1 "# This is first line of sample report\n" +#define LINE_2 "# This is second and last line of sample report\n" + +static void testEpicsSnprintf(void) { + char exbuffer[80], buffer[80]; + const int ivalue = 1234; + const float fvalue = 1.23e4f; + const char *svalue = "OneTwoThreeFour"; + const char *format = "int %d float %8.2e string %s"; + const char *expected = exbuffer; + int size; + int rtn, rlen; + + sprintf(exbuffer, format, ivalue, fvalue, svalue); + rlen = strlen(expected)+1; + + strcpy(buffer, "AAAA"); + + for (size = 1; size < strlen(expected) + 5; ++size) { + rtn = epicsSnprintf(buffer, size, format, ivalue, fvalue, svalue); + testOk(rtn <= rlen-1, "epicsSnprintf(size=%d) = %d", size, rtn); + if (rtn != rlen-1) + testDiag("Return value does not indicate buffer size needed"); + testOk(strncmp(buffer, expected, size - 1) == 0, + "buffer = '%s'", buffer); + rtn = strlen(buffer); + testOk(rtn == (size < rlen ? size : rlen) - 1, + "length = %d", rtn); + } +} + +void testStdoutRedir (const char *report) +{ + FILE *realStdout = stdout; + FILE *stream = 0; + char linebuf[80]; + size_t buflen = sizeof linebuf; + + testOk1(epicsGetStdout() == stdout); + + errno = 0; + if (!testOk1((stream = fopen(report, "w")) != NULL)) { + testDiag("'%s' could not be opened for writing: %s", + report, strerror(errno)); + testSkip(11, "Can't create stream file"); + return; + } + + epicsSetThreadStdout(stream); + testOk1(stdout == stream); + + printf(LINE_1); + printf(LINE_2); + + epicsSetThreadStdout(0); + testOk1(epicsGetStdout() == realStdout); + testOk1(stdout == realStdout); + + errno = 0; + if (!testOk1(!fclose(stream))) + testDiag("fclose error: %s\n", strerror(errno)); + + if (!testOk1((stream = fopen(report, "r")) != NULL)) { + testDiag("'%s' could not be opened for reading: %s", + report, strerror(errno)); + testSkip(6, "Can't reopen stream file."); + return; + } + + if (!testOk1(fgets(linebuf, buflen, stream) != NULL)) { + testDiag("File read error: %s", strerror(errno)); + testSkip(5, "Read failed."); + fclose(stream); + return; + } + testOk(strcmp(linebuf, LINE_1) == 0, "First line correct"); + + if (!testOk1(fgets(linebuf, buflen, stream) != NULL)) { + testDiag("File read error: %s", strerror(errno)); + testSkip(1, "No line to compare."); + } else + testOk(strcmp(linebuf, LINE_2) == 0, "Second line"); + + testOk(!fgets(linebuf, buflen, stream), "File ends"); + + if (!testOk1(!fclose(stream))) + testDiag("fclose error: %s\n", strerror(errno)); +} + +MAIN(epicsStdioTest) +{ +#ifdef _WIN32 + testPlan(166); +#else + testPlan(163); +#endif + testEpicsSnprintf(); + testStdoutRedir("report"); + return testDone(); +} diff --git a/src/libCom/test/epicsStringTest.c b/src/libCom/test/epicsStringTest.c new file mode 100644 index 000000000..a65365eb8 --- /dev/null +++ b/src/libCom/test/epicsStringTest.c @@ -0,0 +1,89 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 Marty Kraimer + */ + +#include +#include +#include + +#include "epicsUnitTest.h" +#include "epicsString.h" +#include "testMain.h" + +void testChars(void) { + int i; + char input[2] = {0, 0}; + char escaped[20]; + char result[20]; + size_t s, t, needed; + + for (i = 255; i >= 0; --i) { + input[0] = i; + needed = epicsStrnEscapedFromRawSize(input, 1); + s = epicsStrnEscapedFromRaw(escaped, sizeof(escaped), input, 1); + t = epicsStrnRawFromEscaped(result, sizeof(result), escaped, s); + testOk(needed == s && t == 1 && + result[0] == input[0] && result[1] == 0, + "escaped char 0x%2.2x -> \"%s\" (%d) -> 0x%2.2x", + input[0] & 0xff, escaped, (int) needed, result[0] & 0xff); + } +} + +MAIN(epicsStringTest) +{ + const char * const empty = ""; + const char * const space = " "; + const char * const A = "A"; + const char * const ABCD = "ABCD"; + const char * const ABCDE = "ABCDE"; + const char * const a = "a"; + const char * const abcd = "abcd"; + const char * const abcde = "abcde"; + char *s; + + testPlan(281); + + testChars(); + + testOk1(epicsStrnCaseCmp(empty, "", 0) == 0); + testOk1(epicsStrnCaseCmp(empty, "", 1) == 0); + testOk1(epicsStrnCaseCmp(space, empty, 1) < 0); + testOk1(epicsStrnCaseCmp(empty, space, 1) > 0); + testOk1(epicsStrnCaseCmp(a, A, 1) == 0); + testOk1(epicsStrnCaseCmp(a, A, 2) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCD, 2) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCD, 4) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCD, 1000) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCDE, 2) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCDE, 4) == 0); + testOk1(epicsStrnCaseCmp(abcd, ABCDE, 1000)> 0); + testOk1(epicsStrnCaseCmp(abcde, ABCD, 2) == 0); + testOk1(epicsStrnCaseCmp(abcde, ABCD, 4) == 0); + testOk1(epicsStrnCaseCmp(abcde, ABCD, 1000) < 0); + + testOk1(epicsStrCaseCmp(empty, "") == 0); + testOk1(epicsStrCaseCmp(a, A) == 0); + testOk1(epicsStrCaseCmp(abcd, ABCD) == 0); + testOk1(epicsStrCaseCmp(abcd, ABCDE) != 0); + testOk1(epicsStrCaseCmp(abcde, ABCD) != 0); + testOk1(epicsStrCaseCmp(abcde, "ABCDF") != 0); + + s = epicsStrDup(abcd); + testOk(strcmp(s, abcd) == 0 && s != abcd, "epicsStrDup"); + free(s); + + testOk1(epicsStrHash(abcd, 0) != epicsStrHash("bacd", 0)); + testOk1(epicsStrHash(abcd, 0) == epicsMemHash(abcde, 4, 0)); + testOk1(epicsStrHash(abcd, 0) != epicsMemHash("abcd\0", 5, 0)); + + return testDone(); +} diff --git a/src/libCom/test/epicsThreadOnceTest.c b/src/libCom/test/epicsThreadOnceTest.c new file mode 100644 index 000000000..f4eb58709 --- /dev/null +++ b/src/libCom/test/epicsThreadOnceTest.c @@ -0,0 +1,115 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* Revision-Id: anj@aps.anl.gov-20101029165844-o9n3mdago2f60fsb */ + +#include +#include + +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsMutex.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define NUM_ONCE_THREADS 8 + +epicsThreadOnceId onceFlag = EPICS_THREAD_ONCE_INIT; +epicsThreadOnceId twiceFlag = EPICS_THREAD_ONCE_INIT; +epicsMutexId lock; +epicsEventId go; +epicsEventId done; + +int runCount = 0; +int initCount = 0; +char initBy[20]; +int doneCount = 0; + +void onceInit(void *ctx) +{ + initCount++; + strcpy(initBy, epicsThreadGetNameSelf()); +} + +void onceThread(void *ctx) +{ + epicsMutexMustLock(lock); + runCount++; + epicsMutexUnlock(lock); + + epicsEventMustWait(go); + epicsEventSignal(go); + + epicsThreadOnce(&onceFlag, onceInit, ctx); + testOk(initCount == 1, "%s: initCount = %d", + epicsThreadGetNameSelf(), initCount); + + epicsMutexMustLock(lock); + doneCount++; + if (doneCount == runCount) + epicsEventSignal(done); + epicsMutexUnlock(lock); +} + + +void recurseInit(void); +void onceRecurse(void *ctx) +{ + recurseInit(); +} + +void recurseInit(void) +{ + epicsThreadOnce(&twiceFlag, onceRecurse, 0); +} + +void recurseThread(void *ctx) +{ + recurseInit(); + testFail("Recursive epicsThreadOnce() not detected"); +} + + +MAIN(epicsThreadOnceTest) +{ + int i; + epicsThreadId tid; + + testPlan(3 + NUM_ONCE_THREADS); + + go = epicsEventMustCreate(epicsEventEmpty); + done = epicsEventMustCreate(epicsEventEmpty); + lock = epicsMutexMustCreate(); + + for (i = 0; i < NUM_ONCE_THREADS; i++) { + char name[20]; + + sprintf(name, "once-%d", i); + epicsThreadCreate(name, epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + onceThread, 0); + } + epicsThreadSleep(0.1); + + testOk(runCount == NUM_ONCE_THREADS, "runCount = %d", runCount); + epicsEventSignal(go); /* Use epicsEventBroadcast(go) when available */ + epicsEventMustWait(done); + + testOk(doneCount == NUM_ONCE_THREADS, "doneCount = %d", doneCount); + testDiag("init was run by %s", initBy); + + testDiag("Expecting thread recurse to suspend:"); + tid = epicsThreadCreate("recurse", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackSmall), + recurseThread, 0); + do { + epicsThreadSleep(0.1); + } while (!epicsThreadIsSuspended(tid)); + testPass("Recursive epicsThreadOnce() detected"); + + return testDone(); +} diff --git a/src/libCom/test/epicsThreadPerform.cpp b/src/libCom/test/epicsThreadPerform.cpp new file mode 100644 index 000000000..e01fc9430 --- /dev/null +++ b/src/libCom/test/epicsThreadPerform.cpp @@ -0,0 +1,228 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsThreadPerform.cpp */ + +/* sleep accuracy and sleep quantum tests by Jeff Hill */ +/* epicsThreadGetIdSelf performance by Jeff Hill */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsTime.h" +#include "errlog.h" +#include "testMain.h" + +static void testPriority(const char *who) +{ + epicsThreadId id; + unsigned int oldPriority,newPriority; + + id = epicsThreadGetIdSelf(); + oldPriority = epicsThreadGetPriority(id); + epicsThreadSetPriority(id,epicsThreadPriorityMax); + newPriority = epicsThreadGetPriority(id); + epicsThreadSetPriority(id,oldPriority); + printf("testPriority %s\n id %p old %u new %u\n", + who,id,oldPriority,newPriority); +} + +extern "C" void testPriorityThread(void *arg) +{ + testPriority("thread"); +} + +static void epicsThreadPriorityTest() +{ + testPriority("main error expected from epicsThreadSetPriority"); + epicsThreadCreate("testPriorityThread",epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium),testPriorityThread,0); + epicsThreadSleep(0.5); +} + + +static double threadSleepMeasureDelayError ( const double & delay ) +{ + epicsTime beg = epicsTime::getCurrent(); + epicsThreadSleep ( delay ); + epicsTime end = epicsTime::getCurrent(); + double meas = end - beg; + double error = fabs ( delay - meas ); + return error; +} + +static double measureSleepQuantum ( + const unsigned iterations, const double testInterval ) +{ + double errorSum = 0.0; + printf ( "Estimating sleep quantum" ); + fflush ( stdout ); + for ( unsigned i = 0u; i < iterations; i++ ) { + // try to guarantee a uniform probability density function + // by intentionally burning some CPU until we are less + // likely to be aligned with the schedualing clock + double interval = rand (); + interval /= RAND_MAX; + interval *= testInterval; + epicsTime start = epicsTime::getCurrent (); + epicsTime current = start; + while ( current - start < interval ) { + current = epicsTime::getCurrent (); + } + errorSum += threadSleepMeasureDelayError ( testInterval ); + if ( i % ( iterations / 10 ) == 0 ) { + printf ( "." ); + fflush ( stdout ); + } + } + printf ( "done\n" ); + + // with a uniform probability density function the + // sleep delay error mean is one half of the quantum + double quantumEstimate = 2 * errorSum / iterations; + return quantumEstimate; +} + +static void threadSleepQuantumTest () +{ + const double quantum = epicsThreadSleepQuantum (); + + double quantumEstimate = measureSleepQuantum ( 10, 10 * quantum ); + quantumEstimate = measureSleepQuantum ( 100, 2 * quantumEstimate ); + + double quantumError = fabs ( quantumEstimate - quantum ) / quantum; + const char * pTol = 0; + if ( quantumError > 0.1 ) { + pTol = "10%"; + } + else if ( quantumError > 0.01 ) { + pTol = "1%"; + } + else if ( quantumError > 0.001 ) { + pTol = "0.1%"; + } + if ( pTol ) { + printf ( + "The epicsThreadSleepQuantum() call returns %f sec.\n", + quantum ); + printf ( + "This doesnt match the quantum estimate " + "of %f sec within %s.\n", + quantumEstimate, pTol ); + } +} + + +static void threadSleepTest () +{ + static const int iterations = 20; + const double quantum = epicsThreadSleepQuantum (); + double errorSum = threadSleepMeasureDelayError ( 0.0 ); + for ( int i = 0u; i < iterations; i++ ) { + double delay = ldexp ( 1.0 , -i ); + double error = + threadSleepMeasureDelayError ( delay ); + errorSum += error; + if ( error > quantum + quantum / 8.0 ) { + printf ( "epicsThreadSleep ( %10f ) delay err %10f sec\n", + delay, error ); + } + } + double averageError = errorSum / ( iterations + 1 ); + if ( averageError > quantum ) { + printf ( "Average sleep delay error was %f sec\n", averageError ); + } +} + +static void epicsThreadGetIdSelfPerfTest () +{ + static const unsigned N = 10000; + static const double microSecPerSec = 1e6; + epicsTime begin = epicsTime::getCurrent (); + for ( unsigned i = 0u; i < N; i++ ) { + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + epicsThreadGetIdSelf (); + }; + epicsTime end = epicsTime::getCurrent (); + printf ( "It takes %f micro sec to call epicsThreadGetIdSelf ()\n", + microSecPerSec * ( end - begin ) / (10 * N) ); +} + + +static epicsThreadPrivate < bool > priv; + +static inline void callItTenTimes () +{ + bool *pFlag; + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); + pFlag = priv.get (); +} + +static inline void callItTenTimesSquared () +{ + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); + callItTenTimes (); +} + +static void timeEpicsThreadPrivateGet () +{ + priv.set ( 0 ); + + epicsTime begin = epicsTime::getCurrent (); + static const unsigned N = 1000u; + for ( unsigned i = 0u; i < N; i++ ) { + callItTenTimesSquared (); + } + double delay = epicsTime::getCurrent() - begin; + delay /= N * 100u; // convert to sec per call + delay *= 1e6; // convert to micro sec + printf("epicsThreadPrivateGet() takes %f microseconds\n", delay); +} + + +MAIN(epicsThreadPerform) +{ + epicsThreadPriorityTest (); + threadSleepQuantumTest (); + threadSleepTest (); + epicsThreadGetIdSelfPerfTest (); + timeEpicsThreadPrivateGet (); + return 0; +} diff --git a/src/libCom/test/epicsThreadPriorityTest.cpp b/src/libCom/test/epicsThreadPriorityTest.cpp new file mode 100644 index 000000000..4f5a82d9e --- /dev/null +++ b/src/libCom/test/epicsThreadPriorityTest.cpp @@ -0,0 +1,121 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsdThreadPriorityTest.cpp */ + +/* Author: Marty Kraimer Date: 21NOV2005 */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsEvent.h" +#include "epicsExit.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +typedef struct info { + epicsEventId waitForMaster; + epicsEventId waitForClient; +}info; + +extern "C" { + +static void client(void *arg) +{ + info *pinfo = (info *)arg; + epicsThreadId idSelf = epicsThreadGetIdSelf(); + int pass; + + for (pass = 0 ; pass < 3 ; pass++) { + epicsEventWaitStatus status; + status = epicsEventWait(pinfo->waitForMaster); + testOk(status == epicsEventWaitOK, + "task %p epicsEventWait returned %d", idSelf, status); + epicsThreadSleep(0.01); + epicsEventSignal(pinfo->waitForClient); + } +} + +static void runThreadPriorityTest(void *arg) +{ + epicsEventId testComplete = (epicsEventId) arg; + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + + epicsThreadId myId = epicsThreadGetIdSelf(); + epicsThreadSetPriority(myId, 50); + + info *pinfo = (info *)calloc(1, sizeof(info)); + pinfo->waitForMaster = epicsEventMustCreate(epicsEventEmpty); + pinfo->waitForClient = epicsEventMustCreate(epicsEventEmpty); + + epicsThreadId clientId = epicsThreadCreate("client", + 50, stackSize, client, pinfo); + epicsEventSignal(pinfo->waitForMaster); + + epicsEventWaitStatus status; + status = epicsEventWaitWithTimeout(pinfo->waitForClient, 0.1); + if (!testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout returned %d", status)) { + testSkip(2, "epicsEventWaitWithTimeout failed"); + goto done; + } + + epicsThreadSetPriority(clientId, 20); + /* expect that client will not be able to run */ + epicsEventSignal(pinfo->waitForMaster); + + status = epicsEventTryWait(pinfo->waitForClient); + if (status != epicsEventWaitTimeout) { + testFail("epicsEventTryWait returned %d", status); + } else { + status = epicsEventWaitWithTimeout(pinfo->waitForClient, 0.1); + if (!testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout returned %d", status)) { + testSkip(1, "epicsEventWaitWithTimeout failed"); + goto done; + } + } + epicsThreadSetPriority(clientId, 80); + /* expect that client will be able to run */ + epicsEventSignal(pinfo->waitForMaster); + status = epicsEventTryWait(pinfo->waitForClient); + if (status==epicsEventWaitOK) { + testPass("Strict priority scheduler"); + } else { + testDiag("No strict priority scheduler"); + status = epicsEventWaitWithTimeout(pinfo->waitForClient,.1); + testOk(status == epicsEventWaitOK, + "epicsEventWaitWithTimeout returned %d", status); + } +done: + epicsThreadSleep(1.0); + epicsEventSignal(testComplete); +} + +} /* extern "C" */ + + +MAIN(epicsThreadPriorityTest) +{ + testPlan(7); + epicsEventId testComplete = epicsEventMustCreate(epicsEventEmpty); + epicsThreadMustCreate("threadPriorityTest", epicsThreadPriorityMedium, + epicsThreadGetStackSize(epicsThreadStackMedium), + runThreadPriorityTest, testComplete); + epicsEventWaitStatus status = epicsEventWait(testComplete); + testOk(status == epicsEventWaitOK, + "epicsEventWait returned %d", status); + return testDone(); +} diff --git a/src/libCom/test/epicsThreadPrivateTest.cpp b/src/libCom/test/epicsThreadPrivateTest.cpp new file mode 100644 index 000000000..4a1494833 --- /dev/null +++ b/src/libCom/test/epicsThreadPrivateTest.cpp @@ -0,0 +1,49 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 Date: March 28 2001 */ + +#include + +#include "epicsTime.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static epicsThreadPrivate < bool > priv; + +extern "C" void epicsThreadPrivateTestThread ( void * ) +{ + testOk1 ( NULL == priv.get () ); + bool var = true; + priv.set ( &var ); + testOk1 ( &var == priv.get () ); +} + +MAIN(epicsThreadPrivateTest) +{ + testPlan(5); + + bool var = false; + priv.set ( &var ); + testOk1 ( &var == priv.get() ); + + epicsThreadCreate ( "epicsThreadPrivateTest", epicsThreadPriorityMax, + epicsThreadGetStackSize ( epicsThreadStackSmall ), + epicsThreadPrivateTestThread, 0 ); + epicsThreadSleep ( 1.0 ); + testOk1 ( &var == priv.get() ); + + priv.set ( NULL ); + testOk1 ( NULL == priv.get() ); + + return testDone(); +} + diff --git a/src/libCom/test/epicsThreadTest.cpp b/src/libCom/test/epicsThreadTest.cpp new file mode 100644 index 000000000..b1e28bb8c --- /dev/null +++ b/src/libCom/test/epicsThreadTest.cpp @@ -0,0 +1,111 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsThreadTest.cpp */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsTime.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +static epicsThreadPrivate privateKey; + +class myThread: public epicsThreadRunable { +public: + myThread(int arg,const char *name); + virtual ~myThread(); + virtual void run(); + epicsThread thread; +private: + int *argvalue; +}; + +myThread::myThread(int arg,const char *name) : + thread(*this,name,epicsThreadGetStackSize(epicsThreadStackSmall),50+arg), + argvalue(0) +{ + argvalue = new int; + *argvalue = arg; + thread.start(); +} + +myThread::~myThread() {delete argvalue;} + +void myThread::run() +{ + int *pset = argvalue; + privateKey.set(argvalue); + epicsThreadSleep(2.0); + int *pget = privateKey.get(); + testOk1(pget == pset); + + epicsThreadId self = epicsThreadGetIdSelf(); + testOk1(thread.getPriority() == epicsThreadGetPriority(self)); +} + + +typedef struct info { + int isOkToBlock; +} info; + +extern "C" { +static void thread(void *arg) +{ + info *pinfo = (info *)arg; + + epicsThreadSetOkToBlock(pinfo->isOkToBlock); + epicsThreadSleep(1.0); + + testOk(epicsThreadIsOkToBlock() == pinfo->isOkToBlock, + "%s epicsThreadIsOkToBlock() = %d", + epicsThreadGetNameSelf(), pinfo->isOkToBlock); + epicsThreadSleep(0.1); +} +} + + +MAIN(epicsThreadTest) +{ + testPlan(8); + + const int ntasks = 3; + myThread *myThreads[ntasks]; + + int startPriority = 0; + for (int i = 0; i < ntasks; i++) { + char name[10]; + sprintf(name, "t%d", i); + myThreads[i] = new myThread(i, name); + if (i == 0) + startPriority = myThreads[i]->thread.getPriority(); + myThreads[i]->thread.setPriority(startPriority + i); + } + epicsThreadSleep(3.0); + + unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall); + + info infoA = {0}; + epicsThreadCreate("threadA", 50, stackSize, thread, &infoA); + + info infoB = {1}; + epicsThreadCreate("threadB", 50, stackSize, thread, &infoB); + + epicsThreadSleep(2.0); + + return testDone(); +} diff --git a/src/libCom/test/epicsTimeTest.cpp b/src/libCom/test/epicsTimeTest.cpp new file mode 100644 index 000000000..31a40f2b6 --- /dev/null +++ b/src/libCom/test/epicsTimeTest.cpp @@ -0,0 +1,190 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Authors: Jeff Hill, Marty Kraimer and Andrew Johnson + */ +#include +#include +#include +#include +#include +#include + +#include "epicsTime.h" +#include "epicsThread.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +using namespace std; + +/* The functionality of the old invalidFormatTest () and badNanosecTest () + * routines is incorporated into epicsTimeTest () below. + */ + +struct l_fp { /* NTP time stamp */ + epicsUInt32 l_ui; /* sec past NTP epoch */ + epicsUInt32 l_uf; /* fractional seconds */ +}; + +epicsTime useSomeCPU; + +static const unsigned mSecPerSec = 1000u; +static const unsigned uSecPerSec = 1000u * mSecPerSec; +static const unsigned nSecPerSec = 1000u * uSecPerSec; + + +MAIN(epicsTimeTest) +{ + const unsigned wasteTime = 100000u; + const int nTimes = 10; + const double precisionEPICS = 1.0 / nSecPerSec; + + testPlan(15 + nTimes * 18); + + const epicsTime begin = epicsTime::getCurrent(); + + { + const epicsTimeStamp epochTS = {0, 0}; + epicsTime epochET = epochTS; + struct gm_tm_nano_sec epicsEpoch = epochET; + testOk1(epicsEpoch.ansi_tm.tm_sec == 0); + testOk1(epicsEpoch.ansi_tm.tm_min == 0); + testOk1(epicsEpoch.ansi_tm.tm_hour == 0); + testOk1(epicsEpoch.ansi_tm.tm_yday == 0); + testOk1(epicsEpoch.ansi_tm.tm_year == 90); + } + + { + epicsTime tsi = epicsTime::getCurrent (); + l_fp ntp = tsi; + epicsTime tsf = ntp; + const double diff = fabs ( tsf - tsi ); + // the difference in the precision of the two time formats + static const double precisionNTP = 1.0 / ( 1.0 + 0xffffffff ); + testOk1(diff <= precisionEPICS + precisionNTP); + } + + { // badNanosecTest + static const char * pFormat = "%a %b %d %Y %H:%M:%S.%4f"; + try { + const epicsTimeStamp badTS = {1, 1000000000}; + epicsTime ts(badTS); + char buf [32]; + ts.strftime(buf, sizeof(buf), pFormat); + testFail("nanosecond overflow returned \"%s\"", buf); + } + catch ( ... ) { + testPass("nanosecond overflow throws"); + } + } + + { + char buf[80]; + epicsTime et; + + const char * pFormat = "%Y-%m-%d %H:%M:%S.%f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "") == 0, "undefined => '%s'", buf); + + // This is Noon GMT, when all timezones have the same date + const epicsTimeStamp tTS = {12*60*60, 98765432}; + et = tTS; + pFormat = "%Y-%m-%d %S.%09f"; // %H and %M change with timezone + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "1990-01-01 00.098765432") == 0, "'%s' => '%s'", pFormat, buf); + + pFormat = "%S.%04f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.0988") == 0, "'%s' => '%s'", pFormat, buf); + + pFormat = "%S.%05f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.09877") == 0, "'%s' => '%s'", pFormat, buf); + + pFormat = "%S.%05f %S.%05f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "00.09877 00.09877") == 0, "'%s' => '%s'", pFormat, buf); + + char smbuf[5]; + pFormat = "%S.%05f"; + et.strftime(smbuf, sizeof(smbuf), pFormat); + testOk(strcmp(smbuf, "00.*") == 0, "'%s' => '%s'", pFormat, smbuf); + + pFormat = "%%S.%%05f"; + et.strftime(buf, sizeof(buf), pFormat); + testOk(strcmp(buf, "%S.%05f") == 0, "'%s' => '%s'", pFormat, buf); + } + + { // invalidFormatTest + char bigBuf [512]; + char buf [32]; + memset(bigBuf, '\a', sizeof(bigBuf )); + bigBuf[ sizeof(bigBuf) - 1] = '\0'; + begin.strftime(buf, sizeof(buf), bigBuf); + testOk(strcmp(buf, "") == 0, "bad format => '%s'", buf); + } + + testDiag("Running %d loops", nTimes); + + for (int iTimes=0; iTimes < nTimes; ++iTimes) { + for (unsigned i=0; i= end); + + testOk1(end > begin); + testOk1(end >= begin); + testOk1(begin != end); + testOk1(begin < end); + testOk1(begin <= end); + + testOk1(end - end == 0); + testOk(fabs((end - begin) - diff) < precisionEPICS * 0.01, + "end - begin ~= diff"); + + testOk1(begin + 0 == begin); + testOk1(begin + diff == end); + testOk1(end - 0 == end); + testOk1(end - diff == begin); + + epicsTime end2 = begin; + end2 += diff; + testOk(end2 == end, "(begin += diff) == end"); + + end2 = end; + end2 -= diff; + testOk(end2 == begin, "(end -= diff) == begin"); + + // test struct tm round-trip conversion + local_tm_nano_sec ansiDate = begin; + epicsTime beginANSI = ansiDate; + testOk1(beginANSI + diff == end); + + // test struct timespec round-trip conversion + struct timespec ts = begin; + epicsTime beginTS = ts; + testOk1(beginTS + diff == end); + } + + return testDone(); +} diff --git a/src/libCom/test/epicsTimerTest.cpp b/src/libCom/test/epicsTimerTest.cpp new file mode 100644 index 000000000..8d56b84d1 --- /dev/null +++ b/src/libCom/test/epicsTimerTest.cpp @@ -0,0 +1,428 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include +#include + +#include "epicsTimer.h" +#include "epicsEvent.h" +#include "epicsAssert.h" +#include "epicsGuard.h" +#include "tsFreeList.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +static const double delayVerifyOffset = 1.0; // sec + +class delayVerify : public epicsTimerNotify { +public: + delayVerify ( double expectedDelay, epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + void setBegin ( const epicsTime & ); + double delay () const; + double checkError () const; +protected: + virtual ~delayVerify (); +private: + epicsTimer &timer; + epicsTime beginStamp; + epicsTime expireStamp; + double expectedDelay; + expireStatus expire ( const epicsTime & ); + delayVerify ( const delayVerify & ); + delayVerify & operator = ( const delayVerify & ); +}; + +static volatile unsigned expireCount; +static epicsEvent expireEvent; + +delayVerify::delayVerify ( double expectedDelayIn, epicsTimerQueue &queueIn ) : + timer ( queueIn.createTimer() ), expectedDelay ( expectedDelayIn ) +{ +} + +delayVerify::~delayVerify () +{ + this->timer.destroy (); +} + +inline void delayVerify::setBegin ( const epicsTime &beginIn ) +{ + this->beginStamp = beginIn; +} + +inline double delayVerify::delay () const +{ + return this->expectedDelay; +} + +double delayVerify::checkError () const +{ + const double messageThresh = 1.0; // percent + double actualDelay = this->expireStamp - this->beginStamp; + double measuredError = actualDelay - this->expectedDelay; + double percentError = 100.0 * fabs ( measuredError ) / this->expectedDelay; + if ( ! testOk1 ( percentError < messageThresh ) ) { + testDiag ( "delay = %f s, error = %f s (%.1f %%)", + this->expectedDelay, measuredError, percentError ); + } + return measuredError; +} + +inline void delayVerify::start ( const epicsTime &expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +epicsTimerNotify::expireStatus delayVerify::expire ( const epicsTime ¤tTime ) +{ + this->expireStamp = currentTime; + if ( --expireCount == 0u ) { + expireEvent.signal (); + } + return noRestart; +} + +// +// verify reasonable timer interval accuracy +// +void testAccuracy () +{ + static const unsigned nTimers = 25u; + delayVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + + testDiag ( "Testing timer accuracy" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMax ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new delayVerify ( i * 0.1 + delayVerifyOffset, queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + + expireCount = nTimers; + for ( i = 0u; i < nTimers; i++ ) { + epicsTime cur = epicsTime::getCurrent (); + pTimers[i]->setBegin ( cur ); + pTimers[i]->start ( cur + pTimers[i]->delay () ); + } + while ( expireCount != 0u ) { + expireEvent.wait (); + } + double averageMeasuredError = 0.0; + for ( i = 0u; i < nTimers; i++ ) { + averageMeasuredError += pTimers[i]->checkError (); + } + averageMeasuredError /= nTimers; + testDiag ("average timer delay error %f ms", + averageMeasuredError * 1000 ); + queue.release (); +} + + +class cancelVerify : public epicsTimerNotify { +public: + cancelVerify ( epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + void cancel (); + static unsigned cancelCount; + static unsigned expireCount; +protected: + virtual ~cancelVerify (); +private: + epicsTimer &timer; + expireStatus expire ( const epicsTime & ); + cancelVerify ( const cancelVerify & ); + cancelVerify & operator = ( const cancelVerify & ); +}; + +unsigned cancelVerify::cancelCount; +unsigned cancelVerify::expireCount; + +cancelVerify::cancelVerify ( epicsTimerQueue &queueIn ) : + timer ( queueIn.createTimer () ) +{ +} + +cancelVerify::~cancelVerify () +{ + this->timer.destroy (); +} + +inline void cancelVerify::start ( const epicsTime &expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +inline void cancelVerify::cancel () +{ + this->timer.cancel (); + ++cancelVerify::cancelCount; +} + +epicsTimerNotify::expireStatus cancelVerify::expire ( const epicsTime & ) +{ + ++cancelVerify::expireCount; + double root = 3.14159; + for ( unsigned i = 0u; i < 1000; i++ ) { + root = sqrt ( root ); + } + return noRestart; +} + +// +// verify that expire() won't be called after the timer is cancelled +// +void testCancel () +{ + static const unsigned nTimers = 25u; + cancelVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + + cancelVerify::cancelCount = 0; + cancelVerify::expireCount = 0; + + testDiag ( "Testing timer cancellation" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new cancelVerify ( queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + if ( ! testOk1 ( cancelVerify::expireCount == 0 ) ) + testDiag ( "expireCount = %u", cancelVerify::expireCount ); + if ( ! testOk1 ( cancelVerify::cancelCount == 0 ) ) + testDiag ( "cancelCount = %u", cancelVerify::cancelCount ); + + testDiag ( "starting %d timers", nTimers ); + epicsTime exp = epicsTime::getCurrent () + 4.0; + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->start ( exp ); + } + testOk1 ( cancelVerify::expireCount == 0 ); + testOk1 ( cancelVerify::cancelCount == 0 ); + + testDiag ( "cancelling timers" ); + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->cancel (); + } + testOk1 ( cancelVerify::expireCount == 0 ); + testOk1 ( cancelVerify::cancelCount == nTimers ); + + testDiag ( "waiting until after timers should have expired" ); + epicsThreadSleep ( 5.0 ); + testOk1 ( cancelVerify::expireCount == 0 ); + testOk1 ( cancelVerify::cancelCount == nTimers ); + + epicsThreadSleep ( 1.0 ); + queue.release (); +} + + +class expireDestroyVerify : public epicsTimerNotify { +public: + expireDestroyVerify ( epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + static unsigned destroyCount; +protected: + virtual ~expireDestroyVerify (); +private: + epicsTimer & timer; + expireStatus expire ( const epicsTime & ); + expireDestroyVerify ( const expireDestroyVerify & ); + expireDestroyVerify & operator = ( const expireDestroyVerify & ); +}; + +unsigned expireDestroyVerify::destroyCount; + +expireDestroyVerify::expireDestroyVerify ( epicsTimerQueue & queueIn ) : + timer ( queueIn.createTimer () ) +{ +} + +expireDestroyVerify::~expireDestroyVerify () +{ + this->timer.destroy (); + ++expireDestroyVerify::destroyCount; +} + +inline void expireDestroyVerify::start ( const epicsTime & expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +epicsTimerNotify::expireStatus expireDestroyVerify::expire ( const epicsTime & ) +{ + delete this; + return noRestart; +} + +// +// verify that a timer can be destroyed in expire +// +void testExpireDestroy () +{ + static const unsigned nTimers = 25u; + expireDestroyVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + expireDestroyVerify::destroyCount = 0; + + testDiag ( "Testing timer destruction in expire()" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new expireDestroyVerify ( queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + testOk1 ( expireDestroyVerify::destroyCount == 0 ); + + testDiag ( "starting %d timers", nTimers ); + epicsTime cur = epicsTime::getCurrent (); + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->start ( cur ); + } + + testDiag ( "waiting until all timers should have expired" ); + epicsThreadSleep ( 5.0 ); + + testOk1 ( expireDestroyVerify::destroyCount == nTimers ); + queue.release (); +} + + +class periodicVerify : public epicsTimerNotify { +public: + periodicVerify ( epicsTimerQueue & ); + void start ( const epicsTime &expireTime ); + void cancel (); + bool verifyCount (); +protected: + virtual ~periodicVerify (); +private: + epicsTimer &timer; + unsigned nExpire; + bool cancelCalled; + expireStatus expire ( const epicsTime & ); + periodicVerify ( const periodicVerify & ); + periodicVerify & operator = ( const periodicVerify & ); +}; + +periodicVerify::periodicVerify ( epicsTimerQueue & queueIn ) : + timer ( queueIn.createTimer () ), nExpire ( 0u ), + cancelCalled ( false ) +{ +} + +periodicVerify::~periodicVerify () +{ + this->timer.destroy (); +} + +inline void periodicVerify::start ( const epicsTime &expireTime ) +{ + this->timer.start ( *this, expireTime ); +} + +inline void periodicVerify::cancel () +{ + this->timer.cancel (); + this->cancelCalled = true; +} + +inline bool periodicVerify::verifyCount () +{ + return ( this->nExpire > 1u ); +} + +epicsTimerNotify::expireStatus periodicVerify::expire ( const epicsTime & ) +{ + this->nExpire++; + double root = 3.14159; + for ( unsigned i = 0u; i < 1000; i++ ) { + root = sqrt ( root ); + } + verify ( ! this->cancelCalled ); + double delay = rand (); + delay = delay / RAND_MAX; + delay /= 10.0; + return expireStatus ( restart, delay ); +} + +// +// verify periodic timers +// +void testPeriodic () +{ + static const unsigned nTimers = 25u; + periodicVerify *pTimers[nTimers]; + unsigned i; + unsigned timerCount = 0; + + testDiag ( "Testing periodic timers" ); + + epicsTimerQueueActive &queue = + epicsTimerQueueActive::allocate ( true, epicsThreadPriorityMin ); + + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i] = new periodicVerify ( queue ); + timerCount += pTimers[i] ? 1 : 0; + } + testOk1 ( timerCount == nTimers ); + + testDiag ( "starting %d timers", nTimers ); + epicsTime cur = epicsTime::getCurrent (); + for ( i = 0u; i < nTimers; i++ ) { + pTimers[i]->start ( cur ); + } + + testDiag ( "waiting until all timers should have expired" ); + epicsThreadSleep ( 5.0 ); + + bool notWorking = false; + for ( i = 0u; i < nTimers; i++ ) { + notWorking |= ! pTimers[i]->verifyCount (); + pTimers[i]->cancel (); + } + testOk( ! notWorking, "All timers expiring" ); + epicsThreadSleep ( 1.0 ); + queue.release (); +} + +MAIN(epicsTimerTest) +{ + testPlan(40); + testAccuracy (); + testCancel (); + testExpireDestroy (); + testPeriodic (); + return testDone(); +} diff --git a/src/libCom/test/epicsUnitTestTest.c b/src/libCom/test/epicsUnitTestTest.c new file mode 100644 index 000000000..8e27fef61 --- /dev/null +++ b/src/libCom/test/epicsUnitTestTest.c @@ -0,0 +1,36 @@ +/*************************************************************************\ +* Copyright (c) 2006 The University of Chicago, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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: Andrew Johnson + * + * Test for the unit test module... + */ + +#include "epicsUnitTest.h" + +#define testOk1_success 1 +#define testOk1_failure 0 + +int main (void) { + testPlan(11); + testOk(1, "testOk(1)"); + testOk(0, "testOk(0)"); + testPass("testPass()"); + testFail("testFail()"); + testSkip(2, "Skipping two"); + testTodoBegin("Testing Todo"); + testOk(1, "Todo pass"); + testOk(0, "Todo fail"); + testSkip(1, "Todo skip"); + testTodoEnd(); + testOk1(testOk1_success); + testOk1(testOk1_failure); + testDiag("Diagnostic"); +/* testAbort("testAbort"); */ + return testDone(); +} diff --git a/src/libCom/test/epicsUnitTestTest.plt b/src/libCom/test/epicsUnitTestTest.plt new file mode 100644 index 000000000..9b8f3e1c9 --- /dev/null +++ b/src/libCom/test/epicsUnitTestTest.plt @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +use strict; +use Test; + +BEGIN {plan tests => 1} + +my $prog = "./$0"; +$prog =~ s/\.t$//; + +my $expected = + "1..11\n" . + "ok 1 - testOk(1)\n" . + "not ok 2 - testOk(0)\n" . + "ok 3 - testPass()\n" . + "not ok 4 - testFail()\n" . + "ok 5 # SKIP Skipping two\n" . + "ok 6 # SKIP Skipping two\n" . + "ok 7 - Todo pass # TODO Testing Todo\n" . + "not ok 8 - Todo fail # TODO Testing Todo\n" . + "ok 9 # SKIP Todo skip\n" . + "ok 10 - testOk1_success\n" . + "not ok 11 - testOk1_failure\n" . + "# Diagnostic\n"; + +$ENV{HARNESS_ACTIVE} = 1; +my $result = `$prog`; + +ok($result, $expected); # test output matches diff --git a/src/libCom/test/fdmgrTest.c b/src/libCom/test/fdmgrTest.c new file mode 100644 index 000000000..86857c6f5 --- /dev/null +++ b/src/libCom/test/fdmgrTest.c @@ -0,0 +1,138 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include "fdmgr.h" +#include "epicsTime.h" +#include "epicsAssert.h" +#include "cadef.h" + +#define verify(exp) ((exp) ? (void)0 : \ + epicsAssert(__FILE__, __LINE__, #exp, epicsAssertAuthor)) + +static const unsigned uSecPerSec = 1000000; + +typedef struct cbStructCreateDestroyFD { + fdctx *pfdm; + int trig; +} cbStructCreateDestroyFD; + +void fdHandler (void *pArg) +{ + cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg; + + printf ("triggered\n"); + pCBFD->trig = 1; +} + +void fdCreateDestroyHandler (void *pArg, int fd, int open) +{ + cbStructCreateDestroyFD *pCBFD = (cbStructCreateDestroyFD *) pArg; + int status; + + if (open) { + printf ("new fd = %d\n", fd); + status = fdmgr_add_callback (pCBFD->pfdm, fd, fdi_read, fdHandler, pArg); + verify (status==0); + } + else { + printf ("terminated fd = %d\n", fd); + status = fdmgr_clear_callback (pCBFD->pfdm, fd, fdi_read); + verify (status==0); + } +} + +typedef struct cbStuctTimer { + epicsTimeStamp time; + int done; +} cbStruct; + +void alarmCB (void *parg) +{ + cbStruct *pCBS = (cbStruct *) parg; + epicsTimeGetCurrent (&pCBS->time); + pCBS->done = 1; +} + +void testTimer (fdctx *pfdm, double delay) +{ + int status; + fdmgrAlarmId aid; + struct timeval tmo; + epicsTimeStamp begin; + cbStruct cbs; + double measuredDelay; + double measuredError; + + epicsTimeGetCurrent (&begin); + cbs.done = 0; + tmo.tv_sec = (unsigned long) delay; + tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec); + aid = fdmgr_add_timeout (pfdm, &tmo, alarmCB, &cbs); + verify (aid!=fdmgrNoAlarm); + + while (!cbs.done) { + tmo.tv_sec = (unsigned long) delay; + tmo.tv_usec = (unsigned long) ((delay - tmo.tv_sec) * uSecPerSec); + status = fdmgr_pend_event (pfdm, &tmo); + verify (status==0); + } + + measuredDelay = epicsTimeDiffInSeconds (&cbs.time, &begin); + measuredError = fabs (measuredDelay-delay); + printf ("measured delay for %lf sec was off by %lf sec (%lf %%)\n", + delay, measuredError, 100.0*measuredError/delay); +} + +int main (int argc, char **argv) +{ + int status; + fdctx *pfdm; + cbStructCreateDestroyFD cbsfd; + struct timeval tmo; + chid chan; + + pfdm = fdmgr_init (); + verify (pfdm); + + SEVCHK (ca_task_initialize(), NULL); + cbsfd.pfdm = pfdm; + SEVCHK (ca_add_fd_registration (fdCreateDestroyHandler, &cbsfd), NULL); + + /* + * timer test + */ + testTimer (pfdm, 0.001); + testTimer (pfdm, 0.01); + testTimer (pfdm, 0.1); + testTimer (pfdm, 1.0); + + if (argc==2) { + SEVCHK(ca_search (argv[1], &chan), NULL); + } + + while (1) { + tmo.tv_sec = 0; + tmo.tv_usec = 100000; + cbsfd.trig = 0; + status = fdmgr_pend_event (pfdm, &tmo); + verify (status==0); + ca_poll (); + } + + status = fdmgr_delete (pfdm); + verify (status==0); + + printf ( "Test Complete\n" ); + + return 0; +} + diff --git a/src/libCom/test/macEnvExpandTest.c b/src/libCom/test/macEnvExpandTest.c new file mode 100644 index 000000000..735cd81a5 --- /dev/null +++ b/src/libCom/test/macEnvExpandTest.c @@ -0,0 +1,163 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is 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 + */ + +#include +#include +#include +#include + +#include "macLib.h" +#include "envDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +int warn; + +static void check(const char *str, const char *expect) +{ + char *got = macEnvExpand(str); + int pass = -1; + + if (expect == NULL) ++warn; + + if (expect && !got) { + testDiag("Got NULL, expected \"%s\".\n", expect); + pass = 0; + } + else if (!expect && got) { + testDiag("Got \"%s\", expected NULL.\n", got); + pass = 0; + } + else if (expect && got && strcmp(got, expect)) { + testDiag("Got \"%s\", expected \"%s\".\n", got, expect); + pass = 0; + } + testOk(pass, str); +} + +MAIN(macEnvExpandTest) +{ + warn = 0; + testPlan(71); + + check("FOO", "FOO"); + + check("${FOO}", NULL); + check("${FOO,BAR}", NULL); + check("${FOO,BAR=baz}", NULL); + check("${FOO,BAR=$(FOO)}", NULL); + check("${FOO,FOO}", NULL); + check("${FOO,FOO=$(FOO)}", NULL); + check("${FOO,BAR=baz,FUM}", NULL); + + check("${=}", ""); + check("x${=}y", "xy"); + + check("${,=}", ""); + check("x${,=}y", "xy"); + + check("${FOO=}", ""); + check("x${FOO=}y", "xy"); + + check("${FOO=,}", ""); + check("x${FOO=,}y", "xy"); + + check("${FOO,FOO=}", ""); + check("x${FOO,FOO=}y", "xy"); + + check("${FOO=,BAR}", ""); + check("x${FOO=,BAR}y", "xy"); + + check("${FOO=$(BAR=)}", ""); + check("x${FOO=$(BAR=)}y", "xy"); + + check("${FOO=,BAR=baz}", ""); + check("x${FOO=,BAR=baz}y", "xy"); + + check("${FOO=$(BAR),BAR=}", ""); + check("x${FOO=$(BAR),BAR=}y", "xy"); + + check("${=BAR}", "BAR"); + check("x${=BAR}y", "xBARy"); + + check("${FOO=BAR}", "BAR"); + check("x${FOO=BAR}y", "xBARy"); + + epicsEnvSet("FOO","BLETCH"); + check("${FOO}", "BLETCH"); + check("${FOO,FOO}", "BLETCH"); + check("x${FOO}y", "xBLETCHy"); + check("x${FOO}y${FOO}z", "xBLETCHyBLETCHz"); + check("${FOO=BAR}", "BLETCH"); + check("x${FOO=BAR}y", "xBLETCHy"); + check("${FOO=${BAZ}}", "BLETCH"); + check("${FOO=${BAZ},BAR=$(BAZ)}", "BLETCH"); + check("x${FOO=${BAZ}}y", "xBLETCHy"); + check("x${FOO=${BAZ},BAR=$(BAZ)}y", "xBLETCHy"); + check("${BAR=${FOO}}", "BLETCH"); + check("x${BAR=${FOO}}y", "xBLETCHy"); + check("w${BAR=x${FOO}y}z", "wxBLETCHyz"); + + check("${FOO,FOO=BAR}", "BAR"); + check("x${FOO,FOO=BAR}y", "xBARy"); + check("${BAR,BAR=$(FOO)}", "BLETCH"); + check("x${BAR,BAR=$(FOO)}y", "xBLETCHy"); + check("${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}", "GRIBBLE"); + check("x${BAR,BAR=$($(FOO)),BLETCH=GRIBBLE}y", "xGRIBBLEy"); + check("${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}", "GRIBBLE"); + check("x${$(BAR,BAR=$(FOO)),BLETCH=GRIBBLE}y", "xGRIBBLEy"); + + epicsEnvSet("BAR","GLEEP"); + check("${FOO}/${BAR}", "BLETCH/GLEEP"); + check("x${FOO}/${BAR}y", "xBLETCH/GLEEPy"); + check("${FOO,BAR}/${BAR}", "BLETCH/GLEEP"); + check("${FOO,BAR=x}/${BAR}", "BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR}/${BAR}", "BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR=x}/${BAR}", "BLETCH/GLEEP"); + + epicsEnvSet("BLETCH","BAR"); + check("${${FOO}}", "BAR"); + check("x${${FOO}}y", "xBARy"); + check("${${FOO}=GRIBBLE}", "BAR"); + check("x${${FOO}=GRIBBLE}y", "xBARy"); + + epicsEnvSet("BLETCH","${BAR}"); + check("${${FOO}}", "GLEEP"); + + epicsEnvSet("FOO","${BAR}"); + check("${FOO}","GLEEP"); + + epicsEnvSet("BAR","${BAZ}"); + check("${FOO}", NULL); + + epicsEnvSet("BAR","${BAZ=GRIBBLE}"); + check("${FOO}", "GRIBBLE"); + + epicsEnvSet("BAR","${STR1}"); + epicsEnvSet("STR1","VAL1"); + epicsEnvSet("STR2","VAL2"); + check("${FOO}", "VAL1"); + + epicsEnvSet("BAR","${STR2}"); + check("${FOO}", "VAL2"); + + epicsEnvSet("BAR","${FOO}"); + check("${FOO}", NULL); + check("${FOO,FOO=$(FOO)}", NULL); + check("${FOO=$(FOO)}", NULL); + check("${FOO=$(BAR),BAR=$(FOO)}", NULL); + + errlogFlush(); + testDiag("%d warning messages from macLib were expected above.\n", warn); + return testDone(); +} diff --git a/src/libCom/test/macLibTest.c b/src/libCom/test/macLibTest.c new file mode 100644 index 000000000..8f41423e0 --- /dev/null +++ b/src/libCom/test/macLibTest.c @@ -0,0 +1,221 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is 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 + */ + +#include +#include +#include +#include + +#include "macLib.h" +#include "dbDefs.h" +#include "envDefs.h" +#include "errlog.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +MAC_HANDLE *h; + +static void check(const char *str, const char *expect) +{ + char output[MAC_SIZE] = {'\0'}; + long status = macExpandString(h, str, output, MAC_SIZE); + long expect_len = strlen(expect+1); + int expect_error = (expect[0] == '!'); + int statBad = expect_error ^ (status < 0); + int strBad = strcmp(output, expect+1); + + testOk(!statBad && !strBad, "%s => %s", str, output); + + if (strBad) { + testDiag("Got \"%s\", expected \"%s\"", output, expect+1); + } + if (statBad) { + testDiag("Return status was %ld, expected %ld", + status, expect_error ? -expect_len : expect_len); + } +} + +static void ovcheck(void) +{ + char output[54]; + long status; + + macPutValue(h, "OVVAR","abcdefghijklmnopqrstuvwxyz"); + + memset(output, '~', sizeof output); + status = macExpandString(h, "abcdefghijklmnopqrstuvwxyz$(OVVAR)", output, 52); + testOk(status == 51, "expansion returned %ld, expected 51", status); + testOk(output[50] == 'y', "final character %x, expect 79 (y)", output[50]); + testOk(output[51] == '\0', "terminator character %x, expect 0", output[51]); + testOk(output[52] == '~', "sentinel character %x, expect 7e, (~)", output[52]); + + memset(output, '~', sizeof output); + status = macExpandString(h, "abcdefghijklmnopqrstuvwxyz$(OVVAR)", output, 53); + testOk(status == 52, "expansion returned %ld, expected 52", status); + testOk(output[51] == 'z', "final character %x, expect 7a (z)", output[51]); + testOk(output[52] == '\0', "terminator character %x, expect 0", output[52]); + testOk(output[53] == '~', "sentinel character %x, expect 7e, (~)", output[53]); +} + +MAIN(macLibTest) +{ + testPlan(89); + + if (macCreateHandle(&h, NULL)) + testAbort("macCreateHandle() failed"); + macSuppressWarning(h, TRUE); + + check("FOO", " FOO"); + + check("$(FOO)", "!$(FOO,undefined)"); + check("${FOO}", "!$(FOO,undefined)"); + check("${FOO=${FOO}}", "!$(FOO,undefined)"); + check("${FOO,FOO}", "!$(FOO,undefined)"); + check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)"); + check("${FOO,BAR}", "!$(FOO,undefined)"); + check("${FOO,BAR=baz}", "!$(FOO,undefined)"); + check("${FOO,BAR=${FOO}}", "!$(FOO,undefined)"); + check("${FOO,BAR=baz,FUM}", "!$(FOO,undefined)"); + check("${FOO=${BAR},BAR=${FOO}}", "!$(FOO,undefined)"); + check("${FOO,FOO=${BAR},BAR=${FOO}}", "!$(BAR,recursive)"); + + check("${=}", " "); + check("x${=}y", " xy"); + + check("${,=}", " "); + check("x${,=}y", " xy"); + + check("${FOO=}", " "); + check("x${FOO=}y", " xy"); + + check("${FOO=,}", " "); + check("x${FOO=,}y", " xy"); + + check("${FOO,FOO=}", " "); + check("x${FOO,FOO=}y", " xy"); + + check("${FOO=,BAR}", " "); + check("x${FOO=,BAR}y", " xy"); + + check("${FOO=${BAR=}}", " "); + check("x${FOO=${BAR=}}y", " xy"); + + check("${FOO=,BAR=baz}", " "); + check("x${FOO=,BAR=baz}y", " xy"); + + check("${FOO=${BAR},BAR=}", " "); + check("x${FOO=${BAR},BAR=}y", " xy"); + + check("${=BAR}", " BAR"); + check("x${=BAR}y", " xBARy"); + + check("${FOO=BAR}", " BAR"); + check("x${FOO=BAR}y", " xBARy"); + + macPutValue(h, "FOO", "BLETCH"); + /* FOO = "BLETCH" */ + check("${FOO}", " BLETCH"); + check("${FOO,FOO}", " BLETCH"); + check("x${FOO}y", " xBLETCHy"); + check("x${FOO}y${FOO}z", " xBLETCHyBLETCHz"); + check("${FOO=BAR}", " BLETCH"); + check("x${FOO=BAR}y", " xBLETCHy"); + check("${FOO=${BAZ}}", " BLETCH"); + check("${FOO=${BAZ},BAR=${BAZ}}", " BLETCH"); + check("x${FOO=${BAZ}}y", " xBLETCHy"); + check("x${FOO=${BAZ},BAR=${BAZ}}y", " xBLETCHy"); + check("${BAR=${FOO}}", " BLETCH"); + check("x${BAR=${FOO}}y", " xBLETCHy"); + check("w${BAR=x${FOO}y}z", " wxBLETCHyz"); + + check("${FOO,FOO=BAR}", " BAR"); + check("x${FOO,FOO=BAR}y", " xBARy"); + check("${BAR,BAR=${FOO}}", " BLETCH"); + check("x${BAR,BAR=${FOO}}y", " xBLETCHy"); + check("${BAR,BAR=${${FOO}},BLETCH=GRIBBLE}", " GRIBBLE"); + check("x${BAR,BAR=${${FOO}},BLETCH=GRIBBLE}y", " xGRIBBLEy"); + check("${${BAR,BAR=${FOO}},BLETCH=GRIBBLE}", " GRIBBLE"); + check("x${${BAR,BAR=${FOO}},BLETCH=GRIBBLE}y", " xGRIBBLEy"); + check("${N=${FOO}/${BAR},BAR=GLEEP}", " BLETCH/GLEEP"); + + macPutValue(h, "BAR","GLEEP"); + /* FOO = "BLETCH" */ + /* BAR = "GLEEP" */ + check("${FOO}/${BAR}", " BLETCH/GLEEP"); + check("x${FOO}/${BAR}y", " xBLETCH/GLEEPy"); + check("${FOO,BAR}/${BAR}", " BLETCH/GLEEP"); + check("${FOO,BAR=x}/${BAR}", " BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR}/${BAR}", " BLETCH/GLEEP"); + check("${BAZ=BLETCH,BAR=x}/${BAR}", " BLETCH/GLEEP"); + check("${N=${FOO}/${BAR}}", " BLETCH/GLEEP"); + + macPutValue(h, "BLETCH","BAR"); + /* FOO = "BLETCH" */ + /* BLETCH = "BAR" */ + check("${${FOO}}", " BAR"); + check("x${${FOO}}y", " xBARy"); + check("${${FOO}=GRIBBLE}", " BAR"); + check("x${${FOO}=GRIBBLE}y", " xBARy"); + + macPutValue(h, "BLETCH","${BAR}"); + /* FOO = "BLETCH" */ + /* BLETCH = "${BAR}" */ + /* BAR = "GLEEP" */ + check("${${FOO}}", " GLEEP"); + check("${BLETCH=${FOO}}", " GLEEP"); + + macPutValue(h, "FOO","${BAR}"); + /* FOO = "${BAR}" */ + /* BAR = "GLEEP" */ + check("${FOO}", " GLEEP"); + check("${FOO=BLETCH,BAR=BAZ}", " BAZ"); + + macPutValue(h, "BAR","${BAZ}"); + /* FOO = "${BAR}" */ + /* BAR = "${BAZ}" */ + check("${FOO}", "!$(BAZ,undefined)"); + + macPutValue(h, "BAR","${BAZ=GRIBBLE}"); + /* FOO = "${BAR}" */ + /* BAR = "${BAZ=GRIBBLE}" */ + check("${FOO}", " GRIBBLE"); + check("${FOO,BAZ=GEEK}", " GEEK"); + + macPutValue(h, "BAR","${STR1}"); + macPutValue(h, "STR1","VAL1"); + macPutValue(h, "STR2","VAL2"); + /* FOO = "${BAR}" */ + /* BAR = "${STR1}" */ + /* STR1 = "VAL1" */ + /* STR2 = "VAL2" */ + check("${FOO}", " VAL1"); + + macPutValue(h, "BAR","${STR2}"); + /* FOO = "${BAR}" */ + /* BAR = "${STR2}" */ + /* STR1 = "VAL1" */ + /* STR2 = "VAL2" */ + check("${FOO}", " VAL2"); + + macPutValue(h, "BAR","${FOO}"); + /* FOO = "${BAR}" */ + /* BAR = "${FOO}" */ + check("${FOO}", "!$(BAR,recursive)"); + check("${FOO=GRIBBLE}", "!$(BAR,recursive)"); + check("${FOO=GRIBBLE,BAR=${FOO}}", "!$(BAR,recursive)"); + check("${FOO,FOO=${FOO}}", "!$(FOO,recursive)"); + check("${FOO=GRIBBLE,FOO=${FOO}}", "!$(FOO,recursive)"); + + ovcheck(); + + return testDone(); +} diff --git a/src/libCom/test/ringBytesTest.c b/src/libCom/test/ringBytesTest.c new file mode 100644 index 000000000..ecfd991be --- /dev/null +++ b/src/libCom/test/ringBytesTest.c @@ -0,0 +1,119 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ringBytesTest.c */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsRingBytes.h" +#include "errlog.h" +#include "epicsEvent.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define RINGSIZE 10 + +typedef struct info { + epicsEventId consumerEvent; + epicsRingBytesId ring; +}info; + +static void check(epicsRingBytesId ring, int expectedFree) +{ + int expectedUsed = RINGSIZE - expectedFree; + int expectedEmpty = (expectedUsed == 0); + int expectedFull = (expectedFree == 0); + int nFree = epicsRingBytesFreeBytes(ring); + int nUsed = epicsRingBytesUsedBytes(ring); + int isEmpty = epicsRingBytesIsEmpty(ring); + int isFull = epicsRingBytesIsFull(ring); + + testOk(nFree == expectedFree, "Free: %d == %d", nFree, expectedFree); + testOk(nUsed == expectedUsed, "Used: %d == %d", nUsed, expectedUsed); + testOk(isEmpty == expectedEmpty, "Empty: %d == %d", isEmpty, expectedEmpty); + testOk(isFull == expectedFull, "Full: %d == %d", isFull, expectedFull); +} + +MAIN(ringBytesTest) +{ + int i, n; + info *pinfo; + epicsEventId consumerEvent; + char put[RINGSIZE+1]; + char get[RINGSIZE+1]; + epicsRingBytesId ring; + + testPlan(245); + + pinfo = calloc(1,sizeof(info)); + if (!pinfo) { + testAbort("calloc failed"); + } + pinfo->consumerEvent = consumerEvent = epicsEventCreate(epicsEventEmpty); + if (!consumerEvent) { + testAbort("epicsEventCreate failed"); + } + + pinfo->ring = ring = epicsRingBytesCreate(RINGSIZE); + if (!ring) { + testAbort("epicsRingBytesCreate failed"); + } + check(ring, RINGSIZE); + + for (i = 0 ; i < sizeof(put) ; i++) + put[i] = i; + for(i = 0 ; i < RINGSIZE ; i++) { + n = epicsRingBytesPut(ring, put, i); + testOk(n==i, "ring put %d", i); + check(ring, RINGSIZE-i); + n = epicsRingBytesGet(ring, get, i); + testOk(n==i, "ring get %d", i); + check(ring, RINGSIZE); + testOk(memcmp(put,get,i)==0, "get matches write"); + } + + for(i = 0 ; i < RINGSIZE ; i++) { + n = epicsRingBytesPut(ring, put+i, 1); + testOk(n==1, "ring put 1, %d", i); + check(ring, RINGSIZE-1-i); + } + n = epicsRingBytesPut(ring, put+RINGSIZE, 1); + testOk(n==0, "put to full ring"); + check(ring, 0); + for(i = 0 ; i < RINGSIZE ; i++) { + n = epicsRingBytesGet(ring, get+i, 1); + testOk(n==1, "ring get 1, %d", i); + check(ring, 1+i); + } + testOk(memcmp(put,get,RINGSIZE)==0, "get matches write"); + n = epicsRingBytesGet(ring, get+RINGSIZE, 1); + testOk(n==0, "get from empty ring"); + check(ring, RINGSIZE); + + n = epicsRingBytesPut(ring, put, RINGSIZE+1); + testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); + check(ring, RINGSIZE); + n = epicsRingBytesPut(ring, put, 1); + testOk(n==1, "ring put %d", 1); + check(ring, RINGSIZE-1); + n = epicsRingBytesPut(ring, put, RINGSIZE); + testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); + check(ring, RINGSIZE-1); + n = epicsRingBytesGet(ring, get, 1); + testOk(n==1, "ring get %d", 1); + check(ring, RINGSIZE); + + return testDone(); +} diff --git a/src/libCom/test/ringPointerTest.c b/src/libCom/test/ringPointerTest.c new file mode 100644 index 000000000..fd3171045 --- /dev/null +++ b/src/libCom/test/ringPointerTest.c @@ -0,0 +1,110 @@ +/*************************************************************************\ +* Copyright (c) 2006 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ringPointerTest.c */ + +/* Author: Marty Kraimer Date: 13OCT2000 */ + +#include +#include +#include +#include +#include +#include +#include + +#include "epicsThread.h" +#include "epicsRingPointer.h" +#include "errlog.h" +#include "epicsEvent.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define ringSize 10 + +static volatile int testExit = 0; + +typedef struct info { + epicsEventId consumerEvent; + epicsRingPointerId ring; +}info; + +static void consumer(void *arg) +{ + info *pinfo = (info *)arg; + static int expectedValue=0; + int *newvalue; + + testDiag("Consumer starting"); + while(1) { + epicsEventMustWait(pinfo->consumerEvent); + if (testExit) return; + while((newvalue = (int *)epicsRingPointerPop(pinfo->ring))) { + testOk(expectedValue == *newvalue, + "Consumer: %d == %d", expectedValue, *newvalue); + expectedValue = *newvalue + 1; + } + } +} + +MAIN(ringPointerTest) +{ + int i; + info *pinfo; + epicsEventId consumerEvent; + int value[ringSize*2]; + int *pgetValue; + epicsRingPointerId ring; + epicsThreadId tid; + + testPlan(54); + + for (i=0; iconsumerEvent = consumerEvent = epicsEventMustCreate(epicsEventEmpty); + if (!consumerEvent) { + testAbort("epicsEventMustCreate failed"); + } + + pinfo->ring = ring = epicsRingPointerCreate(ringSize); + if (!ring) { + testAbort("epicsRingPointerCreate failed"); + } + testOk(epicsRingPointerIsEmpty(ring), "Ring empty"); + + for (i=0; epicsRingPointerPush(ring,(void *)&value[i]); ++i) {} + testOk(i==ringSize, "ring filled, %d values", i); + + for (i=0; (pgetValue = (int *)epicsRingPointerPop(ring)); ++i) { + testOk(i==*pgetValue, "Pop test: %d == %d", i, *pgetValue); + } + testOk(epicsRingPointerIsEmpty(ring), "Ring empty"); + + tid=epicsThreadCreate("consumer", 50, + epicsThreadGetStackSize(epicsThreadStackSmall), consumer, pinfo); + if(!tid) testAbort("epicsThreadCreate failed"); + epicsThreadSleep(0.1); + + for (i=0; i + +#include "taskwd.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + + +void monInsert(void *usr, epicsThreadId tid) +{ + testPass("monInsert(tid=%p)", (void *)tid); +} + +void monNotify(void *usr, epicsThreadId tid, int suspended) +{ + testPass("monNotify(tid=%p, suspended=%d)", (void *)tid, suspended); + epicsThreadResume(tid); +} + +void monRemove(void *usr, epicsThreadId tid) +{ + testPass("monRemove(tid=%p)", (void *)tid); +} + +taskwdMonitor monFuncs = {monInsert, monNotify, monRemove}; + +void anyNotify(void *usr, epicsThreadId tid) +{ + testPass("anyNotify(tid=%p)", (void *)tid); +} + +void taskNotify(void *usr) +{ + testPass("taskNotify"); +} + +void testTask1(void *arg) +{ + taskwdInsert(0, taskNotify, NULL); + epicsThreadSleep(10.0); + taskwdRemove(0); +} + +void testTask2(void *arg) +{ + taskwdInsert(0, taskNotify, NULL); + testDiag("Task suspending"); + epicsThreadSuspendSelf(); + epicsThreadSleep(1.0); + testDiag("Alive again"); + epicsThreadSleep(10.0); + taskwdRemove(0); +} + +MAIN(taskwdTest) +{ + testPlan(8); + + taskwdInit(); + taskwdMonitorAdd(&monFuncs, NULL); + taskwdAnyInsert(NULL, anyNotify, NULL); + + epicsThreadCreate("testTask1", epicsThreadPriorityMax, + epicsThreadGetStackSize(epicsThreadStackSmall), + testTask1, NULL); + epicsThreadSleep(1.0); + + epicsThreadCreate("testTask2", epicsThreadPriorityMax, + epicsThreadGetStackSize(epicsThreadStackSmall), + testTask2, NULL); + + /* taskwd checks tasks every 6 seconds */ + epicsThreadSleep(18.0); + + taskwdMonitorDel(&monFuncs, NULL); + taskwdAnyRemove(NULL); + + return testDone(); +} + diff --git a/src/libCom/timer/epicsTimer.cpp b/src/libCom/timer/epicsTimer.cpp new file mode 100644 index 000000000..fd84d20da --- /dev/null +++ b/src/libCom/timer/epicsTimer.cpp @@ -0,0 +1,279 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#include "epicsMath.h" + +#define epicsExportSharedSymbols +#include "epicsTimer.h" +#include "epicsGuard.h" +#include "timerPrivate.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +template class tsFreeList < epicsTimerForC, 0x20 >; + +epicsTimer::~epicsTimer () {} + +epicsTimerQueueNotify::~epicsTimerQueueNotify () {} + +epicsTimerNotify::~epicsTimerNotify () {} + +void epicsTimerNotify::show ( unsigned /* level */ ) const {} + +epicsTimerForC::epicsTimerForC ( timerQueue &queue, epicsTimerCallback pCBIn, void *pPrivateIn ) : + timer ( queue ), pCallBack ( pCBIn ), pPrivate ( pPrivateIn ) +{ +} + +epicsTimerForC::~epicsTimerForC () +{ +} + +void epicsTimerForC::destroy () +{ + timerQueue & queueTmp = this->queue; + this->~epicsTimerForC (); + queueTmp.timerForCFreeList.release ( this ); +} + +epicsTimerNotify::expireStatus epicsTimerForC::expire ( const epicsTime & ) +{ + ( *this->pCallBack ) ( this->pPrivate ); + return noRestart; +} + +epicsTimerQueueActiveForC :: + epicsTimerQueueActiveForC ( RefMgr & refMgr, + bool okToShare, unsigned priority ) : + timerQueueActive ( refMgr, okToShare, priority ) +{ +} + +epicsTimerQueueActiveForC::~epicsTimerQueueActiveForC () +{ +} + +void epicsTimerQueueActiveForC::release () +{ + _refMgr->release ( *this ); +} + +epicsTimerQueuePassiveForC::epicsTimerQueuePassiveForC ( + epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, + epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, + void * pPrivateIn ) : + timerQueuePassive ( * static_cast < epicsTimerQueueNotify * > ( this ) ), + pRescheduleCallback ( pRescheduleCallbackIn ), + pSleepQuantumCallback ( pSleepQuantumCallbackIn ), + pPrivate ( pPrivateIn ) +{ +} + +epicsTimerQueuePassiveForC::~epicsTimerQueuePassiveForC () +{ +} + +void epicsTimerQueuePassiveForC::reschedule () +{ + (*this->pRescheduleCallback) ( this->pPrivate ); +} + +double epicsTimerQueuePassiveForC::quantum () +{ + return (*this->pSleepQuantumCallback) ( this->pPrivate ); +} + +void epicsTimerQueuePassiveForC::destroy () +{ + delete this; +} + +epicsShareFunc epicsTimerNotify::expireStatus::expireStatus ( restart_t restart ) : + delay ( - DBL_MAX ) +{ + if ( restart != noRestart ) { + throw std::logic_error + ( "timer restart was requested without specifying a delay?" ); + } +} + +epicsShareFunc epicsTimerNotify::expireStatus::expireStatus + ( restart_t restartIn, const double & expireDelaySec ) : + delay ( expireDelaySec ) +{ + if ( restartIn != epicsTimerNotify::restart ) { + throw std::logic_error + ( "no timer restart was requested, but a delay was specified?" ); + } + if ( this->delay < 0.0 || !finite(this->delay) ) { + throw std::logic_error + ( "timer restart was requested, but a negative delay was specified?" ); + } +} + +epicsShareFunc bool epicsTimerNotify::expireStatus::restart () const +{ + return this->delay >= 0.0 && finite(this->delay); +} + +epicsShareFunc double epicsTimerNotify::expireStatus::expirationDelay () const +{ + if ( this->delay < 0.0 || !finite(this->delay) ) { + throw std::logic_error + ( "no timer restart was requested, but you are asking for a restart delay?" ); + } + return this->delay; +} + +extern "C" epicsTimerQueuePassiveId epicsShareAPI + epicsTimerQueuePassiveCreate ( + epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, + epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, + void * pPrivateIn ) +{ + try { + return new epicsTimerQueuePassiveForC ( + pRescheduleCallbackIn, + pSleepQuantumCallbackIn, + pPrivateIn ); + } + catch ( ... ) { + return 0; + } +} + +extern "C" void epicsShareAPI + epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId pQueue ) +{ + pQueue->destroy (); +} + +extern "C" double epicsShareAPI + epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue ) +{ + try { + return pQueue->process ( epicsTime::getCurrent() ); + } + catch ( ... ) { + return 1.0; + } +} + +extern "C" epicsTimerId epicsShareAPI epicsTimerQueuePassiveCreateTimer ( + epicsTimerQueuePassiveId pQueue, epicsTimerCallback pCallback, void *pArg ) +{ + try { + return & pQueue->createTimerForC ( pCallback, pArg ); + } + catch ( ... ) { + return 0; + } +} + +extern "C" epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroyTimer ( + epicsTimerQueuePassiveId /* pQueue */, epicsTimerId pTmr ) +{ + pTmr->destroy (); +} + +extern "C" void epicsShareAPI epicsTimerQueuePassiveShow ( + epicsTimerQueuePassiveId pQueue, unsigned int level ) +{ + pQueue->show ( level ); +} + +extern "C" epicsTimerQueueId epicsShareAPI + epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ) +{ + try { + epicsSingleton < timerQueueActiveMgr > :: reference ref = + timerQueueMgrEPICS.getReference (); + epicsTimerQueueActiveForC & tmr = + ref->allocate ( ref, okToShare ? true : false, threadPriority ); + return &tmr; + } + catch ( ... ) { + return 0; + } +} + +extern "C" void epicsShareAPI epicsTimerQueueRelease ( epicsTimerQueueId pQueue ) +{ + pQueue->release (); +} + +extern "C" epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( + epicsTimerQueueId pQueue, epicsTimerCallback pCallback, void *pArg ) +{ + try { + return & pQueue->createTimerForC ( pCallback, pArg ); + } + catch ( ... ) { + return 0; + } +} + +extern "C" void epicsShareAPI epicsTimerQueueShow ( + epicsTimerQueueId pQueue, unsigned int level ) +{ + pQueue->show ( level ); +} + +extern "C" void epicsShareAPI epicsTimerQueueDestroyTimer ( + epicsTimerQueueId /* pQueue */, epicsTimerId pTmr ) +{ + pTmr->destroy (); +} + +extern "C" void epicsShareAPI epicsTimerStartTime ( + epicsTimerId pTmr, const epicsTimeStamp *pTime ) +{ + pTmr->start ( *pTmr, *pTime ); +} + +extern "C" void epicsShareAPI epicsTimerStartDelay ( + epicsTimerId pTmr, double delaySeconds ) +{ + pTmr->start ( *pTmr, delaySeconds ); +} + +extern "C" void epicsShareAPI epicsTimerCancel ( epicsTimerId pTmr ) +{ + pTmr->cancel (); +} + +extern "C" double epicsShareAPI epicsTimerGetExpireDelay ( epicsTimerId pTmr ) +{ + return pTmr->getExpireDelay (); +} + +extern "C" void epicsShareAPI epicsTimerShow ( + epicsTimerId pTmr, unsigned int level ) +{ + pTmr->timer::show ( level ); +} + diff --git a/src/libCom/timer/epicsTimer.h b/src/libCom/timer/epicsTimer.h new file mode 100644 index 000000000..769174bcc --- /dev/null +++ b/src/libCom/timer/epicsTimer.h @@ -0,0 +1,185 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* epicsTimer.h */ + +/* Authors: Marty Kraimer, Jeff Hill */ + +#ifndef epicsTimerH +#define epicsTimerH + +#include + +#include "shareLib.h" +#include "epicsTime.h" +#include "epicsThread.h" + +#ifdef __cplusplus + +/* + * Notes: + * 1) epicsTimer does not hold its lock when calling callbacks. + */ + +/* code using a timer must implement epicsTimerNotify */ +class epicsShareClass epicsTimerNotify { +public: + enum restart_t { noRestart, restart }; + class expireStatus { + public: + epicsShareFunc expireStatus ( restart_t ); + epicsShareFunc expireStatus ( restart_t, const double & expireDelaySec ); + epicsShareFunc bool restart () const; + epicsShareFunc double expirationDelay () const; + private: + double delay; + }; + + virtual ~epicsTimerNotify () = 0; + /* return "noRestart" or "expireStatus ( restart, 30.0 )" */ + virtual expireStatus expire ( const epicsTime & currentTime ) = 0; + virtual void show ( unsigned int level ) const; +}; + +class epicsShareClass epicsTimer { /* X aCC 655 */ +public: + /* calls cancel (see warning below) and then destroys the timer */ + virtual void destroy () = 0; + virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0; + virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0; + /* WARNING: A deadlock will occur if you hold a lock while + * calling this function that you also take within the timer + * expiration callback. + */ + virtual void cancel () = 0; + struct expireInfo { + expireInfo ( bool active, const epicsTime & expireTime ); + bool active; + epicsTime expireTime; + }; + virtual expireInfo getExpireInfo () const = 0; + double getExpireDelay (); + virtual void show ( unsigned int level ) const = 0; +protected: + virtual ~epicsTimer () = 0; /* protected => delete() must not be called */ +}; + +class epicsTimerQueue { /* X aCC 655 */ +public: + virtual epicsTimer & createTimer () = 0; + virtual void show ( unsigned int level ) const = 0; +protected: + epicsShareFunc virtual ~epicsTimerQueue () = 0; +}; + +class epicsTimerQueueActive /* X aCC 655 */ + : public epicsTimerQueue { +public: + static epicsShareFunc epicsTimerQueueActive & allocate ( + bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 ); + virtual void release () = 0; +protected: + epicsShareFunc virtual ~epicsTimerQueueActive () = 0; +}; + +class epicsTimerQueueNotify { /* X aCC 655 */ +public: + /* called when a new timer is inserted into the queue and the */ + /* delay to the next expire has changed */ + virtual void reschedule () = 0; + /* if there is a quantum in the scheduling of timer intervals */ + /* return this quantum in seconds. If unknown then return zero. */ + virtual double quantum () = 0; +protected: + epicsShareFunc virtual ~epicsTimerQueueNotify () = 0; +}; + +class epicsTimerQueuePassive /* X aCC 655 */ + : public epicsTimerQueue { +public: + static epicsShareFunc epicsTimerQueuePassive & create ( epicsTimerQueueNotify & ); + epicsShareFunc virtual ~epicsTimerQueuePassive () = 0; /* ok to call delete */ + virtual double process ( const epicsTime & currentTime ) = 0; /* returns delay to next expire */ +}; + +inline epicsTimer::expireInfo::expireInfo ( bool activeIn, + const epicsTime & expireTimeIn ) : + active ( activeIn ), expireTime ( expireTimeIn ) +{ +} + +inline double epicsTimer::getExpireDelay () +{ + epicsTimer::expireInfo info = this->getExpireInfo (); + if ( info.active ) { + double delay = info.expireTime - epicsTime::getCurrent (); + if ( delay < 0.0 ) { + delay = 0.0; + } + return delay; + } + return - DBL_MAX; +} + +extern "C" { +#endif /* __cplusplus */ + +typedef struct epicsTimerForC * epicsTimerId; +typedef void ( *epicsTimerCallback ) ( void *pPrivate ); + +/* thread managed timer queue */ +typedef struct epicsTimerQueueActiveForC * epicsTimerQueueId; +epicsShareFunc epicsTimerQueueId epicsShareAPI + epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ); +epicsShareFunc void epicsShareAPI + epicsTimerQueueRelease ( epicsTimerQueueId ); +epicsShareFunc epicsTimerId epicsShareAPI + epicsTimerQueueCreateTimer ( epicsTimerQueueId queueid, + epicsTimerCallback callback, void *arg ); +epicsShareFunc void epicsShareAPI + epicsTimerQueueDestroyTimer ( epicsTimerQueueId queueid, epicsTimerId id ); +epicsShareFunc void epicsShareAPI + epicsTimerQueueShow ( epicsTimerQueueId id, unsigned int level ); + +/* passive timer queue */ +typedef struct epicsTimerQueuePassiveForC * epicsTimerQueuePassiveId; +typedef void ( * epicsTimerQueueNotifyReschedule ) ( void * pPrivate ); +typedef double ( * epicsTimerQueueNotifyQuantum ) ( void * pPrivate ); +epicsShareFunc epicsTimerQueuePassiveId epicsShareAPI + epicsTimerQueuePassiveCreate ( epicsTimerQueueNotifyReschedule, + epicsTimerQueueNotifyQuantum, void *pPrivate ); +epicsShareFunc void epicsShareAPI + epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId ); +epicsShareFunc epicsTimerId epicsShareAPI + epicsTimerQueuePassiveCreateTimer ( + epicsTimerQueuePassiveId queueid, epicsTimerCallback pCallback, void *pArg ); +epicsShareFunc void epicsShareAPI + epicsTimerQueuePassiveDestroyTimer ( epicsTimerQueuePassiveId queueid, epicsTimerId id ); +epicsShareFunc double epicsShareAPI + epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId ); +epicsShareFunc void epicsShareAPI + epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId id, unsigned int level ); + +/* timer */ +epicsShareFunc void epicsShareAPI + epicsTimerStartTime ( epicsTimerId id, const epicsTimeStamp *pTime ); +epicsShareFunc void epicsShareAPI + epicsTimerStartDelay ( epicsTimerId id, double delaySeconds ); +epicsShareFunc void epicsShareAPI + epicsTimerCancel ( epicsTimerId id ); +epicsShareFunc double epicsShareAPI + epicsTimerGetExpireDelay ( epicsTimerId id ); +epicsShareFunc void epicsShareAPI + epicsTimerShow ( epicsTimerId id, unsigned int level ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* epicsTimerH */ diff --git a/src/libCom/timer/timer.cpp b/src/libCom/timer/timer.cpp new file mode 100644 index 000000000..788bf6d61 --- /dev/null +++ b/src/libCom/timer/timer.cpp @@ -0,0 +1,259 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + +/* + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + * + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsGuard.h" +#include "timerPrivate.h" +#include "errlog.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class tsFreeList < timer, 0x20 >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +timer::timer ( timerQueue & queueIn ) : + queue ( queueIn ), curState ( stateLimbo ), pNotify ( 0 ) +{ +} + +timer::~timer () +{ + this->cancel (); +} + +void timer::destroy () +{ + timerQueue & queueTmp = this->queue; + this->~timer (); + queueTmp.timerFreeList.release ( this ); +} + +void timer::start ( epicsTimerNotify & notify, double delaySeconds ) +{ + this->start ( notify, epicsTime::getCurrent () + delaySeconds ); +} + +void timer::start ( epicsTimerNotify & notify, const epicsTime & expire ) +{ + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + this->privateStart ( notify, expire ); +} + +void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire ) +{ + this->pNotify = & notify; + this->exp = expire - ( this->queue.notify.quantum () / 2.0 ); + + bool reschedualNeeded = false; + if ( this->curState == stateActive ) { + // above expire time and notify will override any restart parameters + // that may be returned from the timer expire callback + return; + } + else if ( this->curState == statePending ) { + this->queue.timerList.remove ( *this ); + if ( this->queue.timerList.first() == this && + this->queue.timerList.count() > 0 ) { + reschedualNeeded = true; + } + } + +# ifdef DEBUG + unsigned preemptCount=0u; +# endif + + // + // insert into the pending queue + // + // Finds proper time sorted location using a linear search. + // + // **** this should use a binary tree ???? + // + tsDLIter < timer > pTmr = this->queue.timerList.lastIter (); + while ( true ) { + if ( ! pTmr.valid () ) { + // + // add to the beginning of the list + // + this->queue.timerList.push ( *this ); + reschedualNeeded = true; + break; + } + if ( pTmr->exp <= this->exp ) { + // + // add after the item found that expires earlier + // + this->queue.timerList.insertAfter ( *this, *pTmr ); + break; + } +# ifdef DEBUG + preemptCount++; +# endif + --pTmr; + } + + this->curState = timer::statePending; + + if ( reschedualNeeded ) { + this->queue.notify.reschedule (); + } + +# if defined(DEBUG) && 0 + this->show ( 10u ); + this->queue.show ( 10u ); +# endif + + debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n", + typeid ( this->notify ).name (), + expire - epicsTime::getCurrent (), + this, preemptCount ) ); +} + +void timer::cancel () +{ + bool reschedual = false; + bool wakeupCancelBlockingThreads = false; + { + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + this->pNotify = 0; + if ( this->curState == statePending ) { + this->queue.timerList.remove ( *this ); + this->curState = stateLimbo; + if ( this->queue.timerList.first() == this && + this->queue.timerList.count() > 0 ) { + reschedual = true; + } + } + else if ( this->curState == stateActive ) { + this->queue.cancelPending = true; + this->curState = timer::stateLimbo; + if ( this->queue.processThread != epicsThreadGetIdSelf() ) { + // make certain timer expire() does not run after cancel () returns, + // but dont require that lock is applied while calling expire() + while ( this->queue.cancelPending && + this->queue.pExpireTmr == this ) { + epicsGuardRelease < epicsMutex > autoRelease ( locker ); + this->queue.cancelBlockingEvent.wait (); + } + // in case other threads are waiting + wakeupCancelBlockingThreads = true; + } + } + } + if ( reschedual ) { + this->queue.notify.reschedule (); + } + if ( wakeupCancelBlockingThreads ) { + this->queue.cancelBlockingEvent.signal (); + } +} + +epicsTimer::expireInfo timer::getExpireInfo () const +{ + // taking a lock here guarantees that users will not + // see brief intervals when a timer isnt active because + // it is is canceled when start is called + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + if ( this->curState == statePending || this->curState == stateActive ) { + return expireInfo ( true, this->exp ); + } + return expireInfo ( false, epicsTime() ); +} + +void timer::show ( unsigned int level ) const +{ + epicsGuard < epicsMutex > locker ( this->queue.mutex ); + double delay; + if ( this->curState == statePending || this->curState == stateActive ) { + try { + delay = this->exp - epicsTime::getCurrent(); + } + catch ( ... ) { + delay = - DBL_MAX; + } + } + else { + delay = -DBL_MAX; + } + const char *pStateName; + if ( this->curState == statePending ) { + pStateName = "pending"; + } + else if ( this->curState == stateActive ) { + pStateName = "active"; + } + else if ( this->curState == stateLimbo ) { + pStateName = "limbo"; + } + else { + pStateName = "corrupt"; + } + printf ( "timer, state = %s, delay = %f\n", + pStateName, delay ); + if ( level >= 1u && this->pNotify ) { + this->pNotify->show ( level - 1u ); + } +} + +void * timer::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void timer::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + +void * epicsTimerForC::operator new ( size_t ) // X aCC 361 +{ + // The HPUX compiler seems to require this even though no code + // calls it directly + throw std::logic_error ( "why is the compiler calling private operator new" ); +} + +void epicsTimerForC::operator delete ( void * ) +{ + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", + __FILE__, __LINE__ ); +} + diff --git a/src/libCom/timer/timerPrivate.h b/src/libCom/timer/timerPrivate.h new file mode 100644 index 000000000..754d03ff3 --- /dev/null +++ b/src/libCom/timer/timerPrivate.h @@ -0,0 +1,281 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#ifndef epicsTimerPrivate_h +#define epicsTimerPrivate_h + +#include + +#include "tsFreeList.h" +#include "epicsSingleton.h" +#include "tsDLList.h" +#include "epicsTimer.h" +#include "compilerDependencies.h" + +#ifdef DEBUG +# define debugPrintf(ARGSINPAREN) printf ARGSINPAREN +#else +# define debugPrintf(ARGSINPAREN) +#endif + +template < class T > class epicsGuard; + +class timer : public epicsTimer, public tsDLNode < timer > { +public: + void destroy (); + void start ( class epicsTimerNotify &, const epicsTime & ); + void start ( class epicsTimerNotify &, double delaySeconds ); + void cancel (); + expireInfo getExpireInfo () const; + void show ( unsigned int level ) const; + void * operator new ( size_t size, tsFreeList < timer, 0x20 > & ); + epicsPlacementDeleteOperator (( void *, tsFreeList < timer, 0x20 > & )) +protected: + timer ( class timerQueue & ); + ~timer (); + timerQueue & queue; +private: + enum state { statePending = 45, stateActive = 56, stateLimbo = 78 }; + epicsTime exp; // experation time + state curState; // current state + epicsTimerNotify * pNotify; // callback + void privateStart ( epicsTimerNotify & notify, const epicsTime & ); + timer & operator = ( const timer & ); + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + void * operator new ( size_t size ); + void operator delete ( void * ); + friend class timerQueue; +}; + +struct epicsTimerForC : public epicsTimerNotify, public timer { +public: + void destroy (); +protected: + epicsTimerForC ( timerQueue &, epicsTimerCallback, void *pPrivateIn ); + ~epicsTimerForC (); + void * operator new ( size_t size, tsFreeList < epicsTimerForC, 0x20 > & ); + epicsPlacementDeleteOperator (( void *, tsFreeList < epicsTimerForC, 0x20 > & )) +private: + epicsTimerCallback pCallBack; + void * pPrivate; + expireStatus expire ( const epicsTime & currentTime ); + epicsTimerForC & operator = ( const epicsTimerForC & ); + // Visual C++ .net appears to require operator delete if + // placement operator delete is defined? I smell a ms rat + // because if I declare placement new and delete, but + // comment out the placement delete definition there are + // no undefined symbols. + void * operator new ( size_t size ); + void operator delete ( void * ); + friend class timerQueue; +}; + +using std :: type_info; + +class timerQueue : public epicsTimerQueue { +public: + timerQueue ( epicsTimerQueueNotify ¬ify ); + virtual ~timerQueue (); + epicsTimer & createTimer (); + epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); + double process ( const epicsTime & currentTime ); + void show ( unsigned int level ) const; +private: + tsFreeList < timer, 0x20 > timerFreeList; + tsFreeList < epicsTimerForC, 0x20 > timerForCFreeList; + mutable epicsMutex mutex; + epicsEvent cancelBlockingEvent; + tsDLList < timer > timerList; + epicsTimerQueueNotify & notify; + timer * pExpireTmr; + epicsThreadId processThread; + epicsTime exceptMsgTimeStamp; + bool cancelPending; + static const double exceptMsgMinPeriod; + void printExceptMsg ( const char * pName, + const type_info & type ); + timerQueue ( const timerQueue & ); + timerQueue & operator = ( const timerQueue & ); + friend class timer; + friend struct epicsTimerForC; +}; + +class timerQueueActiveMgrPrivate { // X aCC 655 +public: + timerQueueActiveMgrPrivate (); +protected: + virtual ~timerQueueActiveMgrPrivate () = 0; +private: + unsigned referenceCount; + friend class timerQueueActiveMgr; +}; + +class timerQueueActiveMgr; + +class timerQueueActive : public epicsTimerQueueActive, + public epicsThreadRunable, public epicsTimerQueueNotify, + public timerQueueActiveMgrPrivate { +public: + typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr; + timerQueueActive ( RefMgr &, bool okToShare, unsigned priority ); + epicsTimer & createTimer (); + epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); + void show ( unsigned int level ) const; + bool sharingOK () const; + unsigned threadPriority () const; +protected: + ~timerQueueActive (); + RefMgr _refMgr; +private: + timerQueue queue; + epicsEvent rescheduleEvent; + epicsEvent exitEvent; + epicsThread thread; + const double sleepQuantum; + bool okToShare; + bool exitFlag; + bool terminateFlag; + void run (); + void reschedule (); + double quantum (); + void _printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ); + epicsTimerQueue & getEpicsTimerQueue (); + timerQueueActive ( const timerQueueActive & ); + timerQueueActive & operator = ( const timerQueueActive & ); +}; + +class timerQueueActiveMgr { +public: + typedef epicsSingleton < timerQueueActiveMgr > :: reference RefThis; + timerQueueActiveMgr (); + ~timerQueueActiveMgr (); + epicsTimerQueueActiveForC & allocate ( RefThis &, bool okToShare, + unsigned threadPriority = epicsThreadPriorityMin + 10 ); + void release ( epicsTimerQueueActiveForC & ); +private: + epicsMutex mutex; + tsDLList < epicsTimerQueueActiveForC > sharedQueueList; + timerQueueActiveMgr ( const timerQueueActiveMgr & ); + timerQueueActiveMgr & operator = ( const timerQueueActiveMgr & ); +}; + +extern epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; + +class timerQueuePassive : public epicsTimerQueuePassive { +public: + timerQueuePassive ( epicsTimerQueueNotify & ); + epicsTimer & createTimer (); + epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); + void show ( unsigned int level ) const; + double process ( const epicsTime & currentTime ); +protected: + timerQueue queue; + ~timerQueuePassive (); + epicsTimerQueue & getEpicsTimerQueue (); + timerQueuePassive ( const timerQueuePassive & ); + timerQueuePassive & operator = ( const timerQueuePassive & ); +}; + +struct epicsTimerQueuePassiveForC : + public epicsTimerQueueNotify, public timerQueuePassive { +public: + epicsTimerQueuePassiveForC ( + epicsTimerQueueNotifyReschedule, + epicsTimerQueueNotifyQuantum, + void * pPrivate ); + void destroy (); +protected: + ~epicsTimerQueuePassiveForC (); +private: + epicsTimerQueueNotifyReschedule pRescheduleCallback; + epicsTimerQueueNotifyQuantum pSleepQuantumCallback; + void * pPrivate; + static epicsSingleton < tsFreeList < epicsTimerQueuePassiveForC, 0x10 > > pFreeList; + void reschedule (); + double quantum (); +}; + +struct epicsTimerQueueActiveForC : public timerQueueActive, + public tsDLNode < epicsTimerQueueActiveForC > { +public: + epicsTimerQueueActiveForC ( RefMgr &, bool okToShare, unsigned priority ); + void release (); + void * operator new ( size_t ); + void operator delete ( void * ); +protected: + virtual ~epicsTimerQueueActiveForC (); +private: + epicsTimerQueueActiveForC ( const epicsTimerQueueActiveForC & ); + epicsTimerQueueActiveForC & operator = ( const epicsTimerQueueActiveForC & ); +}; + +inline bool timerQueueActive::sharingOK () const +{ + return this->okToShare; +} + +inline unsigned timerQueueActive::threadPriority () const +{ + return thread.getPriority (); +} + +inline void * timer::operator new ( size_t size, + tsFreeList < timer, 0x20 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void timer::operator delete ( void * pCadaver, + tsFreeList < timer, 0x20 > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void * epicsTimerForC::operator new ( size_t size, + tsFreeList < epicsTimerForC, 0x20 > & freeList ) +{ + return freeList.allocate ( size ); +} + +#ifdef CXX_PLACEMENT_DELETE +inline void epicsTimerForC::operator delete ( void * pCadaver, + tsFreeList < epicsTimerForC, 0x20 > & freeList ) +{ + freeList.release ( pCadaver ); +} +#endif + +inline void * epicsTimerQueueActiveForC::operator new ( size_t size ) +{ + return ::operator new ( size ); +} + +inline void epicsTimerQueueActiveForC::operator delete ( void * pCadaver ) +{ + ::operator delete ( pCadaver ); +} + +#endif // epicsTimerPrivate_h + diff --git a/src/libCom/timer/timerQueue.cpp b/src/libCom/timer/timerQueue.cpp new file mode 100644 index 000000000..cbccd8ffa --- /dev/null +++ b/src/libCom/timer/timerQueue.cpp @@ -0,0 +1,226 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include +#include + +#define epicsExportSharedSymbols +#include "epicsGuard.h" +#include "timerPrivate.h" +#include "errlog.h" + +const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds + +epicsTimerQueue::~epicsTimerQueue () {} + +timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) : + notify ( notifyIn ), + pExpireTmr ( 0 ), + processThread ( 0 ), + exceptMsgTimeStamp ( + epicsTime :: getCurrent () - exceptMsgMinPeriod ), + cancelPending ( false ) +{ +} + +timerQueue::~timerQueue () +{ + timer *pTmr; + while ( ( pTmr = this->timerList.get () ) ) { + pTmr->curState = timer::stateLimbo; + } +} + +void timerQueue :: + printExceptMsg ( const char * pName, const type_info & type ) +{ + char date[64]; + double delay; + try { + epicsTime cur = epicsTime :: getCurrent (); + delay = cur - this->exceptMsgTimeStamp; + cur.strftime ( date, sizeof ( date ), + "%a %b %d %Y %H:%M:%S.%f" ); + if ( delay >= exceptMsgMinPeriod ) { + this->exceptMsgTimeStamp = cur; + } + } + catch ( ... ) { + delay = DBL_MAX; + strcpy ( date, "UKN DATE" ); + } + if ( delay >= exceptMsgMinPeriod ) { + // we dont touch the typeid for the timer expiration + // notify interface here because they might have + // destroyed the timer during its callback + errlogPrintf ( + "timerQueue: Unexpected C++ exception \"%s\" " + "with type \"%s\" during timer expiration " + "callback at %s\n", + pName, + type.name (), + date ); + errlogFlush (); + } +} + +double timerQueue::process ( const epicsTime & currentTime ) +{ + epicsGuard < epicsMutex > guard ( this->mutex ); + + if ( this->pExpireTmr ) { + // if some other thread is processing the queue + // (or if this is a recursive call) + timer * pTmr = this->timerList.first (); + if ( pTmr ) { + double delay = pTmr->exp - currentTime; + if ( delay < 0.0 ) { + delay = 0.0; + } + return delay; + } + else { + return DBL_MAX; + } + } + + // + // Tag current epired tmr so that we can detect if call back + // is in progress when canceling the timer. + // + if ( this->timerList.first () ) { + if ( currentTime >= this->timerList.first ()->exp ) { + this->pExpireTmr = this->timerList.first (); + this->timerList.remove ( *this->pExpireTmr ); + this->pExpireTmr->curState = timer::stateActive; + this->processThread = epicsThreadGetIdSelf (); +# ifdef DEBUG + this->pExpireTmr->show ( 0u ); +# endif + } + else { + double delay = this->timerList.first ()->exp - currentTime; + debugPrintf ( ( "no activity process %f to next\n", delay ) ); + return delay; + } + } + else { + return DBL_MAX; + } + +# ifdef DEBUG + unsigned N = 0u; +# endif + + double delay = DBL_MAX; + while ( true ) { + epicsTimerNotify *pTmpNotify = this->pExpireTmr->pNotify; + this->pExpireTmr->pNotify = 0; + epicsTimerNotify::expireStatus expStat ( epicsTimerNotify::noRestart ); + + { + epicsGuardRelease < epicsMutex > unguard ( guard ); + + debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", + N++, typeid ( this->pExpireTmr->notify ).name (), + currentTime - this->pExpireTmr->exp ) ); + try { + expStat = pTmpNotify->expire ( currentTime ); + } + catch ( std::exception & except ) { + printExceptMsg ( except.what (), typeid ( except ) ); + } + catch ( ... ) { + printExceptMsg ( "non-standard exception", typeid ( void ) ); + } + } + + // + // only restart if they didnt cancel() the timer + // while the call back was running + // + if ( this->cancelPending ) { + // 1) if another thread is canceling then cancel() waits for + // the event below + // 2) if this thread is canceling in the timer callback then + // dont touch timer or notify here because the cancel might + // have occurred because they destroyed the timer in the + // callback + this->cancelPending = false; + this->cancelBlockingEvent.signal (); + } + else { + this->pExpireTmr->curState = timer::stateLimbo; + if ( this->pExpireTmr->pNotify ) { + // pNotify was cleared above so if it is valid now we know that + // someone has started the timer from another thread and that + // predominates over the restart parameters from expire. + this->pExpireTmr->privateStart ( + *this->pExpireTmr->pNotify, this->pExpireTmr->exp ); + } + else if ( expStat.restart() ) { + // restart as nec + this->pExpireTmr->privateStart ( + *pTmpNotify, currentTime + expStat.expirationDelay() ); + } + } + this->pExpireTmr = 0; + + if ( this->timerList.first () ) { + if ( currentTime >= this->timerList.first ()->exp ) { + this->pExpireTmr = this->timerList.first (); + this->timerList.remove ( *this->pExpireTmr ); + this->pExpireTmr->curState = timer::stateActive; +# ifdef DEBUG + this->pExpireTmr->show ( 0u ); +# endif + } + else { + delay = this->timerList.first ()->exp - currentTime; + this->processThread = 0; + break; + } + } + else { + this->processThread = 0; + delay = DBL_MAX; + break; + } + } + return delay; +} + +epicsTimer & timerQueue::createTimer () +{ + return * new ( this->timerFreeList ) timer ( * this ); +} + +epicsTimerForC & timerQueue::createTimerForC ( epicsTimerCallback pCallback, void *pArg ) +{ + return * new ( this->timerForCFreeList ) epicsTimerForC ( *this, pCallback, pArg ); +} + +void timerQueue::show ( unsigned level ) const +{ + epicsGuard < epicsMutex > locker ( this->mutex ); + printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () ); + if ( level >= 1u ) { + tsDLIterConst < timer > iter = this->timerList.firstIter (); + while ( iter.valid () ) { + iter->show ( level - 1u ); + ++iter; + } + } +} diff --git a/src/libCom/timer/timerQueueActive.cpp b/src/libCom/timer/timerQueueActive.cpp new file mode 100644 index 000000000..a1c25f4a0 --- /dev/null +++ b/src/libCom/timer/timerQueueActive.cpp @@ -0,0 +1,153 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#define epicsExportSharedSymbols +#include "timerPrivate.h" +#include "errlog.h" + +#ifdef _MSC_VER +# pragma warning ( push ) +# pragma warning ( disable:4660 ) +#endif + +template class epicsSingleton < timerQueueActiveMgr >; + +#ifdef _MSC_VER +# pragma warning ( pop ) +#endif + +epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; + +epicsTimerQueueActive::~epicsTimerQueueActive () {} + +epicsTimerQueueActive & epicsTimerQueueActive::allocate ( bool okToShare, unsigned threadPriority ) +{ + epicsSingleton < timerQueueActiveMgr >::reference pMgr = + timerQueueMgrEPICS.getReference (); + return pMgr->allocate ( pMgr, okToShare, threadPriority ); +} + +timerQueueActive :: + timerQueueActive ( RefMgr & refMgr, + bool okToShareIn, unsigned priority ) : + _refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue", + epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ), + sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ), + exitFlag ( false ), terminateFlag ( false ) +{ + this->thread.start (); +} + +timerQueueActive::~timerQueueActive () +{ + this->terminateFlag = true; + this->rescheduleEvent.signal (); + while ( ! this->exitFlag ) { + this->exitEvent.wait ( 1.0 ); + } + // in case other threads are waiting here also + this->exitEvent.signal (); +} + +void timerQueueActive :: _printLastChanceExceptionMessage ( + const char * pExceptionTypeName, + const char * pExceptionContext ) +{ + char date[64]; + try { + epicsTime cur = epicsTime :: getCurrent (); + cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); + } + catch ( ... ) { + strcpy ( date, "" ); + } + errlogPrintf ( + "timerQueueActive: Unexpected C++ exception \"%s\" with type \"%s\" " + "while processing timer queue, at %s\n", + pExceptionContext, pExceptionTypeName, date ); +} + + +void timerQueueActive :: run () +{ + this->exitFlag = false; + while ( ! this->terminateFlag ) { + try { + double delay = this->queue.process ( epicsTime::getCurrent() ); + debugPrintf ( ( "timer thread sleeping for %g sec (max)\n", delay ) ); + this->rescheduleEvent.wait ( delay ); + } + catch ( std :: exception & except ) { + _printLastChanceExceptionMessage ( + typeid ( except ).name (), except.what () ); + epicsThreadSleep ( 10.0 ); + } + catch ( ... ) { + _printLastChanceExceptionMessage ( + "catch ( ... )", "Non-standard C++ exception" ); + epicsThreadSleep ( 10.0 ); + } + } + this->exitFlag = true; + this->exitEvent.signal (); // no access to queue after exitEvent signal +} + +epicsTimer & timerQueueActive::createTimer () +{ + return this->queue.createTimer(); +} + +epicsTimerForC & timerQueueActive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) +{ + return this->queue.createTimerForC ( pCallback, pArg ); +} + +void timerQueueActive::reschedule () +{ + this->rescheduleEvent.signal (); +} + +double timerQueueActive::quantum () +{ + return this->sleepQuantum; +} + +void timerQueueActive::show ( unsigned int level ) const +{ + printf ( "EPICS threaded timer queue at %p\n", + static_cast ( this ) ); + if ( level > 0u ) { + // specifying level one here avoids recursive + // show callback + this->thread.show ( 1u ); + this->queue.show ( level - 1u ); + printf ( "reschedule event\n" ); + this->rescheduleEvent.show ( level - 1u ); + printf ( "exit event\n" ); + this->exitEvent.show ( level - 1u ); + printf ( "exitFlag = %c, terminateFlag = %c\n", + this->exitFlag ? 'T' : 'F', + this->terminateFlag ? 'T' : 'F' ); + } +} + +epicsTimerQueue & timerQueueActive::getEpicsTimerQueue () +{ + return static_cast < epicsTimerQueue &> ( * this ); +} + diff --git a/src/libCom/timer/timerQueueActiveMgr.cpp b/src/libCom/timer/timerQueueActiveMgr.cpp new file mode 100644 index 000000000..e7a9b218d --- /dev/null +++ b/src/libCom/timer/timerQueueActiveMgr.cpp @@ -0,0 +1,85 @@ +/*************************************************************************\ +* 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 Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +#include + +#define epicsExportSharedSymbols +#include "epicsGuard.h" +#include "timerPrivate.h" + +timerQueueActiveMgr::timerQueueActiveMgr () +{ +} + +timerQueueActiveMgr::~timerQueueActiveMgr () +{ + epicsGuard < epicsMutex > locker ( this->mutex ); +} + +epicsTimerQueueActiveForC & timerQueueActiveMgr :: + allocate ( RefThis & refThis, bool okToShare, unsigned threadPriority ) +{ + epicsGuard < epicsMutex > locker ( this->mutex ); + if ( okToShare ) { + tsDLIter < epicsTimerQueueActiveForC > iter = this->sharedQueueList.firstIter (); + while ( iter.valid () ) { + if ( iter->threadPriority () == threadPriority ) { + assert ( iter->timerQueueActiveMgrPrivate::referenceCount < UINT_MAX ); + iter->timerQueueActiveMgrPrivate::referenceCount++; + return *iter; + } + iter++; + } + } + + epicsTimerQueueActiveForC & queue = + * new epicsTimerQueueActiveForC ( refThis, okToShare, threadPriority ); + queue.timerQueueActiveMgrPrivate::referenceCount = 1u; + if ( okToShare ) { + this->sharedQueueList.add ( queue ); + } + return queue; +} + +void timerQueueActiveMgr :: + release ( epicsTimerQueueActiveForC & queue ) +{ + timerQueueActiveMgrPrivate * pPriv = & queue; + { + epicsGuard < epicsMutex > locker ( this->mutex ); + assert ( queue.timerQueueActiveMgrPrivate::referenceCount > 0u ); + queue.timerQueueActiveMgrPrivate::referenceCount--; + if ( queue.timerQueueActiveMgrPrivate::referenceCount == 0u ) { + if ( queue.sharingOK () ) { + this->sharedQueueList.remove ( queue ); + } + } + } + // delete only after we release the guard in case the embedded + // reference is the last one and this object is destroyed + // as a side effect + delete pPriv; +} + +timerQueueActiveMgrPrivate::timerQueueActiveMgrPrivate () : + referenceCount ( 0u ) +{ +} + +timerQueueActiveMgrPrivate::~timerQueueActiveMgrPrivate () +{ +} diff --git a/src/libCom/timer/timerQueuePassive.cpp b/src/libCom/timer/timerQueuePassive.cpp new file mode 100644 index 000000000..192f2bbe5 --- /dev/null +++ b/src/libCom/timer/timerQueuePassive.cpp @@ -0,0 +1,70 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * + * Author Jeffrey O. Hill + * johill@lanl.gov + * 505 665 1831 + */ + +// +// Note, a free list for this class is not currently used because of +// entanglements between the file scope free list destructor and a +// file scope fdManager destructor which is trying to call a +// destructor for a passive timer queue which is no longer valid +// in pool. +// + +#include + +#define epicsExportSharedSymbols +#include "timerPrivate.h" + +epicsTimerQueuePassive::~epicsTimerQueuePassive () {} + +epicsTimerQueuePassive & epicsTimerQueuePassive::create ( epicsTimerQueueNotify ¬ify ) +{ + return * new timerQueuePassive ( notify ); +} + +timerQueuePassive::timerQueuePassive ( epicsTimerQueueNotify ¬ifyIn ) : + queue ( notifyIn ) {} + +timerQueuePassive::~timerQueuePassive () {} + +epicsTimer & timerQueuePassive::createTimer () +{ + return this->queue.createTimer (); +} + +epicsTimerForC & timerQueuePassive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) +{ + return this->queue.createTimerForC ( pCallback, pArg ); +} + +double timerQueuePassive::process ( const epicsTime & currentTime ) +{ + return this->queue.process ( currentTime ); +} + +void timerQueuePassive::show ( unsigned int level ) const +{ + printf ( "EPICS non-threaded timer queue at %p\n", + static_cast ( this ) ); + if ( level >=1u ) { + this->queue.show ( level - 1u ); + } +} + +epicsTimerQueue & timerQueuePassive::getEpicsTimerQueue () +{ + return static_cast < epicsTimerQueue &> ( * this ); +} + diff --git a/src/libCom/tsDefs/README b/src/libCom/tsDefs/README new file mode 100644 index 000000000..fddac4575 --- /dev/null +++ b/src/libCom/tsDefs/README @@ -0,0 +1,6 @@ +This directory contains the old R3.13 +time stamp definitions and routines +needed by epics extensions. They exist +for compatibility purposes only and +should not be used by new code. + diff --git a/src/libCom/tsDefs/tsDefs.c b/src/libCom/tsDefs/tsDefs.c new file mode 100644 index 000000000..6c06592d3 --- /dev/null +++ b/src/libCom/tsDefs/tsDefs.c @@ -0,0 +1,36 @@ +/*************************************************************************\ +* 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 + */ + +#define epicsExportSharedSymbols +#include "tsDefs.h" + +epicsShareFunc char * epicsShareAPI tsStampToText(const TS_STAMP *pTS,enum tsTextType textType,char *textBuffer) +{ + switch (textType) { + case TS_TEXT_MMDDYY: + epicsTimeToStrftime(textBuffer,28,"%m/%d/%y %H:%M:%S.%09f",pTS); + break; + case TS_TEXT_MONDDYYYY: + epicsTimeToStrftime(textBuffer,32,"%b %d, %Y %H:%M:%S.%09f",pTS); + break; + default: + return NULL; + } + return textBuffer; +} + +epicsShareFunc long epicsShareAPI tsLocalTime(TS_STAMP *pStamp) +{ + return epicsTimeGetCurrent(pStamp); +} + diff --git a/src/libCom/tsDefs/tsDefs.h b/src/libCom/tsDefs/tsDefs.h new file mode 100644 index 000000000..8136f9a94 --- /dev/null +++ b/src/libCom/tsDefs/tsDefs.h @@ -0,0 +1,50 @@ +/*************************************************************************\ +* 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 + * This file contains the old R3.13 + * time stamp definitions and routines + * needed by epics extensions. They exist + * for compatibility purposes only and + * should not be used by new code. + * + */ +#ifndef tsDefsh +#define tsDefsh + +#include "epicsTime.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------------------------- +* TS_TEXT_xxx text type codes for converting between text and time stamp +* +* TS_TEXT_MONDDYYYY Mon dd, yyyy hh:mm:ss.nano-secs +* TS_TEXT_MMDDYY mm/dd/yy hh:mm:ss.nano-secs +* 123456789012345678901234567890123456789 +* 0 1 2 3 +*----------------------------------------------------------------------------*/ +enum tsTextType{ + TS_TEXT_MONDDYYYY, + TS_TEXT_MMDDYY +}; + +epicsShareFunc char * epicsShareAPI tsStampToText(const TS_STAMP *pTS,enum tsTextType textType,char *textBuffer); +epicsShareFunc long epicsShareAPI tsLocalTime(TS_STAMP *pStamp); + +#ifdef __cplusplus +} +#endif +#endif /* tsDefsh */ + + diff --git a/src/makeBaseApp/Makefile b/src/makeBaseApp/Makefile new file mode 100644 index 000000000..c7f89f8e1 --- /dev/null +++ b/src/makeBaseApp/Makefile @@ -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 + diff --git a/src/makeBaseApp/makeBaseApp.pl b/src/makeBaseApp/makeBaseApp.pl new file mode 100755 index 000000000..a176d9830 --- /dev/null +++ b/src/makeBaseApp/makeBaseApp.pl @@ -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@', 'name@' & '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@' or 'name@'. + +# 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 (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 = ; + 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 = ; + 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 = ; + 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 () { + 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) = /^\s*(\w+)\s*=\s*(.*)/; + if ($macro ne "") { + $Rmacros->{$macro} = $path; + push @$Rapps, $macro; + next; + } + # Handle "include " 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 () { + 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 </bin//makeBaseApp.pl -h + display help on command options +/bin//makeBaseApp.pl -l [options] + list application types +/bin//makeBaseApp.pl -t type [options] [app ...] + create application directories +/bin//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 </bin//makeBaseApp.pl -t example example +/bin//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 = ; + 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; +} diff --git a/src/makeBaseApp/top/Makefile b/src/makeBaseApp/top/Makefile new file mode 100644 index 000000000..f0d9ad762 --- /dev/null +++ b/src/makeBaseApp/top/Makefile @@ -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 + + diff --git a/src/makeBaseApp/top/caClientApp/Makefile b/src/makeBaseApp/top/caClientApp/Makefile new file mode 100644 index 000000000..47011375c --- /dev/null +++ b/src/makeBaseApp/top/caClientApp/Makefile @@ -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 + diff --git a/src/makeBaseApp/top/caClientApp/caExample.c b/src/makeBaseApp/top/caClientApp/caExample.c new file mode 100644 index 000000000..cc342e237 --- /dev/null +++ b/src/makeBaseApp/top/caClientApp/caExample.c @@ -0,0 +1,25 @@ +/*caExample.c*/ +#include +#include +#include +#include + +#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); +} diff --git a/src/makeBaseApp/top/caClientApp/caMonitor.c b/src/makeBaseApp/top/caClientApp/caMonitor.c new file mode 100644 index 000000000..9bd77e8ea --- /dev/null +++ b/src/makeBaseApp/top/caClientApp/caMonitor.c @@ -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 +#include +#include +#include + +#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; imychid), + "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); +} diff --git a/src/makeBaseApp/top/caServerApp/Makefile b/src/makeBaseApp/top/caServerApp/Makefile new file mode 100644 index 000000000..8584b8804 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/Makefile @@ -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 + + diff --git a/src/makeBaseApp/top/caServerApp/README b/src/makeBaseApp/top/caServerApp/README new file mode 100644 index 000000000..202c0dbc5 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/README @@ -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 -t -p +-c -s<1=scan on (default), 0=scan off> +-ss<1=synchronous scan (default), 0=asynchronous scan>] + +-d +Increased diagnostics messages with increasing debug level. Defaults to no +messages. + +-t +Specifies the duration that the server will run. Defaults to forever. + +-p +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 +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. + diff --git a/src/makeBaseApp/top/caServerApp/exAsyncPV.cc b/src/makeBaseApp/top/caServerApp/exAsyncPV.cc new file mode 100644 index 000000000..7e0758c13 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exAsyncPV.cc @@ -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; +} + diff --git a/src/makeBaseApp/top/caServerApp/exChannel.cc b/src/makeBaseApp/top/caServerApp/exChannel.cc new file mode 100644 index 000000000..5f4e5b109 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exChannel.cc @@ -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; +} + + diff --git a/src/makeBaseApp/top/caServerApp/exPV.cc b/src/makeBaseApp/top/caServerApp/exPV.cc new file mode 100644 index 000000000..bc88b15bb --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exPV.cc @@ -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::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; +} + diff --git a/src/makeBaseApp/top/caServerApp/exScalarPV.cc b/src/makeBaseApp/top/caServerApp/exScalarPV.cc new file mode 100644 index 000000000..0e91d81e9 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exScalarPV.cc @@ -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 +#include +#include + +#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; +} + diff --git a/src/makeBaseApp/top/caServerApp/exServer.cc b/src/makeBaseApp/top/caServerApp/exServer.cc new file mode 100644 index 000000000..cff290838 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exServer.cc @@ -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; igetName(), 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; +} + diff --git a/src/makeBaseApp/top/caServerApp/exServer.h b/src/makeBaseApp/top/caServerApp/exServer.h new file mode 100644 index 000000000..5fb6adaf7 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exServer.h @@ -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 +#include + +// +// 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 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) +{ +} + diff --git a/src/makeBaseApp/top/caServerApp/exVectorPV.cc b/src/makeBaseApp/top/caServerApp/exVectorPV.cc new file mode 100644 index 000000000..da357831f --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/exVectorPV.cc @@ -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 (pFinfo.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; +} diff --git a/src/makeBaseApp/top/caServerApp/main.cc b/src/makeBaseApp/top/caServerApp/main.cc new file mode 100644 index 000000000..34d794841 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/main.cc @@ -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 -t -p " + "-c -s <1=scan on (default), 0=scan off> " + "-ss <1=synchronous scan (default), 0=asynchronous scan> " + "-a -ad " + "-an \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); +} + diff --git a/src/makeBaseApp/top/caServerApp/test.adl b/src/makeBaseApp/top/caServerApp/test.adl new file mode 100644 index 000000000..6086a3be5 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/test.adl @@ -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="" +} +"<>" { + 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" + } +} +"<>" { + 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" + } +} +"<>" { + 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" +} diff --git a/src/makeBaseApp/top/caServerApp/vxEntry.cc b/src/makeBaseApp/top/caServerApp/vxEntry.cc new file mode 100644 index 000000000..9f9343f96 --- /dev/null +++ b/src/makeBaseApp/top/caServerApp/vxEntry.cc @@ -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 +#include + +#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" + diff --git a/src/makeBaseApp/top/configure/CONFIG b/src/makeBaseApp/top/configure/CONFIG new file mode 100644 index 000000000..c1a470322 --- /dev/null +++ b/src/makeBaseApp/top/configure/CONFIG @@ -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 + diff --git a/src/makeBaseApp/top/configure/CONFIG_SITE b/src/makeBaseApp/top/configure/CONFIG_SITE new file mode 100644 index 000000000..72b399d43 --- /dev/null +++ b/src/makeBaseApp/top/configure/CONFIG_SITE @@ -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= + +# 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 = diff --git a/src/makeBaseApp/top/configure/Makefile b/src/makeBaseApp/top/configure/Makefile new file mode 100644 index 000000000..925430940 --- /dev/null +++ b/src/makeBaseApp/top/configure/Makefile @@ -0,0 +1,8 @@ +TOP=.. + +include $(TOP)/configure/CONFIG + +TARGETS = $(CONFIG_TARGETS) +CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) + +include $(TOP)/configure/RULES diff --git a/src/makeBaseApp/top/configure/RELEASE b/src/makeBaseApp/top/configure/RELEASE new file mode 100644 index 000000000..84074fe15 --- /dev/null +++ b/src/makeBaseApp/top/configure/RELEASE @@ -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 diff --git a/src/makeBaseApp/top/configure/RULES b/src/makeBaseApp/top/configure/RULES new file mode 100644 index 000000000..6d56e14e8 --- /dev/null +++ b/src/makeBaseApp/top/configure/RULES @@ -0,0 +1,6 @@ +# RULES + +include $(CONFIG)/RULES + +# Library should be rebuilt because LIBOBJS may have changed. +$(LIBNAME): ../Makefile diff --git a/src/makeBaseApp/top/configure/RULES.ioc b/src/makeBaseApp/top/configure/RULES.ioc new file mode 100644 index 000000000..901987c6c --- /dev/null +++ b/src/makeBaseApp/top/configure/RULES.ioc @@ -0,0 +1,2 @@ +#RULES.ioc +include $(CONFIG)/RULES.ioc diff --git a/src/makeBaseApp/top/configure/RULES_DIRS b/src/makeBaseApp/top/configure/RULES_DIRS new file mode 100644 index 000000000..3ba269dcc --- /dev/null +++ b/src/makeBaseApp/top/configure/RULES_DIRS @@ -0,0 +1,2 @@ +#RULES_DIRS +include $(CONFIG)/RULES_DIRS diff --git a/src/makeBaseApp/top/configure/RULES_TOP b/src/makeBaseApp/top/configure/RULES_TOP new file mode 100644 index 000000000..d09d668d5 --- /dev/null +++ b/src/makeBaseApp/top/configure/RULES_TOP @@ -0,0 +1,3 @@ +#RULES_TOP +include $(CONFIG)/RULES_TOP + diff --git a/src/makeBaseApp/top/exampleApp/Db/Makefile b/src/makeBaseApp/top/exampleApp/Db/Makefile new file mode 100644 index 000000000..c285af85a --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/Db/Makefile @@ -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 .db template is not named *.template add +# _TEMPLATE = + +include $(TOP)/configure/RULES +#---------------------------------------- +# ADD RULES AFTER THIS LINE + diff --git a/src/makeBaseApp/top/exampleApp/Db/dbExample1.db b/src/makeBaseApp/top/exampleApp/Db/dbExample1.db new file mode 100644 index 000000000..4f16adac9 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/Db/dbExample1.db @@ -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/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 /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 + diff --git a/src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.c b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.c new file mode 100644 index 000000000..6582b84a4 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.c @@ -0,0 +1,31 @@ +/* Example showing how to register a new command with iocsh */ + +#include + +#include +#include + +/* 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); diff --git a/src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.dbd b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.dbd new file mode 100644 index 000000000..64eb0389a --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Hello.dbd @@ -0,0 +1 @@ +registrar(helloRegister) diff --git a/src/makeBaseApp/top/exampleApp/src/_APPNAME_Main.cpp b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Main.cpp new file mode 100644 index 000000000..ae0ecb68a --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/_APPNAME_Main.cpp @@ -0,0 +1,23 @@ +/* _APPNAME_Main.cpp */ +/* Author: Marty Kraimer Date: 17MAR2000 */ + +#include +#include +#include +#include +#include + +#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); +} diff --git a/src/makeBaseApp/top/exampleApp/src/dbSubExample.c b/src/makeBaseApp/top/exampleApp/src/dbSubExample.c new file mode 100644 index 000000000..1cc748b12 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/dbSubExample.c @@ -0,0 +1,49 @@ +#include + +#include +#include +#include +#include +#include + +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); diff --git a/src/makeBaseApp/top/exampleApp/src/dbSubExample.dbd b/src/makeBaseApp/top/exampleApp/src/dbSubExample.dbd new file mode 100644 index 000000000..5f6e40ac7 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/dbSubExample.dbd @@ -0,0 +1,5 @@ +variable(mySubDebug) +function(mySubInit) +function(mySubProcess) +function(myAsubInit) +function(myAsubProcess) diff --git a/src/makeBaseApp/top/exampleApp/src/devXxxSoft.c b/src/makeBaseApp/top/exampleApp/src/devXxxSoft.c new file mode 100644 index 000000000..0507fdfd0 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/devXxxSoft.c @@ -0,0 +1,58 @@ +/* devXxxSoft.c */ +/* Example device support module */ + +#include +#include +#include +#include + +#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); +} diff --git a/src/makeBaseApp/top/exampleApp/src/initTrace.c b/src/makeBaseApp/top/exampleApp/src/initTrace.c new file mode 100644 index 000000000..50bc8e8c7 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/initTrace.c @@ -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 + +#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); diff --git a/src/makeBaseApp/top/exampleApp/src/initTrace.dbd b/src/makeBaseApp/top/exampleApp/src/initTrace.dbd new file mode 100644 index 000000000..8083c0a50 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/initTrace.dbd @@ -0,0 +1 @@ +registrar(initTraceRegister) diff --git a/src/makeBaseApp/top/exampleApp/src/sncExample.dbd b/src/makeBaseApp/top/exampleApp/src/sncExample.dbd new file mode 100644 index 000000000..df61066c3 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/sncExample.dbd @@ -0,0 +1 @@ +registrar(sncExampleRegistrar) diff --git a/src/makeBaseApp/top/exampleApp/src/sncExample.stt b/src/makeBaseApp/top/exampleApp/src/sncExample.stt new file mode 100644 index 000000000..235f3f45c --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/sncExample.stt @@ -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 + } +} diff --git a/src/makeBaseApp/top/exampleApp/src/sncProgram.st b/src/makeBaseApp/top/exampleApp/src/sncProgram.st new file mode 100644 index 000000000..1ba29893e --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/sncProgram.st @@ -0,0 +1 @@ +#include "../sncExample.stt" diff --git a/src/makeBaseApp/top/exampleApp/src/xxxRecord.c b/src/makeBaseApp/top/exampleApp/src/xxxRecord.c new file mode 100644 index 000000000..4281eb983 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/xxxRecord.c @@ -0,0 +1,273 @@ +/* xxxRecord.c */ +/* Example record support module */ + +#include +#include +#include +#include + +#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; +} diff --git a/src/makeBaseApp/top/exampleApp/src/xxxRecord.dbd b/src/makeBaseApp/top/exampleApp/src/xxxRecord.dbd new file mode 100644 index 000000000..4a71c5409 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/xxxRecord.dbd @@ -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) + } +} diff --git a/src/makeBaseApp/top/exampleApp/src/xxxSupport.dbd b/src/makeBaseApp/top/exampleApp/src/xxxSupport.dbd new file mode 100644 index 000000000..8094bdda6 --- /dev/null +++ b/src/makeBaseApp/top/exampleApp/src/xxxSupport.dbd @@ -0,0 +1,2 @@ +include "xxxRecord.dbd" +device(xxx,CONSTANT,devXxxSoft,"SoftChannel") diff --git a/src/makeBaseApp/top/exampleBoot/Makefile b/src/makeBaseApp/top/exampleBoot/Makefile new file mode 100644 index 000000000..91e47d0b5 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/Makefile @@ -0,0 +1,6 @@ +TOP = .. +include $(TOP)/configure/CONFIG +DIRS += $(wildcard *ioc*) +DIRS += $(wildcard as*) +include $(CONFIG)/RULES_DIRS + diff --git a/src/makeBaseApp/top/exampleBoot/ioc/Makefile@Common b/src/makeBaseApp/top/exampleBoot/ioc/Makefile@Common new file mode 100644 index 000000000..64a3e8bf7 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/Makefile@Common @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = _ARCH_ +TARGETS = envPaths +include $(TOP)/configure/RULES.ioc diff --git a/src/makeBaseApp/top/exampleBoot/ioc/Makefile@vxWorks b/src/makeBaseApp/top/exampleBoot/ioc/Makefile@vxWorks new file mode 100644 index 000000000..12ff7f494 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/Makefile@vxWorks @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = _ARCH_ +TARGETS = cdCommands +include $(TOP)/configure/RULES.ioc diff --git a/src/makeBaseApp/top/exampleBoot/ioc/Makefile@win32 b/src/makeBaseApp/top/exampleBoot/ioc/Makefile@win32 new file mode 100644 index 000000000..59b32d734 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/Makefile@win32 @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = _ARCH_ +TARGETS = envPaths dllPath.bat +include $(TOP)/configure/RULES.ioc diff --git a/src/makeBaseApp/top/exampleBoot/ioc/README@Common b/src/makeBaseApp/top/exampleBoot/ioc/README@Common new file mode 100644 index 000000000..f6dc23be8 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/README@Common @@ -0,0 +1,9 @@ +To start the ioc from this directory execute the command + ../../bin/_ARCH_/ 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. diff --git a/src/makeBaseApp/top/exampleBoot/ioc/README@RTEMS b/src/makeBaseApp/top/exampleBoot/ioc/README@RTEMS new file mode 100644 index 000000000..b040e848c --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/README@RTEMS @@ -0,0 +1,6 @@ +Copy the startup script (st.cmd) and top level db and dbd directories and +contents to +<>/epics/<>/ + +Then load the executable into the IOC (floppy disk, network boot, debugger, +etc.) and start it. diff --git a/src/makeBaseApp/top/exampleBoot/ioc/README@vxWorks b/src/makeBaseApp/top/exampleBoot/ioc/README@vxWorks new file mode 100644 index 000000000..e69de29bb diff --git a/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@Common b/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@Common new file mode 100644 index 000000000..da313e14a --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@Common @@ -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" diff --git a/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@RTEMS b/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@RTEMS new file mode 100644 index 000000000..4addc5c29 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@RTEMS @@ -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_") diff --git a/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@vxWorks b/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@vxWorks new file mode 100644 index 000000000..27ad0572b --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/ioc/st.cmd@vxWorks @@ -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_" diff --git a/src/makeBaseApp/top/exampleBoot/nfsCommands@RTEMS b/src/makeBaseApp/top/exampleBoot/nfsCommands@RTEMS new file mode 100644 index 000000000..0ba95a6d0 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/nfsCommands@RTEMS @@ -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 " +# +# The hostAdd command has the form: +# hostAdd("","xxx.xxx.xxx.xxx") +# +# You can also mount subdirectories as follows: +# nfsMount("", "/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") diff --git a/src/makeBaseApp/top/exampleBoot/nfsCommands@vxWorks b/src/makeBaseApp/top/exampleBoot/nfsCommands@vxWorks new file mode 100644 index 000000000..7cb8232f0 --- /dev/null +++ b/src/makeBaseApp/top/exampleBoot/nfsCommands@vxWorks @@ -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 " +# +# The hostAdd command has the form: +# hostAdd("","xxx.xxx.xxx.xxx") +# +# The nfsMount command has the form: +# nfsMount("", "/xxx/xxx/xxx", "/xxx") +# +# You can also mount subdirectories as follows: +# nfsMountAll("") +# +# 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") diff --git a/src/makeBaseApp/top/iocApp/Db/Makefile b/src/makeBaseApp/top/iocApp/Db/Makefile new file mode 100644 index 000000000..983981d40 --- /dev/null +++ b/src/makeBaseApp/top/iocApp/Db/Makefile @@ -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 /db +# databases, templates, substitutions like this +#DB += xxx.db + +#---------------------------------------------------- +# If .db template is not named *.template add +# _template = + +include $(TOP)/configure/RULES +#---------------------------------------- +# ADD RULES AFTER THIS LINE + diff --git a/src/makeBaseApp/top/iocApp/Makefile b/src/makeBaseApp/top/iocApp/Makefile new file mode 100644 index 000000000..10e0126aa --- /dev/null +++ b/src/makeBaseApp/top/iocApp/Makefile @@ -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 + diff --git a/src/makeBaseApp/top/iocApp/src/Makefile b/src/makeBaseApp/top/iocApp/src/Makefile new file mode 100644 index 000000000..de6b93a5b --- /dev/null +++ b/src/makeBaseApp/top/iocApp/src/Makefile @@ -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 + diff --git a/src/makeBaseApp/top/iocApp/src/_APPNAME_Main.cpp b/src/makeBaseApp/top/iocApp/src/_APPNAME_Main.cpp new file mode 100644 index 000000000..ae0ecb68a --- /dev/null +++ b/src/makeBaseApp/top/iocApp/src/_APPNAME_Main.cpp @@ -0,0 +1,23 @@ +/* _APPNAME_Main.cpp */ +/* Author: Marty Kraimer Date: 17MAR2000 */ + +#include +#include +#include +#include +#include + +#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); +} diff --git a/src/makeBaseApp/top/iocBoot/Makefile b/src/makeBaseApp/top/iocBoot/Makefile new file mode 100644 index 000000000..91e47d0b5 --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/Makefile @@ -0,0 +1,6 @@ +TOP = .. +include $(TOP)/configure/CONFIG +DIRS += $(wildcard *ioc*) +DIRS += $(wildcard as*) +include $(CONFIG)/RULES_DIRS + diff --git a/src/makeBaseApp/top/iocBoot/ioc/Makefile@Common b/src/makeBaseApp/top/iocBoot/ioc/Makefile@Common new file mode 100644 index 000000000..64a3e8bf7 --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/ioc/Makefile@Common @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = _ARCH_ +TARGETS = envPaths +include $(TOP)/configure/RULES.ioc diff --git a/src/makeBaseApp/top/iocBoot/ioc/Makefile@vxWorks b/src/makeBaseApp/top/iocBoot/ioc/Makefile@vxWorks new file mode 100644 index 000000000..12ff7f494 --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/ioc/Makefile@vxWorks @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = _ARCH_ +TARGETS = cdCommands +include $(TOP)/configure/RULES.ioc diff --git a/src/makeBaseApp/top/iocBoot/ioc/Makefile@win32 b/src/makeBaseApp/top/iocBoot/ioc/Makefile@win32 new file mode 100644 index 000000000..59b32d734 --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/ioc/Makefile@win32 @@ -0,0 +1,5 @@ +TOP = ../.. +include $(TOP)/configure/CONFIG +ARCH = _ARCH_ +TARGETS = envPaths dllPath.bat +include $(TOP)/configure/RULES.ioc diff --git a/src/makeBaseApp/top/iocBoot/ioc/st.cmd@Common b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@Common new file mode 100644 index 000000000..68cb61934 --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@Common @@ -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" diff --git a/src/makeBaseApp/top/iocBoot/ioc/st.cmd@Cross b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@Cross new file mode 100644 index 000000000..59e94ea2d --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@Cross @@ -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_" diff --git a/src/makeBaseApp/top/iocBoot/ioc/st.cmd@RTEMS b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@RTEMS new file mode 100644 index 000000000..f7c3d164f --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@RTEMS @@ -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_") diff --git a/src/makeBaseApp/top/iocBoot/ioc/st.cmd@vxWorks b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@vxWorks new file mode 100644 index 000000000..04db4150f --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/ioc/st.cmd@vxWorks @@ -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_" diff --git a/src/makeBaseApp/top/iocBoot/nfsCommands@RTEMS b/src/makeBaseApp/top/iocBoot/nfsCommands@RTEMS new file mode 100644 index 000000000..62221da7c --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/nfsCommands@RTEMS @@ -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 " +# +# The hostAdd command has the form: +# hostAdd("","xxx.xxx.xxx.xxx") +# +# The vxWorks nfsMount command has the form: +# nfsMount("") +# +# You can also mount subdirectories as follows: +# nfsMount("", "/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") diff --git a/src/makeBaseApp/top/iocBoot/nfsCommands@vxWorks b/src/makeBaseApp/top/iocBoot/nfsCommands@vxWorks new file mode 100644 index 000000000..62221da7c --- /dev/null +++ b/src/makeBaseApp/top/iocBoot/nfsCommands@vxWorks @@ -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 " +# +# The hostAdd command has the form: +# hostAdd("","xxx.xxx.xxx.xxx") +# +# The vxWorks nfsMount command has the form: +# nfsMount("") +# +# You can also mount subdirectories as follows: +# nfsMount("", "/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") diff --git a/src/makeBaseApp/top/supportApp/Db/Makefile b/src/makeBaseApp/top/supportApp/Db/Makefile new file mode 100644 index 000000000..983981d40 --- /dev/null +++ b/src/makeBaseApp/top/supportApp/Db/Makefile @@ -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 /db +# databases, templates, substitutions like this +#DB += xxx.db + +#---------------------------------------------------- +# If .db template is not named *.template add +# _template = + +include $(TOP)/configure/RULES +#---------------------------------------- +# ADD RULES AFTER THIS LINE + diff --git a/src/makeBaseApp/top/supportApp/Makefile b/src/makeBaseApp/top/supportApp/Makefile new file mode 100644 index 000000000..ab15bfb1c --- /dev/null +++ b/src/makeBaseApp/top/supportApp/Makefile @@ -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 diff --git a/src/makeBaseApp/top/supportApp/src/Makefile b/src/makeBaseApp/top/supportApp/src/Makefile new file mode 100644 index 000000000..941d94f8b --- /dev/null +++ b/src/makeBaseApp/top/supportApp/src/Makefile @@ -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 /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 + diff --git a/src/makeBaseApp/top/supportApp/src/_APPNAME_.dbd b/src/makeBaseApp/top/supportApp/src/_APPNAME_.dbd new file mode 100644 index 000000000..e70223b19 --- /dev/null +++ b/src/makeBaseApp/top/supportApp/src/_APPNAME_.dbd @@ -0,0 +1,6 @@ +# provide definitions such as +#include "xxxRecord.dbd" +#device(xxx,CONSTANT,devXxxSoft,"SoftChannel") +#driver(myDriver) +#registrar(myRegistrar) +#variable(myVariable) diff --git a/src/makeBaseExt/Makefile b/src/makeBaseExt/Makefile new file mode 100644 index 000000000..b4e9635d1 --- /dev/null +++ b/src/makeBaseExt/Makefile @@ -0,0 +1,32 @@ +TOP=../.. + +include $(TOP)/configure/CONFIG + +TEMPLATES_DIR = makeBaseExt + +TEMPLATES += top/Makefile +TEMPLATES += top/README +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_DIRS +TEMPLATES += top/configure/RULES_TOP +TEMPLATES += top/configure/RULES_PYTHON +TEMPLATES += top/configure/RULES_IDL + +TEMPLATES += $(subst ../,,$(wildcard ../top/configure/os/CONFIG*)) + +TEMPLATES += top/src/Makefile + +TEMPLATES += top/exampleExt/Makefile +TEMPLATES += top/exampleExt/caExample.c +TEMPLATES += top/exampleExt/RELEASE_NOTES.HTM + +TEMPLATES += top/simpleExt/Makefile + +SCRIPTS_HOST += makeBaseExt.pl + +include $(TOP)/configure/RULES + diff --git a/src/makeBaseExt/makeBaseExt.pl b/src/makeBaseExt/makeBaseExt.pl new file mode 100755 index 000000000..9773bd34b --- /dev/null +++ b/src/makeBaseExt/makeBaseExt.pl @@ -0,0 +1,309 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # makeBaseExt + +# 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::Copy; +use File::Find; +use File::Path; + +$user = GetUser(); +$cwd = cwd(); +$eEXTTYPE = $ENV{EPICS_MBE_DEF_EXT_TYPE}; +$eTOP = $ENV{EPICS_MBE_TEMPLATE_TOP}; +$eBASE = $ENV{EPICS_MBE_BASE}; + +&get_commandline_opts; # Read and check options + +$extname = "@ARGV"; + +# +# Declare two default callback routines for file copy plus two +# hook routines to add conversions +# These may be overriden within $top/$exttypename/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 "real" name (which gets the target after $top is removed) +# Empty string: Don't copy this file +sub ReplaceFilename { # (filename) + my($file) = $_[0]; + $file =~ s|.*/CVS/?.*||; # Ignore CVS files + if ($ext) { # exttypenameExt itself is dynamic, too + $file =~ s|/$exttypename|/$extdir|; + $file =~ s|/$extdir/configure|/configure/$exttype|; + } + $file =~ s|_EXTNAME_|$extname|; + $file =~ s|_EXTTYPE_|$exttype|; + # We don't want the Replace overrides + $file =~ s|.*/$extdir/Replace.pl$||; + $file = &ReplaceFilenameHook($file); # Call the user-defineable 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/_USER_/$user/o; + $line =~ s/_EPICS_BASE_/$epics_base/o; + $line =~ s/_ARCH_/$arch/o; + $line =~ s/_EXTNAME_/$extname/o; + $line =~ s/_EXTTYPE_/$exttype/o; + $line =~ s/_TEMPLATE_TOP_/$top/o; + $line = &ReplaceLineHook($line); # Call the user-defineable hook + return $line; +} + +# Source replace overrides for file copy +if (-r "$top/$exttypename/Replace.pl") { + require "$top/$exttypename/Replace.pl"; +} + +# +# Copy files and trees from (non-Ext) if not present +# +opendir TOPDIR, "$top" or die "Can't open $top: $!"; +foreach $f ( grep !/^\.\.?$|^[^\/]*(Ext)/, readdir TOPDIR ) { + if (-f "$f") { + &CopyFile("$top/$f") unless (-e "$f"); + } else { + $note = yes if ("$f" eq "src" && -e "$f"); + find(\&FCopyTree, "$top/$f") unless (-e "$f"); + } +} +closedir TOPDIR; + +# +# Create ext directories (if any names given) +# +$cwdsave = $cwd; +$cwd = "$cwd/src"; +foreach $ext ( @ARGV ) { + ($extname = $ext) =~ s/Ext$//; + $extdir = $extname; + if (-d "src/$extdir") { + print "Extention $extname is already there!\n"; + next; + } + print "Creating template structure " + . "for $extname (of type $exttypename)\n" if $Debug; + find(\&FCopyTree, "$top/$exttypename/"); + if ($note) { + print "\nNOTE: You must add the line \"DIRS += $extname\" to src/Makefile.\n\n"; + } +} +$cwd = $cwdsave; + +exit 0; # END OF SCRIPT + +# +# Get commandline options and check for validity +# +sub get_commandline_opts { #no args + ($len = @ARGV) and getopts("ldit:T:b:a:") or Cleanup(1); + +# Debug option + $Debug = 1 if $opt_d; + +# Locate epics_base + my ($command) = UnixPath($0); + if ($opt_b) { # first choice is -b base + $epics_base = UnixPath($opt_b); + } elsif (-r "configure/RELEASE") { # second choice is configure/RELEASE + open(IN, "configure/RELEASE") or die "Cannot open configure/RELEASE"; + while () { + chomp; + s/EPICS_BASE\s*=\s*// and $epics_base = UnixPath($_), break; + } + close IN; + } elsif ($eBASE) { # third choice is env var EPICS_MBE_BASE + $epics_base = UnixPath($eBASE); + } elsif ($command =~ m|/bin/|) { # assume script was called with full path to base + $epics_base = $command; + $epics_base =~ s|(/.*)/bin/.*makeBaseExt.*|$1|; + } + "$epics_base" or Cleanup(1, "Cannot find EPICS base"); + +# Locate template top directory + if ($opt_T) { # first choice is -T templ-top + $top = UnixPath($opt_T); + } elsif (-r "configure/RELEASE") { # second choice is configure/RELEASE + open(IN, "configure/RELEASE") or die "Cannot open configure/RELEASE"; + while () { + chomp; + s/TEMPLATE_TOP\s*=\s*// and $top = UnixPath($_), break; + } + close IN; + } + if("$top" eq "") { + if ($eTOP) { # third choice is $ENV{EPICS_MBE_TEMPL_TOP} + $top = UnixPath($eTOP); + } else { # use templates from EPICS base + $top = $epics_base . "/templates/makeBaseExt/top"; + } + } + "$top" or Cleanup(1, "Cannot find template top directory"); + +# Print extension type list? + if ($opt_l) { + &ListExtTypes; + exit 0; # finished for -l command + } + +# Extention template type + if ($opt_t) { # first choice is -t type + $exttype = $opt_t; + } elsif ($eEXTTYPE) { # second choice is $ENV{EPICS_DEFAULT_EXT_TYPE} + $exttype = $eEXTTYPE; + } elsif (-r "$top/defaultExt") {# third choice is (a link) in the $top dir + $exttype = "default"; + } elsif (-r "$top/exampleExt") {# fourth choice is (a link) in the $top dir + $exttype = "example"; + } + $exttype =~ s/Ext$//; + "$exttype" or Cleanup(1, "Cannot find default extension type"); + $exttypename = $exttype . "Ext"; + +# Valid $exttypename? + unless (-r "$top/$exttypename") { + print "Template for extension type '$exttype' is unreadable or does not exist.\n"; + &ListExtTypes; + exit 1; + } + + print "\nCommand line / environment options validated:\n" + . " Templ-Top: $top\n" + . "Templ-Type: $exttype\n" + . "Templ-Name: $exttypename\n" + . "EPICS-Base: $epics_base\n\n" if $Debug; + +} + +# +# List extension types +# +sub ListExtTypes { # no args + print "Valid extension types are:\n"; + foreach $name (<$top/*Ext>) { + $name =~ s|$top/||; + $name =~ s|Ext||; + printf "\t$name\n" if ($name && -r "$top/$name" . "Ext"); + } +} + +# +# Copy a file with replacements +# +sub CopyFile { # (source) + $source = $_[0]; + $target = &ReplaceFilename($source); + + if ($target) { + $target =~ s|$top/||; + open(INP, "<$source") and open(OUT, ">$target") + or die "$! Copying $source -> $target"; + + print "Copying file $source -> $target\n" if $Debug; + while () { + print OUT &ReplaceLine($_); + } + close INP; close OUT; + } +} + +# +# Find() callback for file or structure copy +# +sub FCopyTree { + chdir $cwd; # Sigh + if (-d $File::Find::name + and ($dir = &ReplaceFilename($File::Find::name))) { + $dir =~ s|$top/||; + print "Creating directory $dir\n" if $Debug; + &mkpath($dir); + } else { + &CopyFile($File::Find::name); + } + chdir $File::Find::dir; +} + +# +# Cleanup and exit +# +sub Cleanup { # (return-code [ messsage-line1, line 2, ... ]) + my ($rtncode, @message) = @_; + + foreach $line ( @message ) { + print "$line\n"; + } + + print </bin//makeBaseExt.pl -t example example + +EOF + + exit $rtncode; +} + +sub GetUser { # no args + my ($user); + + # add to this list if new possibilities arise, + # currently it's UNIX and WIN32: + $user = $ENV{USER} || $ENV{USERNAME} || Win32::LoginName(); + + unless ($user) { + print "I cannot figure out your user name.\n"; + print "What shall you be called ?\n"; + print ">"; + $user = ; + chomp $user; + } + die "No user name" unless $user; + return $user; +} + +# replace "\" by "/" (for WINxx) +sub UnixPath { # path + my($newpath) = $_[0]; + $newpath =~ s|\\|/|go; + return $newpath; +} diff --git a/src/makeBaseExt/top/Makefile b/src/makeBaseExt/top/Makefile new file mode 100644 index 000000000..afde8dea2 --- /dev/null +++ b/src/makeBaseExt/top/Makefile @@ -0,0 +1,9 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +TOP = . +include $(TOP)/configure/CONFIG +DIRS += configure +DIRS += src +include $(TOP)/configure/RULES_TOP + + diff --git a/src/makeBaseExt/top/README b/src/makeBaseExt/top/README new file mode 100644 index 000000000..c0e91eb9c --- /dev/null +++ b/src/makeBaseExt/top/README @@ -0,0 +1,5 @@ +Notes: + +Each time you add a new extension in the src directory you +must add the extension directory name to src/Makefile. + diff --git a/src/makeBaseExt/top/configure/CONFIG b/src/makeBaseExt/top/configure/CONFIG new file mode 100644 index 000000000..aa00a53c2 --- /dev/null +++ b/src/makeBaseExt/top/configure/CONFIG @@ -0,0 +1,48 @@ +# Revision-Id: jba@aps.anl.gov-20101109205513-6qg5s6cvmoy9oysd + +# You might want to change this to some shared set of rules, e.g. +# RULES=/path/to/epics/support/modules/rules/x-y +RULES=$(EPICS_BASE) + +INSTALL_IDLFILE = $(INSTALL) + +include $(TOP)/configure/RELEASE +-include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH) +-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 for definition in base +INSTALL_LOCATION = $(TOP) +include $(TOP)/configure/CONFIG_SITE + +ifdef INSTALL_LOCATION_EXTENSIONS +INSTALL_LOCATION = $(INSTALL_LOCATION_EXTENSIONS) +endif + +# Site specific host architecture definitions +-include $(TOP)/configure/os/CONFIG_SITE.$(EPICS_HOST_ARCH).Common + +ifdef T_A + +# Site specific target architecture definitions +-include $(TOP)/configure/os/CONFIG_SITE.Common.$(T_A) + +# Cross compile specific definitions +ifneq ($(EPICS_HOST_ARCH),$(T_A)) +-include $(TOP)/configure/CONFIG.CrossCommon +endif + +# Site specific host-target combination definitions +-include $(TOP)/configure/os/CONFIG.$(EPICS_HOST_ARCH).$(T_A) +-include $(TOP)/configure/os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) + +-include $(TOP)/configure/O.$(T_A)/CONFIG_APP_INCLUDE + +endif + diff --git a/src/makeBaseExt/top/configure/CONFIG_SITE b/src/makeBaseExt/top/configure/CONFIG_SITE new file mode 100644 index 000000000..4ef88cedf --- /dev/null +++ b/src/makeBaseExt/top/configure/CONFIG_SITE @@ -0,0 +1,20 @@ +# CONFIG_SITE +# +# Make any extensions-specific changes to the EPICS build +# configuration variables in this file. +# +# Host/target specific settings are in os subdirectory files named +# os/CONFIG_SITE.$(EPICS_HOST_ARCH).Common +# os/CONFIG_SITE.Common.$(T_A) +# os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) + +# Extensions are not normally built for cross targets +CROSS_COMPILER_TARGET_ARCHS = + +# If you don't want to install into $(TOP) then +# define INSTALL_LOCATION here +#INSTALL_LOCATION= + +# Extensions don't normally build shared libraries +SHARED_LIBRARIES = NO + diff --git a/src/makeBaseExt/top/configure/Makefile b/src/makeBaseExt/top/configure/Makefile new file mode 100644 index 000000000..7bf19a3c3 --- /dev/null +++ b/src/makeBaseExt/top/configure/Makefile @@ -0,0 +1,10 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +TOP=.. + +include $(TOP)/configure/CONFIG + +TARGETS = $(CONFIG_TARGETS) + +include $(TOP)/configure/RULES + diff --git a/src/makeBaseExt/top/configure/RELEASE b/src/makeBaseExt/top/configure/RELEASE new file mode 100644 index 000000000..cb4c9cce2 --- /dev/null +++ b/src/makeBaseExt/top/configure/RELEASE @@ -0,0 +1,22 @@ +#RELEASE Location of external products +# +# NOTE: The build does not check dependancies on files +# external to this application. Thus you should run +# "gnumake clean uninstall install" in the top directory +# each time EPICS_BASE, SNCSEQ, or any other external +# module defined in a RELEASE* file is rebuilt. +# +# Host/target specific settings can be specified in files named +# RELEASE.$(EPICS_HOST_ARCH).Common +# RELEASE.Common.$(T_A) +# RELEASE.$(EPICS_HOST_ARCH).$(T_A) + +# Define INSTALL_LOCATION in CONFIG_SITE + +# Location of external products +EPICS_BASE=_EPICS_BASE_ +EPICS_EXTENSIONS = $(TOP) + +# OAG_APPS may be needed by extension SDDS +#OAG_APPS=$(TOP)/../../oag/apps + diff --git a/src/makeBaseExt/top/configure/RULES b/src/makeBaseExt/top/configure/RULES new file mode 100644 index 000000000..678d4f322 --- /dev/null +++ b/src/makeBaseExt/top/configure/RULES @@ -0,0 +1,5 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +include $(CONFIG)/RULES +include $(TOP)/configure/RULES_PYTHON +include $(TOP)/configure/RULES_IDL diff --git a/src/makeBaseExt/top/configure/RULES_DIRS b/src/makeBaseExt/top/configure/RULES_DIRS new file mode 100644 index 000000000..ced7a2973 --- /dev/null +++ b/src/makeBaseExt/top/configure/RULES_DIRS @@ -0,0 +1,3 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +include $(CONFIG)/RULES_DIRS diff --git a/src/makeBaseExt/top/configure/RULES_IDL b/src/makeBaseExt/top/configure/RULES_IDL new file mode 100644 index 000000000..ca4b9e2cc --- /dev/null +++ b/src/makeBaseExt/top/configure/RULES_IDL @@ -0,0 +1,30 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +ifdef T_A +ifeq ($(findstring Host,$(VALID_BUILDS)),Host) + +INSTALL_IDL = $(INSTALL_LOCATION)/idllib +DIRECTORY_TARGETS += $(INSTALL_IDL) + +ifneq ($(strip $(IDLS_$(OS_CLASS))),) +IDLS += $(subst -nil-,,$(IDLS_$(OS_CLASS))) +else +ifdef IDLS_DEFAULT +IDLS += $(IDLS_DEFAULT) +endif +endif + +INSTALL_IDLS =$(IDLS:%=$(INSTALL_IDL)/%) + +buildInstall : $(INSTALL_IDLS) + +$(INSTALL_IDL)/%: % + @echo "Installing idl program $@" + @$(INSTALL_IDLFILE) -d -m 644 $< $(INSTALL_IDL) + +$(INSTALL_IDL)/%: ../% + @echo "Installing idl program $@" + @$(INSTALL_IDLFILE) -d -m 644 $< $(INSTALL_IDL) + +endif +endif diff --git a/src/makeBaseExt/top/configure/RULES_PYTHON b/src/makeBaseExt/top/configure/RULES_PYTHON new file mode 100644 index 000000000..4e35292bd --- /dev/null +++ b/src/makeBaseExt/top/configure/RULES_PYTHON @@ -0,0 +1,50 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +ifdef T_A +ifeq ($(findstring Host,$(VALID_BUILDS)),Host) + +SWIG ?= swig + +vpath %.py $(USR_VPATH) $(ALL_SRC_DIRS) + +# The optional PYTHON_INSTALL_LOCATION environment variable +# should be set to /site-packages +PYTHON_INSTALL_LOCATION ?= $(INSTALL_LOCATION)/lang/python + +INSTALL_PYTHON = $(PYTHON_INSTALL_LOCATION)/$(PYTHON_PACKAGE) + +PYTHON_PACKAGE_PTH = $(addsuffix .pth,$(PYTHON_PACKAGE)) +INSTALL_PYTHON_PACKAGE_PTH = $(addprefix $(PYTHON_INSTALL_LOCATION)/,$(PYTHON_PACKAGE_PTH)) + +PYTHON_SCRIPTS = $(filter-out $(LOADABLE_LIBRARY),$(PYTHON_MODULES)) +PYTHON_LIBRARY = $(filter $(LOADABLE_LIBRARY),$(PYTHON_MODULES)) +PYTHON_SHRLIBNAME = $(PYTHON_LIBRARY:%=$(LOADABLE_SHRLIB_PREFIX)%$(LOADABLE_SHRLIB_SUFFIX)) + +INSTALL_PYTHONS = $(addprefix $(INSTALL_PYTHON)/,$(PYTHON_SCRIPTS)) +INSTALL_PYTHONS += $(addprefix $(INSTALL_PYTHON)/,$(PYTHON_SHRLIBNAME)) + +buildInstall: $(INSTALL_PYTHONS) $(INSTALL_PYTHON_PACKAGE_PTH) + +$(INSTALL_PYTHON)/%: % + @echo "Installing python modules $@" + @$(INSTALL) -d -m 644 $< $(INSTALL_PYTHON) + +$(INSTALL_PYTHON)/%: ../% + @echo "Installing python modules $@" + @$(INSTALL) -d -m 644 $< $(INSTALL_PYTHON) + +$(PYTHON_INSTALL_LOCATION)/%: % + @echo "Installing python pth file $@" + @$(INSTALL) -d -m 644 $< $(PYTHON_INSTALL_LOCATION) + +$(PYTHON_PACKAGE_PTH): + @echo $(PYTHON_PACKAGE) > $@ + +%_wrap.c: ../%.i + $(SWIG) -python -o $@ $< + +clean:: + @$(RM) *.py *.so *.pth + +endif +endif diff --git a/src/makeBaseExt/top/configure/RULES_TOP b/src/makeBaseExt/top/configure/RULES_TOP new file mode 100644 index 000000000..d3910e264 --- /dev/null +++ b/src/makeBaseExt/top/configure/RULES_TOP @@ -0,0 +1,4 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +include $(CONFIG)/RULES_TOP + diff --git a/src/makeBaseExt/top/configure/os/CONFIG.linux-x86.linux-386 b/src/makeBaseExt/top/configure/os/CONFIG.linux-x86.linux-386 new file mode 100644 index 000000000..ed73b3464 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG.linux-x86.linux-386 @@ -0,0 +1,2 @@ + +include $(TOP)/configure/os/CONFIG_SITE.linux-x86.linux-x86 diff --git a/src/makeBaseExt/top/configure/os/CONFIG.win32-x86-borland.win32-x86-borland b/src/makeBaseExt/top/configure/os/CONFIG.win32-x86-borland.win32-x86-borland new file mode 100644 index 000000000..4176b6e75 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG.win32-x86-borland.win32-x86-borland @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.win32-x86.win32-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.Common.Common b/src/makeBaseExt/top/configure/os/CONFIG_SITE.Common.Common new file mode 100644 index 000000000..ce096d191 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.Common.Common @@ -0,0 +1,12 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# java jdk definitions +#CLASSPATH += -classpath .:..:$(INSTALL_JAVA):$(JAVA_DIR)/bin/../classes:$(JAVA_DIR)/bin/../lib/classes.zip +#JAVACCMD = $(subst \,/,$(JAVA_DIR)/bin/javac$(EXE) $(CLASSPATH) $(JAVAC_FLAGS) ) +#JAVAHCMD = $(subst \,/,$(JAVA_DIR)/bin/javah$(EXE) -jni $(CLASSPATH) $(JAVAH_FLAGS)) +#JARCMD = $(subst \,/,$(JAVA_DIR)/bin/jar$(EXE) $(JAR_OPTIONS) $(MANIFEST) $(JAR) $(JAR_INPUT)) + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc-gnu.aix-ppc-gnu b/src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc-gnu.aix-ppc-gnu new file mode 100644 index 000000000..a87cb6064 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc-gnu.aix-ppc-gnu @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.aix-ppc.aix-ppc + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc.aix-ppc b/src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc.aix-ppc new file mode 100644 index 000000000..5412e82b7 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.aix-ppc.aix-ppc @@ -0,0 +1,79 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +# sun X11 +X11_LIB = /usr/openwin/lib +X11_INC = /usr/openwin/include +# mit X11 +#X11_LIB = /opt/X11R5/lib +#X11_INC = /opt/X11R5/include +# osf motif +#MOTIF_INC = /opt/local/Motif2.0/include +#MOTIF_LIB = /opt/local/Motif2.0/lib +# sun SDK motif +#MOTIF_INC = /opt/SUNWmotif/include +#MOTIF_LIB = /opt/SUNWmotif/lib +# sun 5.4,5.6 SDK motif +MOTIF_INC = /usr/dt/include +MOTIF_LIB = /usr/dt/lib +OPENWIN = /usr/openwin + +INTERVIEWS_BIN=/usr/local/interviews/bin/O.solaris +INTERVIEWS_LIB=/usr/local/interviews/lib/O.solaris +IV_INC=/usr/local/interviews/include +IV-2_6_INC=/usr/local/interviews/include/IV-2_6 +IV_BIN=/usr/local/interviews/bin/O.solaris +IV_LIB=/usr/local/interviews/lib/O.solaris + +WINGZ_INC = /opt/local/Wingz2/incl +WINGZ_LIB = /opt/local/Wingz2/lib +#MATHEMATICA = /usr/local/math +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +XRTGRAPH_EXTENSIONS = YES +XRTGRAPH = /opt/local/xrtgraph +QUESTWIN = /usr/local/questwin + +TK_LIB = /opt/local/lib +TK_INC = /opt/local/include +TCL_LIB = /opt/local/lib +TCL_INC = /opt/local/include +DP_LIB = /opt/local/lib +DP_INC = /opt/local/include +BLT_LIB = /opt/local/lib +BLT_INC = /opt/local/include + +IDL = /usr/local/idl +# IDL=$(IDL)/external/rpc is the sun4 version +IDLRPC = $(IDL)/external/rpc.solaris + +ZLIB_PREFIX = /usr/local/oag +ZLIB = $(notdir $(wildcard $(ZLIB_PREFIX)/lib/libz.a)) +ifeq ($(ZLIB) , libz.a) +ZLIB_LIB = $(ZLIB_PREFIX)/lib +ZLIB_INC = -I$(ZLIB_PREFIX)/include +ZLIB_CFLAG = -DzLib +ZLIB_PROD_LIB = z +z_DIR=$(ZLIB_PREFIX)/lib +else +ZLIB_LIB = +ZLIB_INC = +ZLIB_CFLAG = +ZLIB_PROD_LIB = +z_DIR= +endif + +PYTHON_DIR=/opt/local/lib/python2.2 +PYTHON_INCLUDE=/opt/local/include/python2.2 + +JAVA_DIR=/usr/java diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 new file mode 100644 index 000000000..ad07e5de3 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 @@ -0,0 +1,56 @@ +# +# Revision-Id: jba@aps.anl.gov-20101109213708-vcvrlsgrj9nv2lmp +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common +-include $(TOP)/configure/os/CONFIG_SITE.win32-x86.win32-x86 + +# ---------- java definitions +JAVA_DIR=c:/j2sdk1.4.1_01 + +# ---------- tcl/tk definitions +TCL = c:\\Tcl +TK_LIB = $(TCL)/lib +TK_INC = $(TCL)/include +TCL_LIB = $(TCL)/lib +TCL_INC = $(TCL)/include +DP_LIB = $(TCL)/lib +DP_INC = $(TCL)/include +BLT_LIB = $(TCL)/lib +BLT_INC = $(TCL)/include + +IDL = /usr/local/idl +# IDL=$(IDL)/external/rpc is the sun4 version +IDLRPC = $(IDL)/external/rpc.solaris + +#X11_LIB = c:/cygwin/lib +#X11_INC = c:/cygwin/include + +#MOTIF_LIB = $(X11_LIB) +#MOTIF_INC = $(X11_INC) + +OPENWIN = +INTERVIEWS_BIN = +WINGZ_INC = +WINGZ_LIB = +MATHEMATICA = +QUESTWIN = + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +XRTGRAPH_EXTENSIONS = NO +XRTGRAPH = + +SCIPLOT = ../../src/medmdev/medm + +# z library created in SDDS extension +ZLIB_CFLAG = -DzLib +ZLIB_PROD_LIB = z +z_DIR = $(EPICS_EXTENSIONS_LIB) diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppc.darwin-ppc b/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppc.darwin-ppc new file mode 100644 index 000000000..ac3f86ab5 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppc.darwin-ppc @@ -0,0 +1,83 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +PYTHON_DIR=/usr/lib/python2.2 +PYTHON_INCLUDE=/System/Library/Frameworks/Python.framework/Versions/2.3/include/python2.3 + +# Following 3 for SDDS +#USE_GD_LIBRARY=1 +#USE_RUNCONTROL=1 +#USE_LOGDAEMON=1 + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +# +# XDarwin +# +X11_LIB=/usr/X11R6/lib +X11_INC=/usr/X11R6/include/X11 +XPM_LIB=/usr/X11R6/lib +XPM_INC=/usr/X11R6/include/X11 + +# +# Fink OpenMotif +# +MOTIF_LIB=/sw/lib +MOTIF_INC=/sw/include + +# +# DarwinPorts OpenMotif +# +#MOTIF_LIB=/opt/local/lib +#MOTIF_INC=/opt/local/include + +# +# Interviews +# +IV_INC= +IV-2_6_INC= +IV_BIN= +IV_LIB= + +OPENWIN = +WINGZ_INC = +WINGZ_LIB = +MATHEMATICA = + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +#XRTGRAPH_EXTENSIONS = YES +#XRTGRAPH = /opt/local/xrtgraph + +SCIPLOT = YES +QUESTWIN = + +TK_LIB = /sw/lib +TK_INC = /sw/include +TCL_LIB = /sw/lib +TCL_INC = /sw/include +DP_LIB = /sw/lib +DP_INC = /sw/include +BLT_LIB = /sw/lib +BLT_INC = /sw/include + +# +# Preliminary JAVA support +# +JAVA_DIR=/System/Library/Frameworks/JavaVM.framework/Home +JAVA_INC=/System/Library/Frameworks/JavaVM.framework/Headers +JAVA_INCLUDES = -I$(JAVA_INC) + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppcx86.darwin-ppcx86 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppcx86.darwin-ppcx86 new file mode 100644 index 000000000..da9b8591f --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-ppcx86.darwin-ppcx86 @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.darwin-ppc.darwin-ppc + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 new file mode 100644 index 000000000..0ad7a7124 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.darwin-x86.darwin-x86 @@ -0,0 +1,83 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +PYTHON_DIR=/usr/lib/python2.2 +PYTHON_INCLUDE=/System/Library/Frameworks/Python.framework/Versions/2.3/include/python2.3 + +# Following 3 for SDDS +#USE_GD_LIBRARY=1 +#USE_RUNCONTROL=1 +#USE_LOGDAEMON=1 + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +# +# XDarwin +# +X11_LIB=/usr/X11R6/lib +X11_INC=/usr/X11R6/include/X11 +XPM_LIB=/usr/X11R6/lib +XPM_INC=/usr/X11R6/include/X11 + +# +# Fink OpenMotif +# +#MOTIF_LIB=/sw/lib +#MOTIF_INC=/sw/include + +# +# DarwinPorts OpenMotif +# +MOTIF_LIB=/opt/local/lib +MOTIF_INC=/opt/local/include + +# +# Interviews +# +IV_INC= +IV-2_6_INC= +IV_BIN= +IV_LIB= + +OPENWIN = +WINGZ_INC = +WINGZ_LIB = +MATHEMATICA = + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +#XRTGRAPH_EXTENSIONS = YES +#XRTGRAPH = /opt/local/xrtgraph + +SCIPLOT = YES +QUESTWIN = + +TK_LIB = /sw/lib +TK_INC = /sw/include +TCL_LIB = /sw/lib +TCL_INC = /sw/include +DP_LIB = /sw/lib +DP_INC = /sw/include +BLT_LIB = /sw/lib +BLT_INC = /sw/include + +# +# Preliminary JAVA support +# +JAVA_DIR=/System/Library/Frameworks/JavaVM.framework/Home +JAVA_INC=/System/Library/Frameworks/JavaVM.framework/Headers +JAVA_INCLUDES = -I$(JAVA_INC) + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 new file mode 100644 index 000000000..2054ecec2 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.freebsd-x86_64.freebsd-x86_64 @@ -0,0 +1,12 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc-gnu.hpux-parisc-gnu b/src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc-gnu.hpux-parisc-gnu new file mode 100644 index 000000000..cec9f321e --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc-gnu.hpux-parisc-gnu @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.hpux-parisc.hpux-parisc + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc.hpux-parisc b/src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc.hpux-parisc new file mode 100644 index 000000000..76b51623c --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.hpux-parisc.hpux-parisc @@ -0,0 +1,30 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +# Where to find utilities/libraries +X11_INC = /usr/include/X11R5 +X11_LIB = /usr/lib/X11R5 +MOTIF_INC = /usr/include/Motif1.2 +MOTIF_LIB = /usr/lib/Motif1.2 +INTERVIEWS_BIN=/usr/local/interviews/bin/O.hp700 +XRTGRAPH = /usr/csite/xrt +OPENWIN = /usr/csite4/local/xview +WINGZ_INC = /usr/local/Wingz2/incl +WINGZ_LIB = /usr/local/Wingz2/lib +MATHEMATICA = /usr/local/math +QUESTWIN = /usr/local/questwin +IDL = /usr/csite/idl +#JAVA_DIR=/usr/java/j2sdk1.4.1_01 +JAVA_DIR=/usr/local/java +JAVA_INC=$(JAVA_DIR)/include + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.interix-x86.interix-x86 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.interix-x86.interix-x86 new file mode 100644 index 000000000..5be2e0046 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.interix-x86.interix-x86 @@ -0,0 +1,11 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc new file mode 100644 index 000000000..5ad5aad5a --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-ppc.linux-ppc @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.linux-x86.linux-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-borland.linux-x86-borland b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-borland.linux-x86-borland new file mode 100644 index 000000000..f740560ba --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-borland.linux-x86-borland @@ -0,0 +1,41 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +X11_LIB=/usr/X11R6/lib +X11_INC=/usr/X11R6/include/X11 +MOTIF_LIB=/usr/X11R6/lib +MOTIF_INC=/usr/X11R6/include +#X11_INC=/local2/X11R6/include/X11 +#X11_LIB=/local2/X11R6/lib +#MOTIF_LIB=/local2/X11R6/lib +#MOTIF_INC=/local2/X11R6/include + +OPENWIN = +WINGZ = +MATHEMATICA = +SCIPLOT=YES +XRTGRAPH = +QUESTWIN = +TK_TCL = /usr/lib +IDL = + +INTERVIEWS_BIN=/usr/local/interviews/bin/O.Linux + +IV_INC=/usr/local/interviews/include +IV_BIN=/usr/local/interviews/bin/O.Linux +IV_LIB=/usr/local/interviews/lib/O.Linux + +PYTHON_DIR=/usr/lib/python2.2 +PYTHON_INCLUDE=/usr/include/python2.2 + +JAVA_DIR=c:/j2sdk1.4.1_01 diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug new file mode 100644 index 000000000..5ad5aad5a --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86-debug.linux-x86-debug @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.linux-x86.linux-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-athlon b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-athlon new file mode 100644 index 000000000..4f438aae9 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-athlon @@ -0,0 +1,14 @@ + +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + + +-include $(TOP)/configure/os/CONFIG_SITE.linux-x86.linux-x86 diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 new file mode 100644 index 000000000..86e630da4 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86.linux-x86 @@ -0,0 +1,59 @@ +# +# Revision-Id: jba@aps.anl.gov-20101109205513-6qg5s6cvmoy9oysd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +#X11_LIB=/usr/X11R6/lib +#X11_INC=/usr/X11R6/include +#MOTIF_LIB=/usr/X11R6/lib +#MOTIF_INC=/usr/X11R6/include + +#With the release of Fedora Core 5 the +#X11 and MOTIF libraries now live at /usr/lib +X11_LIB=/usr/lib +X11_INC=/usr/include +MOTIF_LIB=/usr/lib +MOTIF_INC=/usr/include + +OPENWIN = +WINGZ = +MATHEMATICA = +QUESTWIN = +TK_TCL = /usr/lib +IDL = + +#JAVA_DIR=/usr/local/java +#JAVA_DIR=/usr/java/j2sdk1.4.1 +JAVA_DIR=/usr +JAVA_INC=$(JAVA_DIR)/include + +INTERVIEWS_BIN=/usr/local/interviews/bin/O.Linux + +IV_INC=/usr/local/interviews/include +IV_BIN=/usr/local/interviews/bin/O.Linux +IV_LIB=/usr/local/interviews/lib/O.Linux + +PYTHON_DIR=/usr/lib/python2.2 +PYTHON_INCLUDE=/usr/include/python2.2 + +CLAPACK_LIB = /usr/local/oag/3rdParty/CLAPACK/Linux_P4SSE2/lib +ATLAS_LIB = /usr/local/oag/3rdParty/ATLAS/Linux_P4SSE2/lib +F2C_LIB = /usr/local/oag/3rdParty/F2C/Linux_P4SSE2/lib +CLAPACK_INCLUDE = /usr/local/oag/3rdParty/CLAPACK/Linux_P4SSE2/include +ATLAS_INCLUDE = /usr/local/oag/3rdParty/ATLAS/Linux_P4SSE2/include + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +#XRTGRAPH_EXTENSIONS = YES +#XRTGRAPH = /usr/local/xrtgraph +# Define SCIPLOT = YES if no XRT/graph 3.x +SCIPLOT=YES + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug new file mode 100644 index 000000000..5ad5aad5a --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64-debug.linux-x86_64-debug @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.linux-x86.linux-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 new file mode 100644 index 000000000..861a86829 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.linux-x86_64.linux-x86_64 @@ -0,0 +1,36 @@ +# +# Revision-Id: jba@aps.anl.gov-20101109205513-6qg5s6cvmoy9oysd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.linux-x86.linux-x86 + +X11_LIB=/usr/lib64 +X11_INC=/usr/include + +# OpenMotif location +MOTIF_LIB=/usr/lib64 +MOTIF_INC=/usr/include +#MOTIF_LIB=/usr/X11R6/lib64 +#MOTIF_INC=/usr/X11R6/include + +# testing pre-compiled versions +CLAPACK_LIB = /usr/local/oag/3rdParty/ATLAS/Linux_Core2Duo64SSE3/lib +ATLAS_LIB = /usr/local/oag/3rdParty/ATLAS/Linux_Core2Duo64SSE3/lib +ATLAS_INCLUDE = /usr/local/oag/3rdParty/ATLAS/Linux_Core2Duo64SSE3/include + +JAVA_DIR=/usr + +# Sciplot and XRTgraph used by medm +SCIPLOT=YES +#XRTGRAPH_EXTENSIONS = YES +#XRTGRAPH = /usr/local/xrtgraph64 +XRTGRAPH_EXTENSIONS = +XRTGRAPH = + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug new file mode 100644 index 000000000..a691e5464 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-debug.solaris-sparc-debug @@ -0,0 +1,14 @@ + +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-gnu.solaris-sparc-gnu b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-gnu.solaris-sparc-gnu new file mode 100644 index 000000000..ae16d24d6 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc-gnu.solaris-sparc-gnu @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc new file mode 100644 index 000000000..1f690da59 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc @@ -0,0 +1,92 @@ +# +# Revision-Id: jba@aps.anl.gov-20101109205513-6qg5s6cvmoy9oysd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +# sun X11 +X11_LIB = /usr/openwin/lib +X11_INC = /usr/openwin/include +# mit X11 +#X11_LIB = /opt/X11R5/lib +#X11_INC = /opt/X11R5/include +# osf motif +#MOTIF_INC = /opt/local/Motif2.0/include +#MOTIF_LIB = /opt/local/Motif2.0/lib +# sun SDK motif +#MOTIF_INC = /opt/SUNWmotif/include +#MOTIF_LIB = /opt/SUNWmotif/lib +# sun 5.4,5.6 SDK motif +MOTIF_INC = /usr/dt/include +MOTIF_LIB = /usr/dt/lib +OPENWIN = /usr/openwin + +INTERVIEWS_BIN=/usr/local/interviews/bin/O.solaris +INTERVIEWS_LIB=/usr/local/interviews/lib/O.solaris +IV_INC=/usr/local/interviews/include +IV-2_6_INC=/usr/local/interviews/include/IV-2_6 +IV_BIN=/usr/local/interviews/bin/O.solaris +IV_LIB=/usr/local/interviews/lib/O.solaris + +WINGZ_INC = /opt/local/Wingz2/incl +WINGZ_LIB = /opt/local/Wingz2/lib +#MATHEMATICA = /usr/local/math + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +#XRTGRAPH_EXTENSIONS = YES +#XRTGRAPH = /opt/local/xrtgraph +SCIPLOT = YES + +QUESTWIN = /usr/local/questwin + +TK_LIB = /opt/local/lib +TK_INC = /opt/local/include +TCL_LIB = /opt/local/lib +TCL_INC = /opt/local/include +DP_LIB = /opt/local/lib +DP_INC = /opt/local/include +BLT_LIB = /opt/local/lib +BLT_INC = /opt/local/include + +IDL = /usr/local/idl +# IDL=$(IDL)/external/rpc is the sun4 version +IDLRPC = $(IDL)/external/rpc.solaris + +# sun 5.4 +#JAVA_DIR=/opt/local/java +# sun 5.6 +JAVA_DIR=/usr/java +JAVA_INC=$(JAVA_DIR)/include + +ZLIB_PREFIX = /usr/local/oag +ZLIB = $(notdir $(wildcard $(ZLIB_PREFIX)/lib/libz.a)) +ifeq ($(ZLIB) , libz.a) +ZLIB_LIB = $(ZLIB_PREFIX)/lib +ZLIB_INC = -I$(ZLIB_PREFIX)/include +ZLIB_CFLAG = -DzLib +ZLIB_PROD_LIB = z +z_DIR=$(ZLIB_PREFIX)/lib +else +ZLIB_LIB = +ZLIB_INC = +ZLIB_CFLAG = +ZLIB_PROD_LIB = +z_DIR= +endif + +PYTHON_DIR=/opt/local/lib/python2.2 +PYTHON_INCLUDE=/opt/local/include/python2.2 + +CLAPACK_LIB = /usr/local/oag/3rdParty/CLAPACK/SunOS_SunUS5/lib +ATLAS_LIB = /usr/local/oag/3rdParty/ATLAS/SunOS_SunUS5/lib +F2C_LIB = /usr/local/oag/3rdParty/F2C/SunOS_SunUS5/lib +CLAPACK_INCLUDE = /usr/local/oag/3rdParty/CLAPACK/SunOS_SunUS5/include +ATLAS_INCLUDE = /usr/local/oag/3rdParty/ATLAS/SunOS_SunUS5/include diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64-gnu.solaris-sparc64-gnu b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64-gnu.solaris-sparc64-gnu new file mode 100644 index 000000000..8946afc9e --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64-gnu.solaris-sparc64-gnu @@ -0,0 +1,14 @@ + +# +# Revision-Id: jba@aps.anl.gov-20101109205513-6qg5s6cvmoy9oysd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-sparc64.solaris-sparc64 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64.solaris-sparc64 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64.solaris-sparc64 new file mode 100644 index 000000000..1448f6811 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-sparc64.solaris-sparc64 @@ -0,0 +1,26 @@ +# +# Revision-Id: jba@aps.anl.gov-20101109205513-6qg5s6cvmoy9oysd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc + +FORCE_ZLIB_BUILD=YES + +CLAPACK_LIB = /usr/local/oag/3rdParty/CLAPACK/SunOS_SunUSIII_4/lib +ATLAS_LIB = /usr/local/oag/3rdParty/ATLAS/SunOS_SunUSIII_4/lib +F2C_LIB = /usr/local/oag/3rdParty/F2C/SunOS_SunUSIII_4/lib +CLAPACK_INCLUDE = /usr/local/oag/3rdParty/CLAPACK/SunOS_SunUSIII_4/include +ATLAS_INCLUDE = /usr/local/oag/3rdParty/ATLAS/SunOS_SunUSIII_4/include + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +#XRTGRAPH_EXTENSIONS = +#XRTGRAPH = +SCIPLOT = YES diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-debug.solaris-x86-debug b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-debug.solaris-x86-debug new file mode 100644 index 000000000..00f6165e4 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-debug.solaris-x86-debug @@ -0,0 +1,8 @@ +# +# Revision-Id: jba@aps.anl.gov-20101109211220-yrh6mszvmacr91js +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-x86.solaris-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-gnu.solaris-x86-gnu b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-gnu.solaris-x86-gnu new file mode 100644 index 000000000..263c3206c --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86-gnu.solaris-x86-gnu @@ -0,0 +1,13 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-x86.solaris-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86.solaris-x86 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86.solaris-x86 new file mode 100644 index 000000000..79186bcc4 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86.solaris-x86 @@ -0,0 +1,22 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-sparc.solaris-sparc + +#XRTGRAPH_EXTENSIONS= +#XRTGRAPH= +SCIPLOT=YES + +X11_LIB = /usr/lib +X11_INC = /usr/include +MOTIF_INC = /usr/include +MOTIF_LIB = /usr/lib + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86_64.solaris-x86_64 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86_64.solaris-x86_64 new file mode 100644 index 000000000..84854b896 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.solaris-x86_64.solaris-x86_64 @@ -0,0 +1,16 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.solaris-x86.solaris-x86 + +XRTGRAPH= +SCIPLOT=YES + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-borland.win32-x86-borland b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-borland.win32-x86-borland new file mode 100644 index 000000000..8cc2f7a1e --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-borland.win32-x86-borland @@ -0,0 +1,109 @@ +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + + +EXCEED = Exceed7.0 + +ifeq ($(EXCEED),Exceed5) + X11_LIB = c:/exceed5/xdk/lib + X11_INC = c:/exceed5/xdk/include + EXCEED_XLIBS=xmstatic xt xlibgui xlib xmu + xmstatic_DIR=$(X11_LIB) + xt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + xlib_DIR=$(X11_LIB) + xmu_DIR=$(X11_LIB) + EXCEED_CFLAGS= +endif +ifeq ($(EXCEED),Exceed6.0) + X11_LIB = c:/exceed/xdk/lib + X11_INC = c:/exceed/xdk/include + EXCEED_XLIBS=xmstatic HCLXt xlibgui xlib HCLXmu + xmstatic_DIR=$(X11_LIB) + HCLXt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS= +endif +ifeq ($(EXCEED),Exceed6.1) + X11_LIB = c:/exceed/xdk/lib + X11_INC = c:/exceed/xdk/include + EXCEED_XLIBS=XmStatic XmStatXt xlibgui xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC +endif +ifeq ($(EXCEED),Exceed6.2) + X11_LIB = c:/exceed/xdk/lib + X11_INC = c:/exceed/xdk/include + EXCEED_XLIBS=XmStatic XmStatXt xlibgui Xlib hclXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + hclXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC +endif +ifeq ($(EXCEED),Exceed7.0) + X11_LIB = c:/Exceed/xdk/lib + X11_INC = c:/Exceed/xdk/include + EXCEED_XLIBS=XmStatic XmStatXt XlibGui Xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + XlibGui_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC /DMOTIFAPP +endif + +MOTIF_LIB = $(X11_LIB) +MOTIF_INC = $(X11_INC) + +TCL = c:\\Tcl +TK_LIB = $(TCL)/lib +TK_INC = $(TCL)/include +TCL_LIB = $(TCL)/lib +TCL_INC = $(TCL)/include +DP_LIB = $(TCL)/lib +DP_INC = $(TCL)/include +BLT_LIB = $(TCL)/lib +BLT_INC = $(TCL)/include + +IDL = /usr/local/idl +# IDL=$(IDL)/external/rpc is the sun4 version +IDLRPC = $(IDL)/external/rpc.solaris + +OPENWIN = +INTERVIEWS_BIN = +WINGZ_INC = +WINGZ_LIB = +MATHEMATICA = +QUESTWIN = + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +XRTGRAPH_EXTENSIONS = NO +XRTGRAPH = + +SCIPLOT = ../../src/medmdev/medm + +# z library created in SDDS extension +ZLIB_CFLAG = -DzLib +ZLIB_PROD_LIB = z +z_DIR = $(EPICS_EXTENSIONS_LIB) + +JAVA_DIR=c:/j2sdk1.4.1_01 diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin new file mode 100644 index 000000000..83ffe54f1 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-cygwin.win32-x86-cygwin @@ -0,0 +1,14 @@ + +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug new file mode 100644 index 000000000..42150a8e0 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-debug.win32-x86-debug @@ -0,0 +1,14 @@ + +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.win32-x86.win32-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw new file mode 100644 index 000000000..83ffe54f1 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86-mingw.win32-x86-mingw @@ -0,0 +1,14 @@ + +# +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.cygwin-x86.cygwin-x86 + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 new file mode 100644 index 000000000..8118893a7 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.win32-x86.win32-x86 @@ -0,0 +1,216 @@ +# +# CONFIG_SITE.win32-x86.win32-x86,v 1.3 2003/09/03 19:06:10 jba Exp +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +# Where to find utilities/libraries +# If you do not have a certain product, +# leave the line empty. +# + +-include $(TOP)/configure/os/CONFIG_SITE.Common.Common + +# If objects were compiled with different default runtime libraries +# (not a good idea), specify which one you want to use in the product +# by making it the default and the others nodefault. Use +# WIN32_RUNTIME = MD, MT, or ML in Makefile.Host if you want to do +# this. It will avoid LNK4098 warnings. +# msvcrt.lib -MD Multi-thread DLL +# msvcrtd.lib -MDd Multi-thread DLL, Debug +# libcmt.lib -MT Multi-thread +# libcmtd.lib -MTd Multi-thread, Debug +# libc.lib -ML Single-thread +# libcd.lib -MLd Single-thread, Debug + +# MD Multi-thread DLL product +ARCH_DEP_LDFLAGS_MD_NO += /DEFAULTLIB:"msvcrtd.lib" +ARCH_DEP_LDFLAGS_MD_NO += /NODEFAULTLIB:"msvcrt.lib" +ARCH_DEP_LDFLAGS_MD_YES += /DEFAULTLIB:"msvcrt.lib" +ARCH_DEP_LDFLAGS_MD_YES += /NODEFAULTLIB:"msvcrtd.lib" +ARCH_DEP_LDFLAGS_MD += $(ARCH_DEP_LDFLAGS_MD_$(HOST_OPT)) +ARCH_DEP_LDFLAGS_MD += /NODEFAULTLIB:"libcmt.lib" +ARCH_DEP_LDFLAGS_MD += /NODEFAULTLIB:"libcmtd.lib" +ARCH_DEP_LDFLAGS_MD += /NODEFAULTLIB:"libc.lib" +ARCH_DEP_LDFLAGS_MD += /NODEFAULTLIB:"libcd.lib" + +# MT Multi-threaded product +ARCH_DEP_LDFLAGS_MT_NO += /DEFAULTLIB:"libcmtd.lib" +ARCH_DEP_LDFLAGS_MT_NO += /NODEFAULTLIB:"libcmt.lib" +ARCH_DEP_LDFLAGS_MT_YES += /DEFAULTLIB:"libcmt.lib" +ARCH_DEP_LDFLAGS_MT_YES += /NODEFAULTLIB:"libcmtd.lib" +ARCH_DEP_LDFLAGS_MT += $(ARCH_DEP_LDFLAGS_MT_$(HOST_OPT)) +ARCH_DEP_LDFLAGS_MT += /NODEFAULTLIB:"msvcrt.lib" +ARCH_DEP_LDFLAGS_MT += /NODEFAULTLIB:"msvcrtd.lib" +ARCH_DEP_LDFLAGS_MT += /NODEFAULTLIB:"libc.lib" +ARCH_DEP_LDFLAGS_MT += /NODEFAULTLIB:"libcd.lib" + +# ML Single-threaded product +ARCH_DEP_LDFLAGS_ML_NO += /DEFAULTLIB:"libcd.lib" +ARCH_DEP_LDFLAGS_ML_NO += /NODEFAULTLIB:"libc.lib" +ARCH_DEP_LDFLAGS_ML_YES += /DEFAULTLIB:"libc.lib" +ARCH_DEP_LDFLAGS_ML_YES += /NODEFAULTLIB:"libcd.lib" +ARCH_DEP_LDFLAGS_ML += $(ARCH_DEP_LDFLAGS_ML_$(HOST_OPT)) +ARCH_DEP_LDFLAGS_ML += /NODEFAULTLIB:"msvcrt.lib" +ARCH_DEP_LDFLAGS_ML += /NODEFAULTLIB:"msvcrtd.lib" +ARCH_DEP_LDFLAGS_ML += /NODEFAULTLIB:"libcmt.lib" +ARCH_DEP_LDFLAGS_ML += /NODEFAULTLIB:"libcmtd.lib" + +ARCH_DEP_LDFLAGS += $(ARCH_DEP_LDFLAGS_$(WIN32_RUNTIME)) + +# ---------- java definitions +JAVA_DIR=c:/j2sdk1.4.1_01 + +# ---------- tcl/tk definitions +TCL = c:\\Tcl +TK_LIB = $(TCL)/lib +TK_INC = $(TCL)/include +TCL_LIB = $(TCL)/lib +TCL_INC = $(TCL)/include +DP_LIB = $(TCL)/lib +DP_INC = $(TCL)/include +BLT_LIB = $(TCL)/lib +BLT_INC = $(TCL)/include + +IDL = /usr/local/idl +# IDL=$(IDL)/external/rpc is the sun4 version +IDLRPC = $(IDL)/external/rpc.solaris + +OPENWIN = +INTERVIEWS_BIN = +WINGZ_INC = +WINGZ_LIB = +MATHEMATICA = +QUESTWIN = + +# Define XRTGRAPH_EXTENSIONS = YES only if using XRT/graph 3.x +# and you want the extensions for MEDM +XRTGRAPH_EXTENSIONS = NO +#XRTGRAPH = + +SCIPLOT = YES + +# z library created in SDDS extension +ZLIB_CFLAG = -DzLib +ZLIB_PROD_LIB = z +z_DIR = $(EPICS_EXTENSIONS_LIB) + +CLAPACK_INCLUDE = c:/CLAPACK/include +CLAPACK_LIB = c:/CLAPACK/lib + +EXCEED = Exceed13.0 + +ifeq ($(EXCEED),Exceed5) + X11_LIB = c:/exceed5/xdk/lib + X11_INC = c:/exceed5/xdk/include + EXCEED_XLIBS=xmstatic xt xlibgui xlib xmu + xmstatic_DIR=$(X11_LIB) + xt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + xlib_DIR=$(X11_LIB) + xmu_DIR=$(X11_LIB) + EXCEED_CFLAGS= +endif +ifeq ($(EXCEED),Exceed6.0) + X11_LIB = c:/exceed/xdk/lib + X11_INC = c:/exceed/xdk/include + EXCEED_XLIBS=xmstatic HCLXt xlibgui xlib HCLXmu + xmstatic_DIR=$(X11_LIB) + HCLXt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS= +endif +ifeq ($(EXCEED),Exceed6.1) + X11_LIB = c:/exceed/xdk/lib + X11_INC = c:/exceed/xdk/include + EXCEED_XLIBS=XmStatic XmStatXt xlibgui xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC +endif +ifeq ($(EXCEED),Exceed6.2) + X11_LIB = c:/exceed/xdk/lib + X11_INC = c:/exceed/xdk/include + EXCEED_XLIBS=XmStatic XmStatXt xlibgui Xlib hclXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + xlibgui_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + hclXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC +endif +ifeq ($(EXCEED),Exceed7.0) + X11_LIB = c:/Exceed/Exceed/xdk/lib + X11_INC = c:/Exceed/Exceed/xdk/include + EXCEED_XLIBS=XmStatic XmStatXt XlibGui Xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + XlibGui_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC /DMOTIFAPP +endif +ifeq ($(EXCEED),Exceed7.10) + X11_LIB = c:/Exceed7.10/XDK/lib + X11_INC = c:/Exceed7.10/XDK/include + EXCEED_XLIBS=XmStatic XmStatXt XlibGui Xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + XlibGui_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC /DMOTIFAPP +endif +ifeq ($(EXCEED),Exceed10.0) + XDK=C:/Exceed10.0/xdk + X11_LIB = $(XDK)/lib + X11_INC = $(XDK)/include + #X11_LIB = c:/Exceed10.0/XDK/lib + #X11_INC = c:/Exceed10.0/XDK/include + EXCEED_XLIBS=XmStatic XmStatXt Xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC /DMOTIFAPP +endif +ifeq ($(EXCEED),Exceed12.0) + XDK=C:/Exceed12.0/XDK + X11_LIB = $(XDK)/lib + X11_INC = $(XDK)/include + EXCEED_XLIBS=XmStatic XmStatXt Xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC /DMOTIFAPP +endif +ifeq ($(EXCEED),Exceed13.0) + XDK=C:/Exceed13.0/XDK + X11_LIB = $(XDK)/lib + X11_INC = $(XDK)/include + EXCEED_XLIBS=XmStatic XmStatXt Xlib HCLXmu + XmStatic_DIR=$(X11_LIB) + XmStatXt_DIR=$(X11_LIB) + Xlib_DIR=$(X11_LIB) + HCLXmu_DIR=$(X11_LIB) + EXCEED_CFLAGS=/DXMSTATIC /DMOTIFAPP +endif + + +###################################################### +# Override for XFree86/LessTif +#X11_LIB = c:/Cygwin/usr/X11R6/lib +#X11_INC = c:/Cygwin/usr/X11R6/include +#EXCEED_XLIBS = Xm Xt Xp Xmu X11 Xext +#EXCEED_CFLAGS = +###################################################### + +MOTIF_LIB = $(X11_LIB) +MOTIF_INC = $(X11_INC) + diff --git a/src/makeBaseExt/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 b/src/makeBaseExt/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 new file mode 100644 index 000000000..8ea0e7877 --- /dev/null +++ b/src/makeBaseExt/top/configure/os/CONFIG_SITE.windows-x64.windows-x64 @@ -0,0 +1,8 @@ +# +# Revision-Id: jba@aps.anl.gov-20101109211220-yrh6mszvmacr91js +# +# Site Specific Configuration Information +# Only the local epics system manager should modify this file + +-include $(TOP)/configure/os/CONFIG_SITE.win32-x86.win32-x86 + diff --git a/src/makeBaseExt/top/exampleExt/Makefile b/src/makeBaseExt/top/exampleExt/Makefile new file mode 100644 index 000000000..618991fa7 --- /dev/null +++ b/src/makeBaseExt/top/exampleExt/Makefile @@ -0,0 +1,13 @@ +TOP=../.. +include $(TOP)/configure/CONFIG + +PROD_HOST = caExample +caExample_SRCS += caExample.c +caExample_LIBS += $(EPICS_BASE_HOST_LIBS) + +#caExample_CFLAGS += #C source file compiler flags, e.g. -DMYDEF=3 +#caExample_LDFLAGS += #Linker flags, e.g. -L$(MOTIF_LIB) -L$(X11_LIB) +#caExample_SYS_LIBS_solaris += #System libraries for OS_CLASS solaris, e.g. socket + +include $(TOP)/configure/RULES + diff --git a/src/makeBaseExt/top/exampleExt/RELEASE_NOTES.HTM b/src/makeBaseExt/top/exampleExt/RELEASE_NOTES.HTM new file mode 100644 index 000000000..56dce9346 --- /dev/null +++ b/src/makeBaseExt/top/exampleExt/RELEASE_NOTES.HTM @@ -0,0 +1,16 @@ + + + +
    +

    caExample Release Notes

    +

    author name

    +
    + +
    + +

    05 Oct 2000 - v1.0

    + +

    Initial version

    + + + diff --git a/src/makeBaseExt/top/exampleExt/caExample.c b/src/makeBaseExt/top/exampleExt/caExample.c new file mode 100644 index 000000000..cc342e237 --- /dev/null +++ b/src/makeBaseExt/top/exampleExt/caExample.c @@ -0,0 +1,25 @@ +/*caExample.c*/ +#include +#include +#include +#include + +#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); +} diff --git a/src/makeBaseExt/top/simpleExt/Makefile b/src/makeBaseExt/top/simpleExt/Makefile new file mode 100644 index 000000000..52cfac525 --- /dev/null +++ b/src/makeBaseExt/top/simpleExt/Makefile @@ -0,0 +1,21 @@ +TOP=../.. +include $(TOP)/configure/CONFIG + +# Product +#PROD_HOST = caExample +#caExample_SRCS += caExample.cc +#caExample_CFLAGS += +#caExample_LDFLAGS += +#caExample_LIBS += ca Com +#ca_LIB=$(EPICS_BASE_LIB) +#Com_LIB=$(EPICS_BASE_LIB) + +# Library +#LIBRARY_HOST = abc +#abc_SRCS += abc.c +#abc_DLL_LIBS = ca Com + + +include $(TOP)/configure/RULES + + diff --git a/src/makeBaseExt/top/src/Makefile b/src/makeBaseExt/top/src/Makefile new file mode 100644 index 000000000..b6f87496a --- /dev/null +++ b/src/makeBaseExt/top/src/Makefile @@ -0,0 +1,9 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +TOP = .. +include $(TOP)/configure/CONFIG + +DIRS += _EXTNAME_ + +include $(TOP)/configure/RULES_DIRS + diff --git a/src/misc/Makefile b/src/misc/Makefile new file mode 100644 index 000000000..3d7bc5168 --- /dev/null +++ b/src/misc/Makefile @@ -0,0 +1,42 @@ +#************************************************************************* +# Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +TOP=../.. +include $(TOP)/configure/CONFIG + +DBD += base.dbd +DBD += system.dbd +DBD += dlload.dbd + +INC += epicsRelease.h +INC += iocInit.h +INC += miscIocRegister.h +INC += iocshRegisterCommon.h + +LIB_SRCS += epicsRelease.c +LIB_SRCS += iocInit.c +LIB_SRCS += asSubRecordFunctions.c +LIB_SRCS += miscIocRegister.c +LIB_SRCS += dlload.c +LIB_SRCS += iocshRegisterCommon.c + + +LIBRARY_IOC = miscIoc + +miscIoc_RCS = miscIoc.rc +miscIoc_LIBS = rsrvIoc asIoc dbtoolsIoc dbIoc registryIoc dbStaticIoc ca Com + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=miscIoc +OBJLIB_SRCS = $(LIB_SRCS) +endif + +include $(TOP)/configure/RULES + diff --git a/src/misc/asSubRecordFunctions.c b/src/misc/asSubRecordFunctions.c new file mode 100644 index 000000000..957db300a --- /dev/null +++ b/src/misc/asSubRecordFunctions.c @@ -0,0 +1,91 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* asSubRecordFunctions.c */ + +/* Author: Marty Kraimer Date: 01MAY2000 */ + +#include +#include +#include +#include +#include + +#include "dbAccess.h" +#include "cantProceed.h" +#include "callback.h" +#include "alarm.h" +#include "errlog.h" +#include "dbEvent.h" +#include "recSup.h" +#include "recGbl.h" +#include "registryFunction.h" +#include "asLib.h" +#include "asDbLib.h" +#include "subRecord.h" +#include "epicsExport.h" + +/* The following is provided for access security*/ +/*It allows a CA client to force access security initialization*/ + +static void myCallback(CALLBACK *pcallback) +{ + ASDBCALLBACK *pasdbcallback = (ASDBCALLBACK *)pcallback; + subRecord *precord; + struct rset *prset; + + callbackGetUser(precord,pcallback); + prset=(struct rset *)(precord->rset); + precord->val = 0.0; + if(pasdbcallback->status) { + recGblSetSevr(precord,READ_ALARM,precord->brsv); + recGblRecordError(pasdbcallback->status,precord,"asInit Failed"); + } + dbScanLock((dbCommon *)precord); + (*prset->process)((dbCommon *)precord); + dbScanUnlock((dbCommon *)precord); +} + +long asSubInit(subRecord *precord,void *process) +{ + ASDBCALLBACK *pcallback; + + pcallback = (ASDBCALLBACK *)callocMustSucceed( + 1,sizeof(ASDBCALLBACK),"asSubInit"); + precord->dpvt = (void *)pcallback; + callbackSetCallback(myCallback,&pcallback->callback); + callbackSetUser(precord,&pcallback->callback); + return(0); +} + +long asSubProcess(subRecord *precord) +{ + ASDBCALLBACK *pcallback = (ASDBCALLBACK *)precord->dpvt; + + if(!precord->pact && precord->val==1.0) { + db_post_events(precord,&precord->val,DBE_VALUE); + callbackSetPriority(precord->prio,&pcallback->callback); + asInitAsyn(pcallback); + precord->pact=TRUE; + return(1); + } + db_post_events(precord,&precord->val,DBE_VALUE); + return(0); +} + +static registryFunctionRef asSubRef[] = { + {"asSubInit",(REGISTRYFUNCTION)asSubInit}, + {"asSubProcess",(REGISTRYFUNCTION)asSubProcess} +}; + +static void asSub(void) +{ + registryFunctionRefAdd(asSubRef,NELEMENTS(asSubRef)); +} +epicsExportRegistrar(asSub); diff --git a/src/misc/base.dbd b/src/misc/base.dbd new file mode 100644 index 000000000..bafe8ca7d --- /dev/null +++ b/src/misc/base.dbd @@ -0,0 +1,50 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# This file defines the standard record types and device support +# provided by Base and (usually) loaded into all IOCs. + +# Menus +include "menuGlobal.dbd" +include "menuConvert.dbd" + +# Record types +include "aaiRecord.dbd" +include "aaoRecord.dbd" +include "aiRecord.dbd" +include "aoRecord.dbd" +include "aSubRecord.dbd" +include "biRecord.dbd" +include "boRecord.dbd" +include "calcRecord.dbd" +include "calcoutRecord.dbd" +include "compressRecord.dbd" +include "dfanoutRecord.dbd" +include "eventRecord.dbd" +include "fanoutRecord.dbd" +include "longinRecord.dbd" +include "longoutRecord.dbd" +include "mbbiRecord.dbd" +include "mbbiDirectRecord.dbd" +include "mbboRecord.dbd" +include "mbboDirectRecord.dbd" +include "permissiveRecord.dbd" +include "selRecord.dbd" +include "seqRecord.dbd" +include "stateRecord.dbd" +include "stringinRecord.dbd" +include "stringoutRecord.dbd" +include "subRecord.dbd" +include "subArrayRecord.dbd" +include "waveformRecord.dbd" + +# "Soft Channel", "Raw Soft Channel", and "Async Soft Channel" device support +include "devSoft.dbd" + +# Access security subroutines +registrar(asSub) +variable(asCaDebug,int) + +# dbStaticLib settings +variable(dbRecordsOnceOnly,int) +variable(dbBptNotMonotonic,int) + diff --git a/src/misc/dlload.c b/src/misc/dlload.c new file mode 100644 index 000000000..5b0591d5f --- /dev/null +++ b/src/misc/dlload.c @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "epicsFindSymbol.h" +#include "iocsh.h" +#include "epicsExport.h" + +static const iocshArg dlloadArg0 = { "path/library.so", iocshArgString}; +static const iocshArg * const dlloadArgs[] = {&dlloadArg0}; +static const iocshFuncDef dlloadFuncDef = {"dlload", 1, dlloadArgs}; +static void dlloadCallFunc(const iocshArgBuf *args) +{ + if (!epicsLoadLibrary(args[0].sval)) { + printf("epicsLoadLibrary failed: %s\n", epicsLoadError()); + } +} + +static void dlloadRegistar(void) { + iocshRegister(&dlloadFuncDef, dlloadCallFunc); +} +epicsExportRegistrar(dlloadRegistar); diff --git a/src/misc/dlload.dbd b/src/misc/dlload.dbd new file mode 100644 index 000000000..740ebbbba --- /dev/null +++ b/src/misc/dlload.dbd @@ -0,0 +1,3 @@ +# Including this DBD file adds the 'dlload' command to the IOC shell. + +registrar(dlloadRegistar) diff --git a/src/misc/epicsRelease.c b/src/misc/epicsRelease.c new file mode 100644 index 000000000..263a2b43c --- /dev/null +++ b/src/misc/epicsRelease.c @@ -0,0 +1,30 @@ +/*************************************************************************\ +* 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 + **/ + +#include +#include +#include "epicsVersion.h" +#include "epicsStdioRedirect.h" + +#define epicsExportSharedSymbols +#include "epicsRelease.h" + +static const char id[] = "@(#) " EPICS_VERSION_STRING ", Misc. Utilities Library" __DATE__; + +epicsShareFunc int epicsShareAPI coreRelease(void) +{ + printf ( "############################################################################\n" ); + printf ( "## %s\n", epicsReleaseVersion ); + printf ( "## %s\n", "EPICS Base built " __DATE__ ); + printf ( "############################################################################\n" ); + return 0; +} diff --git a/src/misc/epicsRelease.h b/src/misc/epicsRelease.h new file mode 100644 index 000000000..eae6c7e17 --- /dev/null +++ b/src/misc/epicsRelease.h @@ -0,0 +1,27 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* epicsRelease.h */ + +#ifndef INCepicsReleaseh +#define INCepicsReleaseh + +#ifdef __cplusplus +extern "C" { +#endif + +#include "shareLib.h" +epicsShareFunc int epicsShareAPI coreRelease(void); + +#ifdef __cplusplus +} +#endif + +#endif /*INCepicsReleaseh*/ + diff --git a/src/misc/iocInit.c b/src/misc/iocInit.c new file mode 100644 index 000000000..a31cf074f --- /dev/null +++ b/src/misc/iocInit.c @@ -0,0 +1,532 @@ +/*************************************************************************\ +* Copyright (c) 2009 UChicago Argonne LLC, 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 is 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 */ +/* + * Original Author: Marty Kraimer + * Date: 06-01-91 + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsThread.h" +#include "epicsPrint.h" +#include "ellLib.h" +#include "dbDefs.h" +#include "dbBase.h" +#include "caeventmask.h" +#include "dbAddr.h" +#include "dbBkpt.h" +#include "dbFldTypes.h" +#include "link.h" +#include "dbLock.h" +#include "dbAccess.h" +#include "recGbl.h" +#include "dbNotify.h" +#include "dbCa.h" +#include "dbScan.h" +#include "taskwd.h" +#include "callback.h" +#include "dbCommon.h" +#include "dbLock.h" +#include "devSup.h" +#include "drvSup.h" +#include "menuPini.h" +#include "registryRecordType.h" +#include "registryDeviceSupport.h" +#include "registryDriverSupport.h" +#include "errMdef.h" +#include "recSup.h" +#include "envDefs.h" +#include "rsrv.h" +#include "asDbLib.h" +#include "dbStaticLib.h" +#include "db_access_routines.h" +#include "initHooks.h" +#include "epicsExit.h" +#include "epicsSignal.h" + +#define epicsExportSharedSymbols +#include "epicsRelease.h" +#include "iocInit.h" + +static enum { + iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped +} iocState = iocVirgin; + +/* define forward references*/ +static void initDrvSup(void); +static void initRecSup(void); +static void initDevSup(void); +static void finishDevSup(void); +static void initDatabase(void); +static void initialProcess(void); +static void exitDatabase(void *dummy); + + +/* + * Initialize EPICS on the IOC. + */ +int iocInit(void) +{ + return iocBuild() || iocRun(); +} + +int iocBuild(void) +{ + if (iocState != iocVirgin) { + errlogPrintf("iocBuild: IOC can only be initialized once\n"); + return -1; + } + initHookAnnounce(initHookAtIocBuild); + + if (!epicsThreadIsOkToBlock()) { + epicsThreadSetOkToBlock(1); + } + + errlogPrintf("Starting iocInit\n"); + if (!pdbbase) { + errlogPrintf("iocBuild: Aborting, no database loaded!\n"); + return -1; + } + epicsSignalInstallSigHupIgnore(); + initHookAnnounce(initHookAtBeginning); + + coreRelease(); + /* After this point, further calls to iocInit() are disallowed. */ + iocState = iocBuilding; + + taskwdInit(); + callbackInit(); + initHookAnnounce(initHookAfterCallbackInit); + + dbCaLinkInit(); + initHookAnnounce(initHookAfterCaLinkInit); + + initDrvSup(); + initHookAnnounce(initHookAfterInitDrvSup); + + initRecSup(); + initHookAnnounce(initHookAfterInitRecSup); + + initDevSup(); + initHookAnnounce(initHookAfterInitDevSup); + + initDatabase(); + dbLockInitRecords(pdbbase); + dbBkptInit(); + initHookAnnounce(initHookAfterInitDatabase); + + finishDevSup(); + initHookAnnounce(initHookAfterFinishDevSup); + + scanInit(); + if (asInit()) { + errlogPrintf("iocBuild: asInit Failed.\n"); + return -1; + } + dbPutNotifyInit(); + epicsThreadSleep(.5); + initHookAnnounce(initHookAfterScanInit); + + initialProcess(); + initHookAnnounce(initHookAfterInitialProcess); + + /* Start CA server threads */ + rsrv_init(); + initHookAnnounce(initHookAfterCaServerInit); + + iocState = iocBuilt; + initHookAnnounce(initHookAfterIocBuilt); + return 0; +} + +int iocRun(void) +{ + if (iocState != iocPaused && iocState != iocBuilt) { + errlogPrintf("iocRun: IOC not paused\n"); + return -1; + } + initHookAnnounce(initHookAtIocRun); + + /* Enable scan tasks and some driver support functions. */ + scanRun(); + dbCaRun(); + initHookAnnounce(initHookAfterDatabaseRunning); + if (iocState == iocBuilt) + initHookAnnounce(initHookAfterInterruptAccept); + + rsrv_run(); + initHookAnnounce(initHookAfterCaServerRunning); + if (iocState == iocBuilt) + initHookAnnounce(initHookAtEnd); + + errlogPrintf("iocRun: %s\n", iocState == iocBuilt ? + "All initialization complete" : + "IOC restarted"); + iocState = iocRunning; + initHookAnnounce(initHookAfterIocRunning); + return 0; +} + +int iocPause(void) +{ + if (iocState != iocRunning) { + errlogPrintf("iocPause: IOC not running\n"); + return -1; + } + initHookAnnounce(initHookAtIocPause); + + rsrv_pause(); + initHookAnnounce(initHookAfterCaServerPaused); + + dbCaPause(); + scanPause(); + initHookAnnounce(initHookAfterDatabasePaused); + + iocState = iocPaused; + errlogPrintf("iocPause: IOC suspended\n"); + initHookAnnounce(initHookAfterIocPaused); + return 0; +} + + +static void initDrvSup(void) /* Locate all driver support entry tables */ +{ + drvSup *pdrvSup; + + for (pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); pdrvSup; + pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { + struct drvet *pdrvet = registryDriverSupportFind(pdrvSup->name); + + if (!pdrvet) { + errlogPrintf("iocInit: driver %s not found\n", pdrvSup->name); + continue; + } + pdrvSup->pdrvet = pdrvet; + + if (pdrvet->init) + pdrvet->init(); + } +} + +static void initRecSup(void) +{ + dbRecordType *pdbRecordType; + + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + recordTypeLocation *precordTypeLocation = + registryRecordTypeFind(pdbRecordType->name); + struct rset *prset; + + if (!precordTypeLocation) { + errlogPrintf("iocInit record support for %s not found\n", + pdbRecordType->name); + continue; + } + prset = precordTypeLocation->prset; + pdbRecordType->prset = prset; + if (prset->init) { + prset->init(); + } + } +} + +static void initDevSup(void) +{ + dbRecordType *pdbRecordType; + + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + devSup *pdevSup; + + for (pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); + pdevSup; + pdevSup = (devSup *)ellNext(&pdevSup->node)) { + struct dset *pdset = registryDeviceSupportFind(pdevSup->name); + + if (!pdset) { + errlogPrintf("device support %s not found\n",pdevSup->name); + continue; + } + dbInitDevSup(pdevSup, pdset); /* Calls pdset->init(0) */ + } + } +} + +static void finishDevSup(void) +{ + dbRecordType *pdbRecordType; + + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + devSup *pdevSup; + + for (pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); + pdevSup; + pdevSup = (devSup *)ellNext(&pdevSup->node)) { + struct dset *pdset = pdevSup->pdset; + + if (pdset && pdset->init) + pdset->init(1); + } + } +} + +/* + * Iterate through all record instances (but not aliases), + * calling a function for each one. + */ +typedef void (*recIterFunc)(dbRecordType *rtyp, dbCommon *prec, void *user); + +static void iterateRecords(recIterFunc func, void *user) +{ + dbRecordType *pdbRecordType; + + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + dbRecordNode *pdbRecordNode; + + for (pdbRecordNode = (dbRecordNode *)ellFirst(&pdbRecordType->recList); + pdbRecordNode; + pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { + dbCommon *precord = pdbRecordNode->precord; + + if (!precord->name[0] || + pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + continue; + + func(pdbRecordType, precord, user); + } + } + return; +} + +static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord, + void *user) +{ + struct rset *prset = pdbRecordType->prset; + devSup *pdevSup; + + if (!prset) return; /* unlikely */ + + precord->rset = prset; + precord->rdes = pdbRecordType; + precord->mlok = epicsMutexMustCreate(); + ellInit(&precord->mlis); + + /* Reset the process active field */ + precord->pact = FALSE; + + /* Init DSET NOTE that result may be NULL */ + pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); + precord->dset = pdevSup ? pdevSup->pdset : NULL; + + if (prset->init_record) + prset->init_record(precord, 0); +} + +static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, + void *user) +{ + devSup *pdevSup; + int j; + + /* Convert all PV_LINKs to DB_LINKs or CA_LINKs */ + /* For all the links in the record type... */ + for (j = 0; j < pdbRecordType->no_links; j++) { + dbFldDes *pdbFldDes = + pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + + if (plink->type == PV_LINK) { + DBADDR dbaddr; + + if (plink == &precord->tsel) recGblTSELwasModified(plink); + if (!(plink->value.pv_link.pvlMask&(pvlOptCA|pvlOptCP|pvlOptCPP)) + && (dbNameToAddr(plink->value.pv_link.pvname,&dbaddr)==0)) { + DBADDR *pdbAddr; + + plink->type = DB_LINK; + pdbAddr = dbCalloc(1,sizeof(struct dbAddr)); + *pdbAddr = dbaddr; /*structure copy*/; + plink->value.pv_link.pvt = pdbAddr; + } else {/*It is a CA link*/ + + if (pdbFldDes->field_type == DBF_INLINK) { + plink->value.pv_link.pvlMask |= pvlOptInpNative; + } + dbCaAddLink(plink); + if (pdbFldDes->field_type == DBF_FWDLINK) { + char *pperiod = + strrchr(plink->value.pv_link.pvname,'.'); + + if (pperiod && strstr(pperiod,"PROC")) { + plink->value.pv_link.pvlMask |= pvlOptFWD; + } else { + errlogPrintf("%s.FLNK is a Channel Access Link " + " but does not link to a PROC field\n", + precord->name); + } + } + } + } + } + pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); + if (pdevSup) { + struct dsxt *pdsxt = pdevSup->pdsxt; + if (pdsxt && pdsxt->add_record) { + pdsxt->add_record(precord); + } + } +} + +static void doInitRecord1(dbRecordType *pdbRecordType, dbCommon *precord, + void *user) +{ + struct rset *prset = pdbRecordType->prset; + + if (!prset) return; /* unlikely */ + + if (prset->init_record) + prset->init_record(precord, 1); +} + +static void initDatabase(void) +{ + iterateRecords(doInitRecord0, NULL); + iterateRecords(doResolveLinks, NULL); + iterateRecords(doInitRecord1, NULL); + + epicsAtExit(exitDatabase, NULL); + return; +} + +/* + * Process database records at initialization ordered by phase + * if their pini (process at init) field is set. + */ +typedef struct { + int this; + int next; + epicsEnum16 pini; +} phaseData_t; + +static void doRecordPini(dbRecordType *rtype, dbCommon *precord, void *user) +{ + phaseData_t *pphase = (phaseData_t *)user; + int phas; + + if (precord->pini != pphase->pini) return; + + phas = precord->phas; + if (phas == pphase->this) { + dbScanLock(precord); + dbProcess(precord); + dbScanUnlock(precord); + } else if (phas > pphase->this && phas < pphase->next) + pphase->next = phas; +} + +static void piniProcess(int pini) +{ + phaseData_t phase; + phase.next = MIN_PHASE; + phase.pini = pini; + + /* This scans through the whole database as many times as needed. + * During the first pass it is unlikely to find any records with + * PHAS = MIN_PHASE, but during each iteration it looks for the + * phase value of the next pass to run. Note that PHAS fields can + * be changed at runtime, so we have to look for the lowest value + * of PHAS each time. + */ + do { + phase.this = phase.next; + phase.next = MAX_PHASE + 1; + iterateRecords(doRecordPini, &phase); + } while (phase.next != MAX_PHASE + 1); +} + +static void piniProcessHook(initHookState state) +{ + switch (state) { + case initHookAtIocRun: + piniProcess(menuPiniRUN); + break; + + case initHookAfterIocRunning: + piniProcess(menuPiniRUNNING); + break; + + case initHookAtIocPause: + piniProcess(menuPiniPAUSE); + break; + + case initHookAfterIocPaused: + piniProcess(menuPiniPAUSED); + break; + + default: + break; + } +} + +static void initialProcess(void) +{ + initHookRegister(piniProcessHook); + piniProcess(menuPiniYES); +} + + +/* + * Shutdown processing. + */ +static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord, + void *user) +{ + devSup *pdevSup; + struct dsxt *pdsxt; + int j; + + for (j = 0; j < pdbRecordType->no_links; j++) { + dbFldDes *pdbFldDes = + pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); + + if (plink->type == CA_LINK) { + dbCaRemoveLink(plink); + } + } + + if (precord->dset && + (pdevSup = dbDSETtoDevSup(pdbRecordType, precord->dset)) && + (pdsxt = pdevSup->pdsxt) && + pdsxt->del_record) { + pdsxt->del_record(precord); + } +} + +static void exitDatabase(void *dummy) +{ + iterateRecords(doCloseLinks, NULL); + iocState = iocStopped; +} diff --git a/src/misc/iocInit.h b/src/misc/iocInit.h new file mode 100644 index 000000000..6a2f5682c --- /dev/null +++ b/src/misc/iocInit.h @@ -0,0 +1,30 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* iocInit.h ioc initialization */ + +#ifndef INCiocInith +#define INCiocInith + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int iocInit(void); +epicsShareFunc int iocBuild(void); +epicsShareFunc int iocRun(void); +epicsShareFunc int iocPause(void); + +#ifdef __cplusplus +} +#endif + + +#endif /*INCiocInith*/ diff --git a/src/misc/iocshRegisterCommon.c b/src/misc/iocshRegisterCommon.c new file mode 100644 index 000000000..3e027eb6c --- /dev/null +++ b/src/misc/iocshRegisterCommon.c @@ -0,0 +1,36 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "iocsh.h" +#include "dbAccess.h" +#include "dbStaticIocRegister.h" +#include "registryIocRegister.h" +#include "asIocRegister.h" +#include "dbIocRegister.h" +#include "dbtoolsIocRegister.h" +#include "rsrvIocRegister.h" +#include "libComRegister.h" + +#define epicsExportSharedSymbols +#include "miscIocRegister.h" +#include "iocshRegisterCommon.h" + +void epicsShareAPI iocshRegisterCommon(void) +{ + iocshPpdbbase = &pdbbase; + + dbStaticIocRegister(); + registryIocRegister(); + dbIocRegister(); + dbtoolsIocRegister(); + asIocRegister(); + rsrvIocRegister(); + miscIocRegister(); + libComRegister(); +} diff --git a/src/misc/iocshRegisterCommon.h b/src/misc/iocshRegisterCommon.h new file mode 100644 index 000000000..6327d7c68 --- /dev/null +++ b/src/misc/iocshRegisterCommon.h @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* iocshRegisterCommon.h */ +/* Author: Marty Kraimer Date: 27APR2000 */ + +#ifndef INCiocshRegisterCommonH +#define INCiocshRegisterCommonH + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* register many useful commands */ +epicsShareFunc void epicsShareAPI iocshRegisterCommon(void); + +#ifdef __cplusplus +} +#endif + +#endif /*INCiocshRegisterCommonH*/ diff --git a/src/misc/misc.rc b/src/misc/misc.rc new file mode 100755 index 000000000..040cf26c8 --- /dev/null +++ b/src/misc/misc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Miscellaneous Tools Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Miscellaneous Tools Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "misc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "misc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/misc/miscIoc.rc b/src/misc/miscIoc.rc new file mode 100755 index 000000000..abff0a137 --- /dev/null +++ b/src/misc/miscIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Miscellaneous Ioc Tools Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Miscellaneous Ioc Tools Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "miscIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "miscIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/misc/miscIocRegister.c b/src/misc/miscIocRegister.c new file mode 100644 index 000000000..0d482f7cf --- /dev/null +++ b/src/misc/miscIocRegister.c @@ -0,0 +1,88 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "iocsh.h" +#include "errlog.h" +#include "epicsExport.h" + +#define epicsExportSharedSymbols +#include "iocInit.h" +#include "epicsRelease.h" +#include "miscIocRegister.h" + +/* iocInit */ +static const iocshFuncDef iocInitFuncDef = {"iocInit",0,NULL}; +static void iocInitCallFunc(const iocshArgBuf *args) +{ + iocInit(); +} + +/* iocBuild */ +static const iocshFuncDef iocBuildFuncDef = {"iocBuild",0,NULL}; +static void iocBuildCallFunc(const iocshArgBuf *args) +{ + iocBuild(); +} + +/* iocRun */ +static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL}; +static void iocRunCallFunc(const iocshArgBuf *args) +{ + iocRun(); +} + +/* iocPause */ +static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL}; +static void iocPauseCallFunc(const iocshArgBuf *args) +{ + iocPause(); +} + +/* coreRelease */ +static const iocshFuncDef coreReleaseFuncDef = {"coreRelease",0,NULL}; +static void coreReleaseCallFunc(const iocshArgBuf *args) +{ + coreRelease (); +} + + +void epicsShareAPI miscIocRegister(void) +{ + iocshRegister(&iocInitFuncDef,iocInitCallFunc); + iocshRegister(&iocBuildFuncDef,iocBuildCallFunc); + iocshRegister(&iocRunFuncDef,iocRunCallFunc); + iocshRegister(&iocPauseFuncDef,iocPauseCallFunc); + iocshRegister(&coreReleaseFuncDef, coreReleaseCallFunc); +} + + +/* system -- escape to system command interpreter. + * + * Disabled by default, for security reasons. To enable this command, add + * registrar(iocshSystemCommand) + * to an application dbd file. + */ +static const iocshArg systemArg0 = { "command string",iocshArgString}; +static const iocshArg * const systemArgs[] = {&systemArg0}; +static const iocshFuncDef systemFuncDef = {"system",1,systemArgs}; +static void systemCallFunc(const iocshArgBuf *args) +{ + system(args[0].sval); +} + +static void iocshSystemCommand(void) +{ + if (system(NULL)) + iocshRegister(&systemFuncDef, systemCallFunc); + else + errlogPrintf ("Can't register 'system' command -- no command interpreter available.\n"); +} +epicsExportRegistrar(iocshSystemCommand); diff --git a/src/misc/miscIocRegister.h b/src/misc/miscIocRegister.h new file mode 100644 index 000000000..28f67796e --- /dev/null +++ b/src/misc/miscIocRegister.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2007 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_miscIocRegister_H +#define INC_miscIocRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI miscIocRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_miscIocRegister_H */ diff --git a/src/misc/system.dbd b/src/misc/system.dbd new file mode 100644 index 000000000..e50c19820 --- /dev/null +++ b/src/misc/system.dbd @@ -0,0 +1,3 @@ +# Including this DBD file adds a 'system' command to the IOC shell. + +registrar(iocshSystemCommand) diff --git a/src/rec/Makefile b/src/rec/Makefile new file mode 100644 index 000000000..0d4b78aa8 --- /dev/null +++ b/src/rec/Makefile @@ -0,0 +1,84 @@ +#************************************************************************* +# 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 + +DBDINC += aaiRecord +DBDINC += aaoRecord +DBDINC += aiRecord +DBDINC += aoRecord +DBDINC += aSubRecord +DBDINC += biRecord +DBDINC += boRecord +DBDINC += calcRecord +DBDINC += calcoutRecord +DBDINC += compressRecord +DBDINC += dfanoutRecord +DBDINC += eventRecord +DBDINC += fanoutRecord +DBDINC += histogramRecord +DBDINC += longinRecord +DBDINC += longoutRecord +DBDINC += mbbiRecord +DBDINC += mbbiDirectRecord +DBDINC += mbboRecord +DBDINC += mbboDirectRecord +DBDINC += permissiveRecord +DBDINC += selRecord +DBDINC += seqRecord +DBDINC += stateRecord +DBDINC += stringinRecord +DBDINC += stringoutRecord +DBDINC += subRecord +DBDINC += subArrayRecord +DBDINC += waveformRecord + +LIBSRCS += aaiRecord.c +LIBSRCS += aaoRecord.c +LIBSRCS += aiRecord.c +LIBSRCS += aoRecord.c +LIBSRCS += aSubRecord.c +LIBSRCS += biRecord.c +LIBSRCS += boRecord.c +LIBSRCS += calcRecord.c +LIBSRCS += calcoutRecord.c +LIBSRCS += compressRecord.c +LIBSRCS += dfanoutRecord.c +LIBSRCS += eventRecord.c +LIBSRCS += fanoutRecord.c +LIBSRCS += histogramRecord.c +LIBSRCS += longinRecord.c +LIBSRCS += longoutRecord.c +LIBSRCS += mbbiRecord.c +LIBSRCS += mbbiDirectRecord.c +LIBSRCS += mbboRecord.c +LIBSRCS += mbboDirectRecord.c +LIBSRCS += permissiveRecord.c +LIBSRCS += selRecord.c +LIBSRCS += seqRecord.c +LIBSRCS += stateRecord.c +LIBSRCS += stringinRecord.c +LIBSRCS += stringoutRecord.c +LIBSRCS += subRecord.c +LIBSRCS += subArrayRecord.c +LIBSRCS += waveformRecord.c + +LIBRARY_IOC += recIoc + +recIoc_LIBS = asIoc dbIoc registryIoc dbStaticIoc ca Com +recIoc_RCS = recIoc.rc + +# For R3.13 compatability +ifeq ($(strip $(COMPAT_313)),YES) +OBJS_vxWorks = $(LIBSRCS:%.c=%) +endif + +include $(TOP)/configure/RULES + diff --git a/src/rec/aSubRecord.c b/src/rec/aSubRecord.c new file mode 100644 index 000000000..f1aa23353 --- /dev/null +++ b/src/rec/aSubRecord.c @@ -0,0 +1,494 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is 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 + * + * Record Support Routines for the Array Subroutine Record type, + * derived from Andy Foster's genSub record, with some features + * removed and asynchronous support added. + * + * Original Author: Andy Foster + * Revised by: Andrew Johnson + * + */ + +#include +#include +#include + +#include "alarm.h" +#include "cantProceed.h" +#include "dbDefs.h" +#include "dbEvent.h" +#include "dbAccess.h" +#include "dbFldTypes.h" +#include "dbStaticLib.h" +#include "errMdef.h" +#include "errlog.h" +#include "recSup.h" +#include "devSup.h" +#include "special.h" +#include "registryFunction.h" +#include "epicsExport.h" +#include "recGbl.h" + +#define GEN_SIZE_OFFSET +#include "aSubRecord.h" +#undef GEN_SIZE_OFFSET + + +typedef long (*GENFUNCPTR)(struct aSubRecord *); + +/* Create RSET - Record Support Entry Table*/ + +#define report NULL +#define initialize NULL +static long init_record(aSubRecord *, int); +static long process(aSubRecord *); +static long special(DBADDR *, int); +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long ); +#define get_units NULL +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset aSubRSET = { + 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, aSubRSET); + +static long initFields(epicsEnum16 *pft, epicsUInt32 *pno, epicsUInt32 *pne, + const char **fldnames, void **pval, void **povl); +static long fetch_values(aSubRecord *prec); +static void monitor(aSubRecord *); +static long do_sub(aSubRecord *); + +#define NUM_ARGS 21 +#define MAX_ARRAY_SIZE 10000000 + +/* These are the names of the Input fields */ +static const char *Ifldnames[] = { + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", + "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U" +}; + +/* These are the names of the Output fields */ +static const char *Ofldnames[] = { + "VALA", "VALB", "VALC", "VALD", "VALE", "VALF", "VALG", + "VALH", "VALI", "VALJ", "VALK", "VALL", "VALM", "VALN", + "VALO", "VALP", "VALQ", "VALR", "VALS", "VALT", "VALU" +}; + + +static long init_record(aSubRecord *prec, int pass) +{ + STATIC_ASSERT(sizeof(prec->onam)==sizeof(prec->snam)); + GENFUNCPTR pfunc; + long status; + int i; + + status = 0; + if (pass == 0) { + /* Allocate memory for arrays */ + initFields(&prec->fta, &prec->noa, &prec->nea, Ifldnames, + &prec->a, NULL); + initFields(&prec->ftva, &prec->nova, &prec->neva, Ofldnames, + &prec->vala, &prec->ovla); + return 0; + } + + /* Initialize the Subroutine Name Link */ + switch (prec->subl.type) { + case CONSTANT: + recGblInitConstantLink(&prec->subl, DBF_STRING, prec->snam); + break; + + case PV_LINK: + case DB_LINK: + case CA_LINK: + break; + + default: + recGblRecordError(S_db_badField, (void *)prec, + "aSubRecord(init_record) Bad SUBL link type"); + return S_db_badField; + } + + /* Initialize Input Links */ + for (i = 0; i < NUM_ARGS; i++) { + struct link *plink = &(&prec->inpa)[i]; + switch (plink->type) { + case CONSTANT: + if ((&prec->noa)[i] < 2) { + if (recGblInitConstantLink(plink, (&prec->fta)[i], (&prec->a)[i])) { + prec->udf = FALSE; + } else + prec->udf = TRUE; + } + break; + + case PV_LINK: + case CA_LINK: + case DB_LINK: + break; + + default: + recGblRecordError(S_db_badField, (void *)prec, + "aSubRecord(init_record) Illegal INPUT LINK"); + status = S_db_badField; + break; + } + } + + if (status) + return status; + + /* Initialize Output Links */ + for (i = 0; i < NUM_ARGS; i++) { + switch ((&prec->outa)[i].type) { + case CONSTANT: + case PV_LINK: + case CA_LINK: + case DB_LINK: + break; + + default: + recGblRecordError(S_db_badField, (void *)prec, + "aSubRecord(init_record) Illegal OUTPUT LINK"); + status = S_db_badField; + } + } + if (status) + return status; + + /* Call the user initialization routine if there is one */ + if (prec->inam[0] != 0) { + pfunc = (GENFUNCPTR)registryFunctionFind(prec->inam); + if (pfunc) { + pfunc(prec); + } else { + recGblRecordError(S_db_BadSub, (void *)prec, + "aSubRecord::init_record - INAM subr not found"); + return S_db_BadSub; + } + } + + if (prec->lflg == aSubLFLG_IGNORE && + prec->snam[0] != 0) { + pfunc = (GENFUNCPTR)registryFunctionFind(prec->snam); + if (pfunc) + prec->sadr = pfunc; + else { + recGblRecordError(S_db_BadSub, (void *)prec, + "aSubRecord::init_record - SNAM subr not found"); + return S_db_BadSub; + } + } + strcpy(prec->onam, prec->snam); + prec->oval = prec->val; + return 0; +} + + +static long initFields(epicsEnum16 *pft, epicsUInt32 *pno, epicsUInt32 *pne, + const char **fldnames, void **pval, void **povl) +{ + int i; + long status = 0; + + for (i = 0; i < NUM_ARGS; i++, pft++, pno++, pne++, pval++) { + epicsUInt32 num; + epicsUInt32 flen; + + if (*pft > DBF_ENUM) + *pft = DBF_CHAR; + + if (*pno == 0) + *pno = 1; + + flen = dbValueSize(*pft); + num = *pno * flen; + + if (num > MAX_ARRAY_SIZE) { + epicsPrintf("Link %s - Array too large! %d Bytes\n", fldnames[i], num); + *pno = num = 0; + status = S_db_errArg; + } else + *pval = (char *)callocMustSucceed(*pno, flen, + "aSubRecord::init_record"); + + *pne = *pno; + + if (povl) { + if (num) + *povl = (char *)callocMustSucceed(*pno, flen, + "aSubRecord::init_record"); + povl++; + } + } + return status; +} + + +static long process(aSubRecord *prec) +{ + int pact = prec->pact; + long status = 0; + + if (!pact) { + prec->pact = TRUE; + status = fetch_values(prec); + prec->pact = FALSE; + } + + if (!status) { + status = do_sub(prec); + prec->val = status; + } + + if (!pact && prec->pact) + return 0; + + prec->pact = TRUE; + + /* Push the output link values */ + if (!status) { + int i; + for (i = 0; i < NUM_ARGS; i++) + dbPutLink(&(&prec->outa)[i], (&prec->ftva)[i], (&prec->vala)[i], + (&prec->neva)[i]); + } + + recGblGetTimeStamp(prec); + monitor(prec); + recGblFwdLink(prec); + prec->pact = FALSE; + + return 0; +} + +static long fetch_values(aSubRecord *prec) +{ + long status; + int i; + + if (prec->lflg == aSubLFLG_READ) { + /* Get the Subroutine Name and look it up if changed */ + status = dbGetLink(&prec->subl, DBR_STRING, prec->snam, 0, 0); + if (status) + return status; + + if (prec->snam[0] != 0 && + strcmp(prec->snam, prec->onam)) { + GENFUNCPTR pfunc = (GENFUNCPTR)registryFunctionFind(prec->snam); + + if (!pfunc) + return S_db_BadSub; + + prec->sadr = pfunc; + strcpy(prec->onam, prec->snam); + } + } + + /* Get the input link values */ + for (i = 0; i < NUM_ARGS; i++) { + long nRequest = (&prec->noa)[i]; + status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0, + &nRequest); + if (nRequest > 0) + (&prec->nea)[i] = nRequest; + if (status) + return status; + } + return 0; +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + aSubRecord *prec = (aSubRecord *)paddr->precord; + + *precision = prec->prec; + recGblGetPrec(paddr, precision); + return 0; +} + + +static void monitor(aSubRecord *prec) +{ + int i; + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec) | DBE_VALUE | DBE_LOG; + + /* Post events for VAL field */ + if (prec->val != prec->oval) { + db_post_events(prec, &prec->val, monitor_mask); + prec->oval = prec->val; + } + + /* Event posting on VAL arrays depends on the setting of prec->eflg */ + switch (prec->eflg) { + case aSubEFLG_NEVER: + break; + case aSubEFLG_ON_CHANGE: + for (i = 0; i < NUM_ARGS; i++) { + epicsUInt32 alen = dbValueSize((&prec->ftva)[i]) * (&prec->neva)[i]; + void *povl = (&prec->ovla)[i]; + void *pval = (&prec->vala)[i]; + if (memcmp(povl, pval, alen)) { + memcpy(povl, pval, alen); + db_post_events(prec, pval, monitor_mask); + } + } + break; + case aSubEFLG_ALWAYS: + for (i = 0; i < NUM_ARGS; i++) + db_post_events(prec, (&prec->vala)[i], monitor_mask); + break; + } + return; +} + + +static long do_sub(aSubRecord *prec) +{ + GENFUNCPTR pfunc = prec->sadr; + long status; + + if (prec->snam[0] == 0) + return 0; + + if (pfunc == NULL) { + recGblSetSevr(prec, BAD_SUB_ALARM, INVALID_ALARM); + return S_db_BadSub; + } + status = pfunc(prec); + if (status < 0) + recGblSetSevr(prec, SOFT_ALARM, prec->brsv); + else + prec->udf = FALSE; + + return status; +} + + +static long cvt_dbaddr(DBADDR *paddr) +{ + aSubRecord *prec = (aSubRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex >= aSubRecordA && + fieldIndex <= aSubRecordU) { + int offset = fieldIndex - aSubRecordA; + + paddr->pfield = (&prec->a )[offset]; + paddr->no_elements = (&prec->noa)[offset]; + paddr->field_type = (&prec->fta)[offset]; + } + else if (fieldIndex >= aSubRecordVALA && + fieldIndex <= aSubRecordVALU) { + int offset = fieldIndex - aSubRecordVALA; + + paddr->pfield = (&prec->vala)[offset]; + paddr->no_elements = (&prec->nova)[offset]; + paddr->field_type = (&prec->ftva)[offset]; + } + else { + errlogPrintf("aSubRecord::cvt_dbaddr called for %s.%s\n", + prec->name, paddr->pfldDes->name); + return 0; + } + paddr->dbr_field_type = paddr->field_type; + paddr->field_size = dbValueSize(paddr->field_type); + return 0; +} + + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + aSubRecord *prec = (aSubRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex >= aSubRecordA && + fieldIndex <= aSubRecordU) { + *no_elements = (&prec->nea)[fieldIndex - aSubRecordA]; + } + else if (fieldIndex >= aSubRecordVALA && + fieldIndex <= aSubRecordVALU) { + *no_elements = (&prec->neva)[fieldIndex - aSubRecordVALA]; + } + else { + errlogPrintf("aSubRecord::get_array_info called for %s.%s\n", + prec->name, paddr->pfldDes->name); + } + *offset = 0; + + return 0; +} + + +static long put_array_info(DBADDR *paddr, long nNew) +{ + aSubRecord *prec = (aSubRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex >= aSubRecordA && + fieldIndex <= aSubRecordU) { + (&prec->nea)[fieldIndex - aSubRecordA] = nNew; + } + else if (fieldIndex >= aSubRecordVALA && + fieldIndex <= aSubRecordVALU) { + (&prec->neva)[fieldIndex - aSubRecordVALA] = nNew; + } + else { + errlogPrintf("aSubRecord::put_array_info called for %s.%s\n", + prec->name, paddr->pfldDes->name); + } + return 0; +} + + +static long special(DBADDR *paddr, int after) +{ + aSubRecord *prec = (aSubRecord *)paddr->precord; + + if (after && + prec->lflg == aSubLFLG_IGNORE) { + if (prec->snam[0] == 0) + return 0; + + prec->sadr = (GENFUNCPTR)registryFunctionFind(prec->snam); + if (!prec->sadr) { + recGblRecordError(S_db_BadSub, (void *)prec, prec->snam); + return S_db_BadSub; + } + } + return 0; +} diff --git a/src/rec/aSubRecord.dbd b/src/rec/aSubRecord.dbd new file mode 100644 index 000000000..7c55adea0 --- /dev/null +++ b/src/rec/aSubRecord.dbd @@ -0,0 +1,1636 @@ +menu(aSubLFLG) { + choice(aSubLFLG_IGNORE,"IGNORE") + choice(aSubLFLG_READ,"READ") +} + +menu(aSubEFLG) { + choice(aSubEFLG_NEVER,"NEVER") + choice(aSubEFLG_ON_CHANGE,"ON CHANGE") + choice(aSubEFLG_ALWAYS,"ALWAYS") +} + +recordtype(aSub) { + include "dbCommon.dbd" + field(VAL,DBF_LONG) { + prompt("Subr. return value") + asl(ASL0) + } + field(OVAL,DBF_LONG) { + prompt("Old return value") + special(SPC_NOMOD) + interest(3) + } + field(INAM,DBF_STRING) { + prompt("Initialize Subr. Name") + promptgroup(GUI_SUB) + special(SPC_NOMOD) + interest(1) + size(41) + } + field(LFLG,DBF_MENU) { + prompt("Subr. Input Enable") + promptgroup(GUI_SUB) + interest(1) + menu(aSubLFLG) + } + field(SUBL,DBF_INLINK) { + prompt("Subroutine Name Link") + promptgroup(GUI_SUB) + special(SPC_NOMOD) + interest(1) + } + field(SNAM,DBF_STRING) { + prompt("Process Subr. Name") + promptgroup(GUI_SUB) + special(SPC_MOD) + interest(1) + size(41) + } + field(ONAM,DBF_STRING) { + prompt("Old Subr. Name") + promptgroup(GUI_SUB) + special(SPC_NOMOD) + interest(3) + size(41) + } + %struct aSubRecord; + field(SADR,DBF_NOACCESS) { + prompt("Subroutine Address") + special(SPC_NOMOD) + interest(2) + extra("long (*sadr)(struct aSubRecord *)") + } + field(BRSV,DBF_MENU) { + prompt("Bad Return Severity") + promptgroup(GUI_SUB) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(EFLG,DBF_MENU) { + prompt("Output Event Flag") + promptgroup(GUI_OUTPUT) + interest(1) + menu(aSubEFLG) + initial("1") + } + field(INPA,DBF_INLINK) { + prompt("Input Link A") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input Link B") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input Link C") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input Link D") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input Link E") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input Link F") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input Link G") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input Link H") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input Link I") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input Link J") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input Link K") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input Link L") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPM,DBF_INLINK) { + prompt("Input Link M") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input Link N") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input Link O") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input Link P") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Link Q") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input Link R") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input Link S") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input Link T") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input Link U") + promptgroup(GUI_INPUTS) + interest(1) + } + field(A,DBF_NOACCESS) { + prompt("Input value A") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *a") + } + field(B,DBF_NOACCESS) { + prompt("Input value B") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *b") + } + field(C,DBF_NOACCESS) { + prompt("Input value C") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *c") + } + field(D,DBF_NOACCESS) { + prompt("Input value D") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *d") + } + field(E,DBF_NOACCESS) { + prompt("Input value E") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *e") + } + field(F,DBF_NOACCESS) { + prompt("Input value F") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *f") + } + field(G,DBF_NOACCESS) { + prompt("Input value G") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *g") + } + field(H,DBF_NOACCESS) { + prompt("Input value H") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *h") + } + field(I,DBF_NOACCESS) { + prompt("Input value I") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *i") + } + field(J,DBF_NOACCESS) { + prompt("Input value J") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *j") + } + field(K,DBF_NOACCESS) { + prompt("Input value K") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *k") + } + field(L,DBF_NOACCESS) { + prompt("Input value L") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *l") + } + field(M,DBF_NOACCESS) { + prompt("Input value M") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *m") + } + field(N,DBF_NOACCESS) { + prompt("Input value N") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *n") + } + field(O,DBF_NOACCESS) { + prompt("Input value O") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *o") + } + field(P,DBF_NOACCESS) { + prompt("Input value P") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *p") + } + field(Q,DBF_NOACCESS) { + prompt("Input value Q") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *q") + } + field(R,DBF_NOACCESS) { + prompt("Input value R") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *r") + } + field(S,DBF_NOACCESS) { + prompt("Input value S") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *s") + } + field(T,DBF_NOACCESS) { + prompt("Input value T") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *t") + } + field(U,DBF_NOACCESS) { + prompt("Input value U") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *u") + } + field(FTA,DBF_MENU) { + prompt("Type of A") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTB,DBF_MENU) { + prompt("Type of B") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTC,DBF_MENU) { + prompt("Type of C") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTD,DBF_MENU) { + prompt("Type of D") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTE,DBF_MENU) { + prompt("Type of E") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTF,DBF_MENU) { + prompt("Type of F") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTG,DBF_MENU) { + prompt("Type of G") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTH,DBF_MENU) { + prompt("Type of H") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTI,DBF_MENU) { + prompt("Type of I") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTJ,DBF_MENU) { + prompt("Type of J") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTK,DBF_MENU) { + prompt("Type of K") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTL,DBF_MENU) { + prompt("Type of L") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTM,DBF_MENU) { + prompt("Type of M") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTN,DBF_MENU) { + prompt("Type of N") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTO,DBF_MENU) { + prompt("Type of O") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTP,DBF_MENU) { + prompt("Type of P") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTQ,DBF_MENU) { + prompt("Type of Q") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTR,DBF_MENU) { + prompt("Type of R") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTS,DBF_MENU) { + prompt("Type of S") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTT,DBF_MENU) { + prompt("Type of T") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTU,DBF_MENU) { + prompt("Type of U") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(NOA,DBF_ULONG) { + prompt("Max. elements in A") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOB,DBF_ULONG) { + prompt("Max. elements in B") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOC,DBF_ULONG) { + prompt("Max. elements in C") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOD,DBF_ULONG) { + prompt("Max. elements in D") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOE,DBF_ULONG) { + prompt("Max. elements in E") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOF,DBF_ULONG) { + prompt("Max. elements in F") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOG,DBF_ULONG) { + prompt("Max. elements in G") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOH,DBF_ULONG) { + prompt("Max. elements in H") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOI,DBF_ULONG) { + prompt("Max. elements in I") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOJ,DBF_ULONG) { + prompt("Max. elements in J") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOK,DBF_ULONG) { + prompt("Max. elements in K") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOL,DBF_ULONG) { + prompt("Max. elements in L") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOM,DBF_ULONG) { + prompt("Max. elements in M") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NON,DBF_ULONG) { + prompt("Max. elements in N") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOO,DBF_ULONG) { + prompt("Max. elements in O") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOP,DBF_ULONG) { + prompt("Max. elements in P") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOQ,DBF_ULONG) { + prompt("Max. elements in Q") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOR,DBF_ULONG) { + prompt("Max. elements in R") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOS,DBF_ULONG) { + prompt("Max. elements in S") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOT,DBF_ULONG) { + prompt("Max. elements in T") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOU,DBF_ULONG) { + prompt("Max. elements in U") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NEA,DBF_ULONG) { + prompt("Num. elements in A") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEB,DBF_ULONG) { + prompt("Num. elements in B") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEC,DBF_ULONG) { + prompt("Num. elements in C") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NED,DBF_ULONG) { + prompt("Num. elements in D") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEE,DBF_ULONG) { + prompt("Num. elements in E") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEF,DBF_ULONG) { + prompt("Num. elements in F") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEG,DBF_ULONG) { + prompt("Num. elements in G") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEH,DBF_ULONG) { + prompt("Num. elements in H") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEI,DBF_ULONG) { + prompt("Num. elements in I") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEJ,DBF_ULONG) { + prompt("Num. elements in J") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEK,DBF_ULONG) { + prompt("Num. elements in K") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEL,DBF_ULONG) { + prompt("Num. elements in L") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEM,DBF_ULONG) { + prompt("Num. elements in M") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEN,DBF_ULONG) { + prompt("Num. elements in N") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEO,DBF_ULONG) { + prompt("Num. elements in O") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEP,DBF_ULONG) { + prompt("Num. elements in P") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEQ,DBF_ULONG) { + prompt("Num. elements in Q") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NER,DBF_ULONG) { + prompt("Num. elements in R") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NES,DBF_ULONG) { + prompt("Num. elements in S") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NET,DBF_ULONG) { + prompt("Num. elements in T") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEU,DBF_ULONG) { + prompt("Num. elements in U") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(OUTA,DBF_OUTLINK) { + prompt("Output Link A") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTB,DBF_OUTLINK) { + prompt("Output Link B") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTC,DBF_OUTLINK) { + prompt("Output Link C") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTD,DBF_OUTLINK) { + prompt("Output Link D") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTE,DBF_OUTLINK) { + prompt("Output Link E") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTF,DBF_OUTLINK) { + prompt("Output Link F") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTG,DBF_OUTLINK) { + prompt("Output Link G") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTH,DBF_OUTLINK) { + prompt("Output Link H") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTI,DBF_OUTLINK) { + prompt("Output Link I") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTJ,DBF_OUTLINK) { + prompt("Output Link J") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTK,DBF_OUTLINK) { + prompt("Output Link K") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTL,DBF_OUTLINK) { + prompt("Output Link L") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTM,DBF_OUTLINK) { + prompt("Output Link M") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTN,DBF_OUTLINK) { + prompt("Output Link N") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTO,DBF_OUTLINK) { + prompt("Output Link O") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTP,DBF_OUTLINK) { + prompt("Output Link P") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTQ,DBF_OUTLINK) { + prompt("Output Link Q") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTR,DBF_OUTLINK) { + prompt("Output Link R") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTS,DBF_OUTLINK) { + prompt("Output Link S") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTT,DBF_OUTLINK) { + prompt("Output Link T") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTU,DBF_OUTLINK) { + prompt("Output Link U") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(VALA,DBF_NOACCESS) { + prompt("Output value A") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *vala") + } + field(VALB,DBF_NOACCESS) { + prompt("Output value B") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valb") + } + field(VALC,DBF_NOACCESS) { + prompt("Output value C") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valc") + } + field(VALD,DBF_NOACCESS) { + prompt("Output value D") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *vald") + } + field(VALE,DBF_NOACCESS) { + prompt("Output value E") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *vale") + } + field(VALF,DBF_NOACCESS) { + prompt("Output value F") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valf") + } + field(VALG,DBF_NOACCESS) { + prompt("Output value G") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valg") + } + field(VALH,DBF_NOACCESS) { + prompt("Output value H") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valh") + } + field(VALI,DBF_NOACCESS) { + prompt("Output value I") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *vali") + } + field(VALJ,DBF_NOACCESS) { + prompt("Output value J") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valj") + } + field(VALK,DBF_NOACCESS) { + prompt("Output value K") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valk") + } + field(VALL,DBF_NOACCESS) { + prompt("Output value L") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *vall") + } + field(VALM,DBF_NOACCESS) { + prompt("Output value M") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valm") + } + field(VALN,DBF_NOACCESS) { + prompt("Output value N") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valn") + } + field(VALO,DBF_NOACCESS) { + prompt("Output value O") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valo") + } + field(VALP,DBF_NOACCESS) { + prompt("Output value P") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valp") + } + field(VALQ,DBF_NOACCESS) { + prompt("Output value Q") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valq") + } + field(VALR,DBF_NOACCESS) { + prompt("Output value R") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valr") + } + field(VALS,DBF_NOACCESS) { + prompt("Output value S") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *vals") + } + field(VALT,DBF_NOACCESS) { + prompt("Output value T") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valt") + } + field(VALU,DBF_NOACCESS) { + prompt("Output value U") + asl(ASL0) + special(SPC_DBADDR) + interest(2) + extra("void *valu") + } + field(OVLA,DBF_NOACCESS) { + prompt("Old Output A") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovla") + } + field(OVLB,DBF_NOACCESS) { + prompt("Old Output B") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlb") + } + field(OVLC,DBF_NOACCESS) { + prompt("Old Output C") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlc") + } + field(OVLD,DBF_NOACCESS) { + prompt("Old Output D") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovld") + } + field(OVLE,DBF_NOACCESS) { + prompt("Old Output E") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovle") + } + field(OVLF,DBF_NOACCESS) { + prompt("Old Output F") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlf") + } + field(OVLG,DBF_NOACCESS) { + prompt("Old Output G") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlg") + } + field(OVLH,DBF_NOACCESS) { + prompt("Old Output H") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlh") + } + field(OVLI,DBF_NOACCESS) { + prompt("Old Output I") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovli") + } + field(OVLJ,DBF_NOACCESS) { + prompt("Old Output J") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlj") + } + field(OVLK,DBF_NOACCESS) { + prompt("Old Output K") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlk") + } + field(OVLL,DBF_NOACCESS) { + prompt("Old Output L") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovll") + } + field(OVLM,DBF_NOACCESS) { + prompt("Old Output M") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlm") + } + field(OVLN,DBF_NOACCESS) { + prompt("Old Output N") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovln") + } + field(OVLO,DBF_NOACCESS) { + prompt("Old Output O") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlo") + } + field(OVLP,DBF_NOACCESS) { + prompt("Old Output P") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlp") + } + field(OVLQ,DBF_NOACCESS) { + prompt("Old Output Q") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlq") + } + field(OVLR,DBF_NOACCESS) { + prompt("Old Output R") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlr") + } + field(OVLS,DBF_NOACCESS) { + prompt("Old Output S") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovls") + } + field(OVLT,DBF_NOACCESS) { + prompt("Old Output T") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlt") + } + field(OVLU,DBF_NOACCESS) { + prompt("Old Output U") + asl(ASL0) + special(SPC_NOMOD) + interest(4) extra("void *ovlu") + } + field(FTVA,DBF_MENU) { + prompt("Type of VALA") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVB,DBF_MENU) { + prompt("Type of VALB") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVC,DBF_MENU) { + prompt("Type of VALC") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVD,DBF_MENU) { + prompt("Type of VALD") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVE,DBF_MENU) { + prompt("Type of VALE") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVF,DBF_MENU) { + prompt("Type of VALF") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVG,DBF_MENU) { + prompt("Type of VALG") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVH,DBF_MENU) { + prompt("Type of VALH") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVI,DBF_MENU) { + prompt("Type of VALI") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVJ,DBF_MENU) { + prompt("Type of VALJ") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVK,DBF_MENU) { + prompt("Type of VALK") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVL,DBF_MENU) { + prompt("Type of VALL") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVM,DBF_MENU) { + prompt("Type of VALM") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVN,DBF_MENU) { + prompt("Type of VALN") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVO,DBF_MENU) { + prompt("Type of VALO") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVP,DBF_MENU) { + prompt("Type of VALP") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVQ,DBF_MENU) { + prompt("Type of VALQ") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVR,DBF_MENU) { + prompt("Type of VALR") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVS,DBF_MENU) { + prompt("Type of VALS") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVT,DBF_MENU) { + prompt("Type of VALT") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(FTVU,DBF_MENU) { + prompt("Type of VALU") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("DOUBLE") + menu(menuFtype) + } + field(NOVA,DBF_ULONG) { + prompt("Max. elements in VALA") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVB,DBF_ULONG) { + prompt("Max. elements in VALB") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVC,DBF_ULONG) { + prompt("Max. elements in VALC") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVD,DBF_ULONG) { + prompt("Max. elements in VALD") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVE,DBF_ULONG) { + prompt("Max. elements in VALE") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVF,DBF_ULONG) { + prompt("Max. elements in VALF") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVG,DBF_ULONG) { + prompt("Max. elements in VALG") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVH,DBF_ULONG) { + prompt("Max. elements in VAlH") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVI,DBF_ULONG) { + prompt("Max. elements in VALI") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVJ,DBF_ULONG) { + prompt("Max. elements in VALJ") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVK,DBF_ULONG) { + prompt("Max. elements in VALK") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVL,DBF_ULONG) { + prompt("Max. elements in VALL") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVM,DBF_ULONG) { + prompt("Max. elements in VALM") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVN,DBF_ULONG) { + prompt("Max. elements in VALN") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVO,DBF_ULONG) { + prompt("Max. elements in VALO") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVP,DBF_ULONG) { + prompt("Max. elements in VALP") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVQ,DBF_ULONG) { + prompt("Max. elements in VALQ") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVR,DBF_ULONG) { + prompt("Max. elements in VALR") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVS,DBF_ULONG) { + prompt("Max. elements in VALS") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVT,DBF_ULONG) { + prompt("Max. elements in VALT") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NOVU,DBF_ULONG) { + prompt("Max. elements in VALU") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NEVA,DBF_ULONG) { + prompt("Num. elements in VALA") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVB,DBF_ULONG) { + prompt("Num. elements in VALB") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVC,DBF_ULONG) { + prompt("Num. elements in VALC") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVD,DBF_ULONG) { + prompt("Num. elements in VALD") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVE,DBF_ULONG) { + prompt("Num. elements in VALE") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVF,DBF_ULONG) { + prompt("Num. elements in VALF") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVG,DBF_ULONG) { + prompt("Num. elements in VALG") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVH,DBF_ULONG) { + prompt("Num. elements in VAlH") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVI,DBF_ULONG) { + prompt("Num. elements in VALI") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVJ,DBF_ULONG) { + prompt("Num. elements in VALJ") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVK,DBF_ULONG) { + prompt("Num. elements in VALK") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVL,DBF_ULONG) { + prompt("Num. elements in VALL") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVM,DBF_ULONG) { + prompt("Num. elements in VALM") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVN,DBF_ULONG) { + prompt("Num. elements in VALN") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVO,DBF_ULONG) { + prompt("Num. elements in VALO") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVP,DBF_ULONG) { + prompt("Num. elements in VALP") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVQ,DBF_ULONG) { + prompt("Num. elements in VALQ") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVR,DBF_ULONG) { + prompt("Num. elements in VALR") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVS,DBF_ULONG) { + prompt("Num. elements in VALS") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVT,DBF_ULONG) { + prompt("Num. elements in VALT") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } + field(NEVU,DBF_ULONG) { + prompt("Num. elements in VALU") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(3) + initial("1") + } +} diff --git a/src/rec/aaiRecord.c b/src/rec/aaiRecord.c new file mode 100644 index 000000000..5c88791c4 --- /dev/null +++ b/src/rec/aaiRecord.c @@ -0,0 +1,322 @@ +/*************************************************************************\ +* Copyright (c) 2002 Southeastern Universities Research Association, as +* Operator of Thomas Jefferson National Accelerator Facility. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* recAai.c */ + +/* recAai.c - Record Support Routines for Array Analog In records */ +/* + * Original Author: Dave Barker + * + * C E B A F + * + * Continuous Electron Beam Accelerator Facility + * Newport News, Virginia, USA. + * + * Copyright SURA CEBAF 1993. + * + * Current Author: Dirk Zimoch + * Date: 27-MAY-2010 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsString.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "dbScan.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "aaiRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(aaiRecord *, int); +static long process(aaiRecord *); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +#define get_alarm_double NULL + +rset aaiRSET={ + 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,aaiRSET); + +struct aaidset { /* aai dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_aai; /*returns: (-1,0)=>(failure,success)*/ +}; + +static void monitor(aaiRecord *); +static long readValue(aaiRecord *); + +static long init_record(aaiRecord *prec, int pass) +{ + long status; + struct aaidset *pdset = (struct aaidset *)(prec->dset); + + /* must have dset defined */ + if (!pdset) { + recGblRecordError(S_dev_noDSET, prec, "aai: init_record"); + return S_dev_noDSET; + } + + if (pass == 0) { + if (prec->nelm <= 0) + prec->nelm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + if (prec->nelm == 1) { + prec->nord = 1; + } else { + prec->nord = 0; + } + + /* we must call pdset->init_record in pass 0 + because it may set prec->bptr which must + not change after links are established before pass 1 + */ + + if (pdset->init_record) { + /* init_record may set the bptr to point to the data */ + if ((status = pdset->init_record(prec))) + return status; + } + if (!prec->bptr) { + /* device support did not allocate memory so we must do it */ + prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), + "aai: buffer calloc failed"); + } + return 0; + } + + /* SIML must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + /* must have read_aai function defined */ + if (pdset->number < 5 || pdset->read_aai == NULL) { + recGblRecordError(S_dev_missingSup, prec, "aai: init_record"); + return S_dev_missingSup; + } + return 0; +} + +static long process(aaiRecord *prec) +{ + struct aaidset *pdset = (struct aaidset *)(prec->dset); + long status; + unsigned char pact = prec->pact; + + if (pdset == NULL || pdset->read_aai == NULL) { + prec->pact = TRUE; + recGblRecordError(S_dev_missingSup, prec, "read_aai"); + return S_dev_missingSup; + } + + status = readValue(prec); /* read the new value */ + if (!pact && prec->pact) return 0; + prec->pact = TRUE; + + prec->udf = FALSE; + recGblGetTimeStamp(prec); + + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact = FALSE; + return status; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + aaiRecord *prec = (aaiRecord *)paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->nelm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + aaiRecord *prec = (aaiRecord *)paddr->precord; + + *no_elements = prec->nord; + *offset = 0; + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + aaiRecord *prec = (aaiRecord *)paddr->precord; + + prec->nord = nNew; + if (prec->nord > prec->nelm) + prec->nord = prec->nelm; + return 0; +} + +static long get_units(DBADDR *paddr, char *units) +{ + aaiRecord *prec = (aaiRecord *)paddr->precord; + + strncpy(units, prec->egu, DB_UNITS_SIZE); + return 0; +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + aaiRecord *prec = (aaiRecord *)paddr->precord; + + *precision = prec->prec; + if (paddr->pfield == prec->bptr) return 0; + recGblGetPrec(paddr, precision); + return 0; +} + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + aaiRecord *prec = (aaiRecord *)paddr->precord; + + if (paddr->pfield == prec->bptr) { + 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) +{ + aaiRecord *prec = (aaiRecord *)paddr->precord; + + if (paddr->pfield == prec->bptr) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + } else recGblGetControlDouble(paddr, pcd); + return 0; +} + +static void monitor(aaiRecord *prec) +{ + unsigned short monitor_mask; + unsigned int hash = 0; + + monitor_mask = recGblResetAlarms(prec); + + if (prec->mpst == aaiPOST_Always) + monitor_mask |= DBE_VALUE; + if (prec->apst == aaiPOST_Always) + monitor_mask |= DBE_LOG; + + /* Calculate hash if we are interested in OnChange events. */ + if ((prec->mpst == aaiPOST_OnChange) || + (prec->apst == aaiPOST_OnChange)) { + hash = epicsMemHash(prec->bptr, + prec->nord * dbValueSize(prec->ftvl), 0); + + /* Only post OnChange values if the hash is different. */ + if (hash != prec->hash) { + if (prec->mpst == aaiPOST_OnChange) + monitor_mask |= DBE_VALUE; + if (prec->apst == aaiPOST_OnChange) + monitor_mask |= DBE_LOG; + + /* Store hash for next process. */ + prec->hash = hash; + /* Post HASH. */ + db_post_events(prec, &prec->hash, DBE_VALUE); + } + } + + if (monitor_mask) + db_post_events(prec, prec->bptr, monitor_mask); +} + +static long readValue(aaiRecord *prec) +{ + long status; + struct aaidset *pdset = (struct aaidset *)prec->dset; + + if (prec->pact == TRUE){ + status = pdset->read_aai(prec); + return status; + } + + status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); + if (status) + return status; + + if (prec->simm == menuYesNoNO){ + return pdset->read_aai(prec); + } + + if (prec->simm == menuYesNoYES){ + /* Device suport is responsible for buffer + which might be read-only so we may not be + allowed to call dbGetLink on it. + Maybe also device support has an advanced + simulation mode. + Thus call device now. + */ + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + return pdset->read_aai(prec); + } + + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + return -1; +} + diff --git a/src/rec/aaiRecord.dbd b/src/rec/aaiRecord.dbd new file mode 100644 index 000000000..e94fecbac --- /dev/null +++ b/src/rec/aaiRecord.dbd @@ -0,0 +1,110 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(aaiPOST) { + choice(aaiPOST_Always,"Always") + choice(aaiPOST_OnChange,"On Change") +} +recordtype(aai) { + include "dbCommon.dbd" + field(VAL,DBF_NOACCESS) { + prompt("Value") + asl(ASL0) + special(SPC_DBADDR) + pp(TRUE) + extra("void * val") + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_ALARMS) + interest(1) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_BITS1) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units Name") + promptgroup(GUI_BITS2) + interest(1) + size(16) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_CALC) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_CLOCK) + interest(1) + } + field(NELM,DBF_ULONG) { + prompt("Number of Elements") + promptgroup(GUI_COMPRESS) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(FTVL,DBF_MENU) { + prompt("Field Type of Value") + promptgroup(GUI_CONVERT) + special(SPC_NOMOD) + interest(1) + menu(menuFtype) + } + field(NORD,DBF_ULONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(BPTR,DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + interest(4) + extra("void * bptr") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_HIST) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(MPST,DBF_MENU) { + prompt("Post Value Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(aaiPOST) + } + field(APST,DBF_MENU) { + prompt("Post Archive Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(aaiPOST) + } + field(HASH,DBF_ULONG) { + prompt("Hash of OnChange data.") + interest(3) + } +} diff --git a/src/rec/aaoRecord.c b/src/rec/aaoRecord.c new file mode 100644 index 000000000..695f8e1b1 --- /dev/null +++ b/src/rec/aaoRecord.c @@ -0,0 +1,320 @@ +/*************************************************************************\ +* Copyright (c) 2002 Southeastern Universities Research Association, as +* Operator of Thomas Jefferson National Accelerator Facility. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* recAao.c */ + +/* recAao.c - Record Support Routines for Array Analog Out records */ +/* + * Original Author: Dave Barker + * + * C E B A F + * + * Continuous Electron Beam Accelerator Facility + * Newport News, Virginia, USA. + * + * Copyright SURA CEBAF 1993. + * + * Current Author: Dirk Zimoch + * Date: 27-MAY-2010 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsString.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "dbScan.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "aaoRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(aaoRecord *, int); +static long process(aaoRecord *); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +#define get_alarm_double NULL + +rset aaoRSET={ + 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,aaoRSET); + +struct aaodset { /* aao dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_aao; /*returns: (-1,0)=>(failure,success)*/ +}; + +static void monitor(aaoRecord *); +static long writeValue(aaoRecord *); + +static long init_record(aaoRecord *prec, int pass) +{ + long status; + struct aaodset *pdset = (struct aaodset *)(prec->dset); + + /* must have dset defined */ + if (!pdset) { + recGblRecordError(S_dev_noDSET, prec, "aao: init_record"); + return S_dev_noDSET; + } + + if (pass == 0) { + if (prec->nelm <= 0) + prec->nelm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + if (prec->nelm == 1) { + prec->nord = 1; + } else { + prec->nord = 0; + } + + /* we must call pdset->init_record in pass 0 + because it may set prec->bptr which must + not change after links are established before pass 1 + */ + + if (pdset->init_record) { + /* init_record may set the bptr to point to the data */ + if ((status = pdset->init_record(prec))) + return status; + } + if (!prec->bptr) { + /* device support did not allocate memory so we must do it */ + prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), + "aao: buffer calloc failed"); + } + return 0; + } + + /* SIML must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + /* must have write_aao function defined */ + if (pdset->number < 5 || pdset->write_aao == NULL) { + recGblRecordError(S_dev_missingSup, prec, "aao: init_record"); + return S_dev_missingSup; + } + return 0; +} + +static long process(aaoRecord *prec) +{ + struct aaodset *pdset = (struct aaodset *)(prec->dset); + long status; + unsigned char pact = prec->pact; + + if (pdset == NULL || pdset->write_aao == NULL) { + prec->pact = TRUE; + recGblRecordError(S_dev_missingSup, prec, "write_aao"); + return S_dev_missingSup; + } + + status = writeValue(prec); /* write the data */ + if (!pact && prec->pact) return 0; + prec->pact = TRUE; + + prec->udf = FALSE; + recGblGetTimeStamp(prec); + + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact = FALSE; + return status; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + aaoRecord *prec = (aaoRecord *)paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->nelm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + aaoRecord *prec = (aaoRecord *)paddr->precord; + + *no_elements = prec->nord; + *offset = 0; + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + aaoRecord *prec = (aaoRecord *)paddr->precord; + + prec->nord = nNew; + if (prec->nord > prec->nelm) + prec->nord = prec->nelm; + return 0; +} + +static long get_units(DBADDR *paddr, char *units) +{ + aaoRecord *prec = (aaoRecord *)paddr->precord; + + strncpy(units, prec->egu, DB_UNITS_SIZE); + return 0; +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + aaoRecord *prec = (aaoRecord *)paddr->precord; + + *precision = prec->prec; + if (paddr->pfield == (void *)prec->bptr) return 0; + recGblGetPrec(paddr, precision); + return 0; +} + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + aaoRecord *prec = (aaoRecord *)paddr->precord; + + if (paddr->pfield == prec->bptr) { + 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) +{ + aaoRecord *prec = (aaoRecord *)paddr->precord; + + if(paddr->pfield == prec->bptr){ + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + } else recGblGetControlDouble(paddr,pcd); + return 0; +} + +static void monitor(aaoRecord *prec) +{ + unsigned short monitor_mask; + unsigned int hash = 0; + + monitor_mask = recGblResetAlarms(prec); + + if (prec->mpst == aaoPOST_Always) + monitor_mask |= DBE_VALUE; + if (prec->apst == aaoPOST_Always) + monitor_mask |= DBE_LOG; + + /* Calculate hash if we are interested in OnChange events. */ + if ((prec->mpst == aaoPOST_OnChange) || + (prec->apst == aaoPOST_OnChange)) { + hash = epicsMemHash(prec->bptr, + prec->nord * dbValueSize(prec->ftvl), 0); + + /* Only post OnChange values if the hash is different. */ + if (hash != prec->hash) { + if (prec->mpst == aaoPOST_OnChange) + monitor_mask |= DBE_VALUE; + if (prec->apst == aaoPOST_OnChange) + monitor_mask |= DBE_LOG; + + /* Store hash for next process. */ + prec->hash = hash; + /* Post HASH. */ + db_post_events(prec, &prec->hash, DBE_VALUE); + } + } + + if (monitor_mask) + db_post_events(prec, prec->bptr, monitor_mask); +} + +static long writeValue(aaoRecord *prec) +{ + long status; + struct aaodset *pdset = (struct aaodset *)prec->dset; + + if (prec->pact == TRUE) { + /* no asyn allowed, pact true means do not process */ + return 0; + } + + status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); + if (status) + return status; + + if (prec->simm == menuYesNoNO) { + return pdset->write_aao(prec); + } + if (prec->simm == menuYesNoYES) { + /* Device suport is responsible for buffer + which might be write-only so we may not be + allowed to call dbPutLink on it. + Maybe also device support has an advanced + simulation mode. + Thus call device now. + */ + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + return pdset->write_aao(prec); + } + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + return -1; +} + diff --git a/src/rec/aaoRecord.dbd b/src/rec/aaoRecord.dbd new file mode 100644 index 000000000..60b9b773e --- /dev/null +++ b/src/rec/aaoRecord.dbd @@ -0,0 +1,110 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(aaoPOST) { + choice(aaoPOST_Always,"Always") + choice(aaoPOST_OnChange,"On Change") +} +recordtype(aao) { + include "dbCommon.dbd" + field(VAL,DBF_NOACCESS) { + prompt("Value") + asl(ASL0) + special(SPC_DBADDR) + pp(TRUE) + extra("void * val") + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_ALARMS) + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_BITS1) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units Name") + promptgroup(GUI_BITS2) + interest(1) + size(16) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_CALC) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_CLOCK) + interest(1) + } + field(NELM,DBF_ULONG) { + prompt("Number of Elements") + promptgroup(GUI_COMPRESS) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(FTVL,DBF_MENU) { + prompt("Field Type of Value") + promptgroup(GUI_CONVERT) + special(SPC_NOMOD) + interest(1) + menu(menuFtype) + } + field(NORD,DBF_ULONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(BPTR,DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + interest(4) + extra("void * bptr") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_HIST) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(MPST,DBF_MENU) { + prompt("Post Value Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(aaoPOST) + } + field(APST,DBF_MENU) { + prompt("Post Archive Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(aaoPOST) + } + field(HASH,DBF_ULONG) { + prompt("Hash of OnChange data.") + interest(3) + } +} diff --git a/src/rec/aiRecord.c b/src/rec/aiRecord.c new file mode 100644 index 000000000..657df122b --- /dev/null +++ b/src/rec/aiRecord.c @@ -0,0 +1,460 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* aiRecord.c - Record Support Routines for Analog Input records */ +/* + * Original Author: Bob Dalesio + * Date: 7-14-89 + * + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "errlog.h" +#include "epicsMath.h" +#include "alarm.h" +#include "cvtTable.h" +#include "dbAccess.h" +#include "dbScan.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "menuSimm.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#include "menuConvert.h" +#define GEN_SIZE_OFFSET +#include "aiRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(void *, int); +static long process(void *); +static long special(DBADDR *, int); +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); + +rset aiRSET={ + 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,aiRSET); + +typedef struct aidset { /* analog input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_ai;/*(0,2)=> success and convert,don't convert)*/ + /* if convert then raw value stored in rval */ + DEVSUPFUN special_linconv; +}aidset; + + +/*Following from timing system */ +/* +extern unsigned int gts_trigger_counter; +*/ + +static void checkAlarms(aiRecord *prec); +static void convert(aiRecord *prec); +static void monitor(aiRecord *prec); +static long readValue(aiRecord *prec); + +static long init_record(void *precord,int pass) +{ + aiRecord *prec = (aiRecord *)precord; + aidset *pdset; + double eoff = prec->eoff, eslo = prec->eslo; + + if (pass==0) return(0); + + /* ai.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + /* ai.siol must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siol.type == CONSTANT) { + recGblInitConstantLink(&prec->siol,DBF_DOUBLE,&prec->sval); + } + + if(!(pdset = (aidset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"ai: init_record"); + return(S_dev_noDSET); + } + /* must have read_ai function defined */ + if( (pdset->number < 6) || (pdset->read_ai == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"ai: init_record"); + return(S_dev_missingSup); + } + prec->init = TRUE; + /*The following is for old device support that doesnt know about eoff*/ + if ((prec->eslo==1.0) && (prec->eoff==0.0)) { + prec->eoff = prec->egul; + } + + if( pdset->init_record ) { + long status=(*pdset->init_record)(prec); + if (prec->linr == menuConvertSLOPE) { + prec->eoff = eoff; + prec->eslo = eslo; + } + return (status); + } + prec->mlst = prec->val; + prec->alst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + return(0); +} + +static long process(void *precord) +{ + aiRecord *prec = (aiRecord *)precord; + aidset *pdset = (aidset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->read_ai==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"read_ai"); + return(S_dev_missingSup); + } + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + if (status==0) convert(prec); + else if (status==2) status=0; + + /* check for alarms */ + checkAlarms(prec); + /* check event list */ + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->init=FALSE; + prec->pact=FALSE; + return(status); +} + +static long special(DBADDR *paddr,int after) +{ + aiRecord *prec = (aiRecord *)(paddr->precord); + aidset *pdset = (aidset *) (prec->dset); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_LINCONV): + if(pdset->number<6) { + recGblDbaddrError(S_db_noMod,paddr,"ai: special"); + return(S_db_noMod); + } + prec->init=TRUE; + if ((prec->linr == menuConvertLINEAR) && pdset->special_linconv) { + double eoff = prec->eoff; + double eslo = prec->eslo; + long status; + prec->eoff = prec->egul; + status = (*pdset->special_linconv)(prec,after); + if (eoff != prec->eoff) + db_post_events(prec, &prec->eoff, DBE_VALUE|DBE_LOG); + if (eslo != prec->eslo) + db_post_events(prec, &prec->eslo, DBE_VALUE|DBE_LOG); + return(status); + } + return(0); + default: + recGblDbaddrError(S_db_badChoice,paddr,"ai: special"); + return(S_db_badChoice); + } +} + +static long get_units(DBADDR *paddr, char *units) +{ + aiRecord *prec=(aiRecord *)paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + return(0); +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + aiRecord *prec=(aiRecord *)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) +{ + aiRecord *prec=(aiRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == aiRecordVAL + || fieldIndex == aiRecordHIHI + || fieldIndex == aiRecordHIGH + || fieldIndex == aiRecordLOW + || fieldIndex == aiRecordLOLO + || fieldIndex == aiRecordHOPR + || fieldIndex == aiRecordLOPR) { + 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) +{ + aiRecord *prec=(aiRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == aiRecordVAL + || fieldIndex == aiRecordHIHI + || fieldIndex == aiRecordHIGH + || fieldIndex == aiRecordLOW + || fieldIndex == aiRecordLOLO) { + 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) +{ + aiRecord *prec=(aiRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == aiRecordVAL) { + 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(aiRecord *prec) +{ + double val, hyst, lalm; + double alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +static void convert(aiRecord *prec) +{ + double val; + + + val = (double)prec->rval + (double)prec->roff; + /* adjust slope and offset */ + if(prec->aslo!=0.0) val*=prec->aslo; + val+=prec->aoff; + + /* convert raw to engineering units and signal units */ + switch (prec->linr) { + case menuConvertNO_CONVERSION: + break; /* do nothing*/ + + case menuConvertLINEAR: + case menuConvertSLOPE: + val = (val * prec->eslo) + prec->eoff; + break; + + default: /* must use breakpoint table */ + if (cvtRawToEngBpt(&val,prec->linr,prec->init,(void *)&prec->pbrk,&prec->lbrk)!=0) { + recGblSetSevr(prec,SOFT_ALARM,MAJOR_ALARM); + } + } + + /* apply smoothing algorithm */ + if (prec->smoo != 0.0){ + if (prec->init) prec->val = val; /* initial condition */ + prec->val = val * (1.00 - prec->smoo) + (prec->val * prec->smoo); + }else{ + prec->val = val; + } + prec->udf = isnan(prec->val); + return; +} + +static void monitor(aiRecord *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); + if(prec->oraw != prec->rval) { + db_post_events(prec,&prec->rval,monitor_mask); + prec->oraw = prec->rval; + } + } + return; +} + +static long readValue(aiRecord *prec) +{ + long status; + aidset *pdset = (aidset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_ai)(prec); + return(status); + } + + status = dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); + + if (status) + return(status); + + if (prec->simm == menuSimmNO){ + status=(*pdset->read_ai)(prec); + return(status); + } + if (prec->simm == menuSimmYES){ + status = dbGetLink(&(prec->siol),DBR_DOUBLE,&(prec->sval),0,0); + if (status==0){ + prec->val=prec->sval; + prec->udf=isnan(prec->val); + } + status=2; /* dont convert */ + } + else if (prec->simm == menuSimmRAW){ + status = dbGetLink(&(prec->siol),DBR_DOUBLE,&(prec->sval),0,0); + if (status==0) { + prec->udf=isnan(prec->sval); + if (!prec->udf) { + prec->rval=(long)floor(prec->sval); + } + } + status=0; /* convert since we've written RVAL */ + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/aiRecord.dbd b/src/rec/aiRecord.dbd new file mode 100644 index 000000000..a7518ab73 --- /dev/null +++ b/src/rec/aiRecord.dbd @@ -0,0 +1,233 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(ai) { + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Current EGU Value") + promptgroup(GUI_INPUTS) + asl(ASL0) + pp(TRUE) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LINR,DBF_MENU) { + prompt("Linearization") + promptgroup(GUI_CONVERT) + special(SPC_LINCONV) + pp(TRUE) + interest(1) + menu(menuConvert) + } + field(EGUF,DBF_DOUBLE) { + prompt("Engineer Units Full") + promptgroup(GUI_CONVERT) + special(SPC_LINCONV) + pp(TRUE) + interest(1) + } + field(EGUL,DBF_DOUBLE) { + prompt("Engineer Units Low") + promptgroup(GUI_CONVERT) + special(SPC_LINCONV) + pp(TRUE) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(AOFF,DBF_DOUBLE) { + prompt("Adjustment Offset") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(1) + } + field(ASLO,DBF_DOUBLE) { + prompt("Adjustment Slope") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(1) + initial("1") + } + field(SMOO,DBF_DOUBLE) { + prompt("Smoothing") + promptgroup(GUI_CONVERT) + interest(1) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_DOUBLE) { + 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) + } + field(ESLO,DBF_DOUBLE) { + prompt("Raw to EGU Slope") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(2) + initial("1") + } + field(EOFF,DBF_DOUBLE) { + prompt("Raw to EGU Offset") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(2) + } + field(ROFF,DBF_LONG) { + prompt("Raw Offset, obsolete") + pp(TRUE) + interest(2) + } + field(PBRK,DBF_NOACCESS) { + prompt("Ptrto brkTable") + special(SPC_NOMOD) + interest(4) + extra("void * pbrk") + } + field(INIT,DBF_SHORT) { + prompt("Initialized?") + special(SPC_NOMOD) + interest(3) + } + field(LBRK,DBF_SHORT) { + prompt("LastBreak Point") + special(SPC_NOMOD) + interest(3) + } + field(RVAL,DBF_LONG) { + prompt("Current Raw Value") + pp(TRUE) + } + field(ORAW,DBF_LONG) { + prompt("Previous Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SVAL,DBF_DOUBLE) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuSimm) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } +} diff --git a/src/rec/aoRecord.c b/src/rec/aoRecord.c new file mode 100644 index 000000000..d166c22ac --- /dev/null +++ b/src/rec/aoRecord.c @@ -0,0 +1,554 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* aoRecord.c - Record Support Routines for Analog Output records */ +/* + * Original Author: Bob Dalesio + * Date: 7-14-89 + * + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsMath.h" +#include "alarm.h" +#include "cvtTable.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "special.h" +#include "recSup.h" +#include "recGbl.h" +#include "menuConvert.h" +#include "menuOmsl.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "aoRecord.h" +#undef GEN_SIZE_OFFSET +#include "menuIvoa.h" +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(aoRecord *prec, int pass); +static long process(aoRecord *); +static long special(DBADDR *, int); +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); + +rset aoRSET={ + 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 }; + +struct aodset { /* analog input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (0,2)=>(success,success no convert)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_ao;/*(0)=>(success ) */ + DEVSUPFUN special_linconv; +}; +epicsExportAddress(rset,aoRSET); + + + +static void checkAlarms(aoRecord *); +static long fetch_value(aoRecord *, double *); +static void convert(aoRecord *, double); +static void monitor(aoRecord *); +static long writeValue(aoRecord *); + +static long init_record(aoRecord *prec, int pass) +{ + struct aodset *pdset; + double eoff = prec->eoff, eslo = prec->eslo; + double value; + + if (pass==0) return(0); + + /* ao.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + if(!(pdset = (struct aodset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"ao: init_record"); + return(S_dev_noDSET); + } + /* get the initial value if dol is a constant*/ + if (prec->dol.type == CONSTANT) { + if(recGblInitConstantLink(&prec->dol,DBF_DOUBLE,&prec->val)) + prec->udf = isnan(prec->val); + } + + /* must have write_ao function defined */ + if ((pdset->number < 6) || (pdset->write_ao ==NULL)) { + recGblRecordError(S_dev_missingSup,(void *)prec,"ao: init_record"); + return(S_dev_missingSup); + } + prec->init = TRUE; + /*The following is for old device support that doesnt know about eoff*/ + if ((prec->eslo==1.0) && (prec->eoff==0.0)) { + prec->eoff = prec->egul; + } + + if (pdset->init_record) { + long status=(*pdset->init_record)(prec); + if (prec->linr == menuConvertSLOPE) { + prec->eoff = eoff; + prec->eslo = eslo; + } + switch(status){ + case(0): /* convert */ + value = (double)prec->rval + (double)prec->roff; + if(prec->aslo!=0.0) value *= prec->aslo; + value += prec->aoff; + if (prec->linr == menuConvertNO_CONVERSION){ + ; /*do nothing*/ + } else if ((prec->linr == menuConvertLINEAR) || + (prec->linr == menuConvertSLOPE)) { + value = value*prec->eslo + prec->eoff; + }else{ + if(cvtRawToEngBpt(&value,prec->linr,prec->init, + (void *)&prec->pbrk,&prec->lbrk)!=0) break; + } + prec->val = value; + prec->udf = isnan(value); + break; + case(2): /* no convert */ + break; + default: + recGblRecordError(S_dev_badInitRet,(void *)prec,"ao: init_record"); + return(S_dev_badInitRet); + } + } + prec->oval = prec->pval = prec->val; + prec->mlst = prec->val; + prec->alst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + prec->orbv = prec->rbv; + return(0); +} + +static long process(aoRecord *prec) +{ + struct aodset *pdset = (struct aodset *)(prec->dset); + long status=0; + unsigned char pact=prec->pact; + double value; + + if ((pdset==NULL) || (pdset->write_ao==NULL)) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"write_ao"); + return(S_dev_missingSup); + } + + /* fetch value and convert*/ + if (prec->pact == FALSE) { + if ((prec->dol.type != CONSTANT) + && (prec->omsl == menuOmslclosed_loop)) { + status = fetch_value(prec, &value); + } + else { + value = prec->val; + } + if(!status) convert(prec, value); + prec->udf = isnan(prec->val); + } + + /* check for alarms */ + checkAlarms(prec); + + if (prec->nsev < INVALID_ALARM ) + status=writeValue(prec); /* write the new value */ + else { + switch (prec->ivoa) { + case (menuIvoaContinue_normally) : + status=writeValue(prec); /* write the new value */ + break; + case (menuIvoaDon_t_drive_outputs) : + break; + case (menuIvoaSet_output_to_IVOV) : + if(prec->pact == FALSE){ + prec->val=prec->ivov; + value=prec->ivov; + convert(prec,value); + } + status=writeValue(prec); /* write the new value */ + break; + default : + status=-1; + recGblRecordError(S_db_badField,(void *)prec, + "ao:process Illegal IVOA field"); + } + } + + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + + /* check event list */ + monitor(prec); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->init=FALSE; + prec->pact=FALSE; + return(status); +} + +static long special(DBADDR *paddr, int after) +{ + aoRecord *prec = (aoRecord *)(paddr->precord); + struct aodset *pdset = (struct aodset *) (prec->dset); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_LINCONV): + if(pdset->number<6 ) { + recGblDbaddrError(S_db_noMod,paddr,"ao: special"); + return(S_db_noMod); + } + prec->init=TRUE; + if ((prec->linr == menuConvertLINEAR) && pdset->special_linconv) { + double eoff = prec->eoff; + double eslo = prec->eslo; + long status; + prec->eoff = prec->egul; + status = (*pdset->special_linconv)(prec,after); + if (eoff != prec->eoff) + db_post_events(prec, &prec->eoff, DBE_VALUE|DBE_LOG); + if (eslo != prec->eslo) + db_post_events(prec, &prec->eslo, DBE_VALUE|DBE_LOG); + return (status); + } + return (0); + default: + recGblDbaddrError(S_db_badChoice,paddr,"ao: special"); + return(S_db_badChoice); + } +} + +static long get_units(DBADDR * paddr,char *units) +{ + aoRecord *prec=(aoRecord *)paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + return(0); +} + +static long get_precision(DBADDR *paddr,long *precision) +{ + aoRecord *prec=(aoRecord *)paddr->precord; + + *precision = prec->prec; + if(paddr->pfield == (void *)&prec->val + || paddr->pfield == (void *)&prec->oval + || paddr->pfield == (void *)&prec->pval) return(0); + recGblGetPrec(paddr,precision); + return(0); +} + +static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) +{ + aoRecord *prec=(aoRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val + || paddr->pfield==(void *)&prec->hihi + || paddr->pfield==(void *)&prec->high + || paddr->pfield==(void *)&prec->low + || paddr->pfield==(void *)&prec->lolo + || paddr->pfield==(void *)&prec->oval + || paddr->pfield==(void *)&prec->pval){ + 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) +{ + aoRecord *prec=(aoRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val + || paddr->pfield==(void *)&prec->hihi + || paddr->pfield==(void *)&prec->high + || paddr->pfield==(void *)&prec->low + || paddr->pfield==(void *)&prec->lolo + || paddr->pfield==(void *)&prec->oval + || paddr->pfield==(void *)&prec->pval){ + pcd->upper_ctrl_limit = prec->drvh; + pcd->lower_ctrl_limit = prec->drvl; + } else recGblGetControlDouble(paddr,pcd); + return(0); +} +static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) +{ + aoRecord *prec=(aoRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val){ + 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(aoRecord *prec) +{ + double val, hyst, lalm; + double alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +static long fetch_value(aoRecord *prec,double *pvalue) +{ + short save_pact; + long status; + + save_pact = prec->pact; + prec->pact = TRUE; + + /* don't allow dbputs to val field */ + prec->val=prec->pval; + + status = dbGetLink(&prec->dol,DBR_DOUBLE,pvalue,0,0); + prec->pact = save_pact; + + if (status) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + return(status); + } + + if (prec->oif == aoOIF_Incremental) + *pvalue += prec->val; + + return(0); +} + +static void convert(aoRecord *prec, double value) +{ + /* check drive limits */ + if (prec->drvh > prec->drvl) { + if (value > prec->drvh) + value = prec->drvh; + else if (value < prec->drvl) + value = prec->drvl; + } + prec->val = value; + prec->pval = value; + + /* now set value equal to desired output value */ + /* apply the output rate of change */ + if (prec->oroc != 0){/*must be defined and >0*/ + double diff; + + diff = value - prec->oval; + if (diff < 0) { + if (prec->oroc < -diff) + value = prec->oval - prec->oroc; + } else if (prec->oroc < diff) + value = prec->oval + prec->oroc; + } + prec->omod = (prec->oval!=value); + prec->oval = value; + + /* convert */ + switch (prec->linr) { + case menuConvertNO_CONVERSION: + break; /* do nothing*/ + case menuConvertLINEAR: + case menuConvertSLOPE: + if (prec->eslo == 0.0) value = 0; + else value = (value - prec->eoff) / prec->eslo; + break; + default: + if (cvtEngToRawBpt(&value, prec->linr, prec->init, + (void *)&prec->pbrk, &prec->lbrk) != 0) { + recGblSetSevr(prec, SOFT_ALARM, MAJOR_ALARM); + return; + } + } + value -= prec->aoff; + if (prec->aslo != 0) value /= prec->aslo; + if (value >= 0.0) + prec->rval = (epicsInt32)(value + 0.5) - prec->roff; + else + prec->rval = (epicsInt32)(value - 0.5) - prec->roff; +} + + +static void monitor(aoRecord *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); + } + if(prec->omod) monitor_mask |= (DBE_VALUE|DBE_LOG); + if(monitor_mask) { + prec->omod = FALSE; + db_post_events(prec,&prec->oval,monitor_mask); + if(prec->oraw != prec->rval) { + db_post_events(prec,&prec->rval, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->oraw = prec->rval; + } + if(prec->orbv != prec->rbv) { + db_post_events(prec,&prec->rbv, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->orbv = prec->rbv; + } + } + return; +} + +static long writeValue(aoRecord *prec) +{ + long status; + struct aodset *pdset = (struct aodset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->write_ao)(prec); + return(status); + } + + status = dbGetLink(&prec->siml,DBR_USHORT,&(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->write_ao)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status = dbPutLink(&(prec->siol),DBR_DOUBLE,&(prec->oval),1); + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/aoRecord.dbd b/src/rec/aoRecord.dbd new file mode 100644 index 000000000..a1caa6b56 --- /dev/null +++ b/src/rec/aoRecord.dbd @@ -0,0 +1,294 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(aoOIF) { + choice(aoOIF_Full,"Full") + choice(aoOIF_Incremental,"Incremental") +} +recordtype(ao) { + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Desired Output") + promptgroup(GUI_OUTPUT) + asl(ASL0) + pp(TRUE) + } + field(OVAL,DBF_DOUBLE) { + prompt("Output Value") + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OROC,DBF_DOUBLE) { + prompt("Output Rate of Chang") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_OUTPUT) + interest(1) + menu(menuOmsl) + } + field(OIF,DBF_MENU) { + prompt("Out Full/Incremental") + promptgroup(GUI_OUTPUT) + interest(1) + menu(aoOIF) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LINR,DBF_MENU) { + prompt("Linearization") + promptgroup(GUI_CONVERT) + special(SPC_LINCONV) + pp(TRUE) + interest(1) + menu(menuConvert) + } + field(EGUF,DBF_DOUBLE) { + prompt("Eng Units Full") + promptgroup(GUI_CONVERT) + special(SPC_LINCONV) + pp(TRUE) + interest(1) + } + field(EGUL,DBF_DOUBLE) { + prompt("Eng Units Low") + promptgroup(GUI_CONVERT) + special(SPC_LINCONV) + pp(TRUE) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(ROFF,DBF_LONG) { + prompt("Raw Offset, obsolete") + pp(TRUE) + interest(2) + } + field(EOFF,DBF_DOUBLE) { + prompt("EGU to Raw Offset") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(2) + } + field(ESLO,DBF_DOUBLE) { + prompt("EGU to Raw Slope") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(2) + initial("1") + } + field(DRVH,DBF_DOUBLE) { + prompt("Drive High Limit") + promptgroup(GUI_OUTPUT) + pp(TRUE) + interest(1) + } + field(DRVL,DBF_DOUBLE) { + prompt("Drive Low Limit") + promptgroup(GUI_OUTPUT) + pp(TRUE) + interest(1) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(AOFF,DBF_DOUBLE) { + prompt("Adjustment Offset") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(1) + } + field(ASLO,DBF_DOUBLE) { + prompt("Adjustment Slope") + promptgroup(GUI_CONVERT) + pp(TRUE) + interest(1) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_DOUBLE) { + 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(RVAL,DBF_LONG) { + prompt("Current Raw Value") + pp(TRUE) + } + field(ORAW,DBF_LONG) { + prompt("Previous Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(RBV,DBF_LONG) { + prompt("Readback Value") + special(SPC_NOMOD) + } + field(ORBV,DBF_LONG) { + prompt("Prev Readback Value") + special(SPC_NOMOD) + interest(3) + } + field(PVAL,DBF_DOUBLE) { + prompt("Previous value") + special(SPC_NOMOD) + interest(3) + } + 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) + } + field(PBRK,DBF_NOACCESS) { + prompt("Ptrto brkTable") + special(SPC_NOMOD) + interest(4) + extra("void * pbrk") + } + field(INIT,DBF_SHORT) { + prompt("Initialized?") + special(SPC_NOMOD) + interest(3) + } + field(LBRK,DBF_SHORT) { + prompt("LastBreak Point") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(IVOA,DBF_MENU) { + prompt("INVALID output action") + promptgroup(GUI_OUTPUT) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_DOUBLE) { + prompt("INVALID output value") + promptgroup(GUI_OUTPUT) + interest(2) + } + field(OMOD,DBF_UCHAR) { + prompt("Was OVAL modified?") + special(SPC_NOMOD) + } +} diff --git a/src/rec/biRecord.c b/src/rec/biRecord.c new file mode 100644 index 000000000..4ba8ebf97 --- /dev/null +++ b/src/rec/biRecord.c @@ -0,0 +1,292 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recBi.c - Record Support Routines for Binary Input records */ +/* + * Original Author: Bob Dalesio + * Date: 7-14-89 + * + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbFldTypes.h" +#include "dbEvent.h" +#include "devSup.h" +#include "errMdef.h" +#include "menuSimm.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "biRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(biRecord *, int); +static long process(biRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +static long get_enum_str(DBADDR *, char *); +static long get_enum_strs(DBADDR *, struct dbr_enumStrs *); +static long put_enum_str(DBADDR *, char *); +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL +rset biRSET={ + 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 }; +struct bidset { /* binary input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_bi;/*(0,2)=> success and convert, don't convert)*/ + /* if convert then raw value stored in rval */ +}; +epicsExportAddress(rset,biRSET); +static void checkAlarms(biRecord *); +static void monitor(biRecord *); +static long readValue(biRecord *); + +static long init_record(biRecord *prec, int pass) +{ + struct bidset *pdset; + long status; + + if (pass==0) return(0); + + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + recGblInitConstantLink(&prec->siol,DBF_USHORT,&prec->sval); + if(!(pdset = (struct bidset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"bi: init_record"); + return(S_dev_noDSET); + } + /* must have read_bi function defined */ + if( (pdset->number < 5) || (pdset->read_bi == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"bi: init_record"); + return(S_dev_missingSup); + } + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + } + prec->mlst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + return(0); +} + +static long process(biRecord *prec) +{ + struct bidset *pdset = (struct bidset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->read_bi==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"read_bi"); + return(S_dev_missingSup); + } + + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + if(status==0) { /* convert rval to val */ + if(prec->rval==0) prec->val =0; + else prec->val = 1; + prec->udf = FALSE; + } + else if(status==2) status=0; + /* 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_enum_str(DBADDR *paddr, char *pstring) +{ + biRecord *prec=(biRecord *)paddr->precord; + int index; + unsigned short *pfield = (unsigned short *)paddr->pfield; + + + index = dbGetFieldIndex(paddr); + if(index!=biRecordVAL) { + strcpy(pstring,"Illegal_Value"); + } else if(*pfield==0) { + strncpy(pstring,prec->znam,sizeof(prec->znam)); + pstring[sizeof(prec->znam)] = 0; + } else if(*pfield==1) { + strncpy(pstring,prec->onam,sizeof(prec->onam)); + pstring[sizeof(prec->onam)] = 0; + } else { + strcpy(pstring,"Illegal_Value"); + } + return(0); +} + +static long get_enum_strs(DBADDR *paddr,struct dbr_enumStrs *pes) +{ + biRecord *prec=(biRecord *)paddr->precord; + + pes->no_str = 2; + memset(pes->strs,'\0',sizeof(pes->strs)); + strncpy(pes->strs[0],prec->znam,sizeof(prec->znam)); + if(*prec->znam!=0) pes->no_str=1; + strncpy(pes->strs[1],prec->onam,sizeof(prec->onam)); + if(*prec->onam!=0) pes->no_str=2; + return(0); +} + +static long put_enum_str(DBADDR *paddr, char *pstring) +{ + biRecord *prec=(biRecord *)paddr->precord; + + if(strncmp(pstring,prec->znam,sizeof(prec->znam))==0) prec->val = 0; + else if(strncmp(pstring,prec->onam,sizeof(prec->onam))==0) prec->val = 1; + else return(S_db_badChoice); + prec->udf=FALSE; + return(0); +} + + +static void checkAlarms(biRecord *prec) +{ + unsigned short val = prec->val; + + + if(prec->udf == TRUE){ + recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM); + return; + } + + if(val>1)return; + /* check for state alarm */ + if (val == 0){ + recGblSetSevr(prec,STATE_ALARM,prec->zsv); + }else{ + recGblSetSevr(prec,STATE_ALARM,prec->osv); + } + + /* check for cos alarm */ + if(val == prec->lalm) return; + recGblSetSevr(prec,COS_ALARM,prec->cosv); + prec->lalm = val; + return; +} + +static void monitor(biRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + /* check for value change */ + if (prec->mlst != prec->val){ + /* post events for value change and archive change */ + monitor_mask |= (DBE_VALUE | DBE_LOG); + /* update last value monitored */ + prec->mlst = prec->val; + } + + /* send out monitors connected to the value field */ + if (monitor_mask){ + db_post_events(prec,&prec->val,monitor_mask); + } + if(prec->oraw!=prec->rval) { + db_post_events(prec,&prec->rval, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->oraw = prec->rval; + } + return; +} + +static long readValue(biRecord *prec) +{ + long status; + struct bidset *pdset = (struct bidset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_bi)(prec); + return(status); + } + + status = dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuSimmNO){ + status=(*pdset->read_bi)(prec); + return(status); + } + if (prec->simm == menuSimmYES){ + status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0); + if (status==0){ + prec->val=(unsigned short)prec->sval; + prec->udf=FALSE; + } + status=2; /* dont convert */ + } + else if (prec->simm == menuSimmRAW){ + status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0); + if (status==0){ + prec->rval=prec->sval; + prec->udf=FALSE; + } + status=0; /* convert since we've written RVAL */ + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/biRecord.dbd b/src/rec/biRecord.dbd new file mode 100644 index 000000000..f2c94e7ee --- /dev/null +++ b/src/rec/biRecord.dbd @@ -0,0 +1,106 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(bi) { + include "dbCommon.dbd" + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + } + field(VAL,DBF_ENUM) { + prompt("Current Value") + promptgroup(GUI_INPUTS) + asl(ASL0) + pp(TRUE) + } + field(ZSV,DBF_MENU) { + prompt("Zero Error Severity") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(OSV,DBF_MENU) { + prompt("One Error Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Svr") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(ZNAM,DBF_STRING) { + prompt("Zero Name") + promptgroup(GUI_CALC) + pp(TRUE) + interest(1) + size(26) + } + field(ONAM,DBF_STRING) { + prompt("One Name") + promptgroup(GUI_CLOCK) + pp(TRUE) + interest(1) + size(26) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SVAL,DBF_ULONG) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuSimm) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } +} diff --git a/src/rec/boRecord.c b/src/rec/boRecord.c new file mode 100644 index 000000000..51902dbac --- /dev/null +++ b/src/rec/boRecord.c @@ -0,0 +1,407 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recBo.c - Record Support Routines for Binary Output records */ +/* + * Original Author: Bob Dalesio + * Date: 7-14-89 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "callback.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "boRecord.h" +#undef GEN_SIZE_OFFSET +#include "menuIvoa.h" +#include "menuOmsl.h" +#include "menuYesNo.h" +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(boRecord *, int); +static long process(boRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +static long get_precision(DBADDR *, long *); +static long get_enum_str(DBADDR *, char *); +static long get_enum_strs(DBADDR *, struct dbr_enumStrs *); +static long put_enum_str(DBADDR *, char *); +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset boRSET={ + 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,boRSET); + +struct bodset { /* binary output dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns:(0,2)=>(success,success no convert*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_bo;/*returns: (-1,0)=>(failure,success)*/ +}; + + +/* control block for callback*/ +typedef struct myCallback { + CALLBACK callback; + struct dbCommon *precord; +}myCallback; + +static void checkAlarms(boRecord *); +static void monitor(boRecord *); +static long writeValue(boRecord *); + +static void myCallbackFunc(CALLBACK *arg) +{ + myCallback *pcallback; + boRecord *prec; + + callbackGetUser(pcallback,arg); + prec=(boRecord *)pcallback->precord; + dbScanLock((struct dbCommon *)prec); + if(prec->pact) { + if((prec->val==1) && (prec->high>0)){ + myCallback *pcallback; + pcallback = (myCallback *)(prec->rpvt); + callbackSetPriority(prec->prio, &pcallback->callback); + callbackRequestDelayed(&pcallback->callback,(double)prec->high); + } + } else { + prec->val = 0; + dbProcess((struct dbCommon *)prec); + } + dbScanUnlock((struct dbCommon *)prec); +} + +static long init_record(boRecord *prec,int pass) +{ + struct bodset *pdset; + long status=0; + myCallback *pcallback; + + if (pass==0) return(0); + + /* bo.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + if(!(pdset = (struct bodset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"bo: init_record"); + return(S_dev_noDSET); + } + /* must have write_bo functions defined */ + if( (pdset->number < 5) || (pdset->write_bo == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"bo: init_record"); + return(S_dev_missingSup); + } + /* get the initial value */ + if (prec->dol.type == CONSTANT) { + unsigned short ival = 0; + + if(recGblInitConstantLink(&prec->dol,DBF_USHORT,&ival)) { + if (ival == 0) prec->val = 0; + else prec->val = 1; + prec->udf = FALSE; + } + } + + pcallback = (myCallback *)(calloc(1,sizeof(myCallback))); + prec->rpvt = (void *)pcallback; + callbackSetCallback(myCallbackFunc,&pcallback->callback); + callbackSetUser(pcallback,&pcallback->callback); + pcallback->precord = (struct dbCommon *)prec; + + if( pdset->init_record ) { + status=(*pdset->init_record)(prec); + if(status==0) { + if(prec->rval==0) prec->val = 0; + else prec->val = 1; + prec->udf = FALSE; + } else if (status==2) status=0; + } + prec->mlst = prec->val; + /* convert val to rval */ + if ( prec->mask != 0 ) { + if(prec->val==0) prec->rval = 0; + else prec->rval = prec->mask; + } else prec->rval = (epicsUInt32)prec->val; + + prec->mlst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + prec->orbv = prec->rbv; + return(status); +} + +static long process(boRecord *prec) +{ + struct bodset *pdset = (struct bodset *)(prec->dset); + long status=0; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->write_bo==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"write_bo"); + return(S_dev_missingSup); + } + if (!prec->pact) { + if ((prec->dol.type != CONSTANT) && (prec->omsl == menuOmslclosed_loop)){ + unsigned short val; + + prec->pact = TRUE; + status=dbGetLink(&prec->dol,DBR_USHORT, &val,0,0); + prec->pact = FALSE; + if(status==0){ + prec->val = val; + prec->udf = FALSE; + }else { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + } + } + + /* convert val to rval */ + if ( prec->mask != 0 ) { + if(prec->val==0) prec->rval = 0; + else prec->rval = prec->mask; + } else prec->rval = (epicsUInt32)prec->val; + } + + /* check for alarms */ + checkAlarms(prec); + + if (prec->nsev < INVALID_ALARM ) + status=writeValue(prec); /* write the new value */ + else { + switch (prec->ivoa) { + case (menuIvoaContinue_normally) : + status=writeValue(prec); /* write the new value */ + break; + case (menuIvoaDon_t_drive_outputs) : + break; + case (menuIvoaSet_output_to_IVOV) : + if(prec->pact == FALSE){ + /* convert val to rval */ + prec->val=prec->ivov; + if ( prec->mask != 0 ) { + if(prec->val==0) prec->rval = 0; + else prec->rval = prec->mask; + } else prec->rval = (epicsUInt32)prec->val; + } + status=writeValue(prec); /* write the new value */ + break; + default : + status=-1; + recGblRecordError(S_db_badField,(void *)prec, + "bo:process Illegal IVOA field"); + } + } + + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + if((prec->val==1) && (prec->high>0)){ + myCallback *pcallback; + pcallback = (myCallback *)(prec->rpvt); + callbackSetPriority(prec->prio, &pcallback->callback); + callbackRequestDelayed(&pcallback->callback,(double)prec->high); + } + /* check event list */ + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return(status); +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + boRecord *prec=(boRecord *)paddr->precord; + + if(paddr->pfield == (void *)&prec->high) *precision=2; + else recGblGetPrec(paddr,precision); + return(0); +} + +static long get_enum_str(DBADDR *paddr, char *pstring) +{ + boRecord *prec=(boRecord *)paddr->precord; + int index; + unsigned short *pfield = (unsigned short *)paddr->pfield; + + + index = dbGetFieldIndex(paddr); + if(index!=boRecordVAL) { + strcpy(pstring,"Illegal_Value"); + } else if(*pfield==0) { + strncpy(pstring,prec->znam,sizeof(prec->znam)); + pstring[sizeof(prec->znam)] = 0; + } else if(*pfield==1) { + strncpy(pstring,prec->onam,sizeof(prec->onam)); + pstring[sizeof(prec->onam)] = 0; + } else { + strcpy(pstring,"Illegal_Value"); + } + return(0); +} + +static long get_enum_strs(DBADDR *paddr,struct dbr_enumStrs *pes) +{ + boRecord *prec=(boRecord *)paddr->precord; + + /*SETTING no_str=0 breaks channel access clients*/ + pes->no_str = 2; + memset(pes->strs,'\0',sizeof(pes->strs)); + strncpy(pes->strs[0],prec->znam,sizeof(prec->znam)); + if(*prec->znam!=0) pes->no_str=1; + strncpy(pes->strs[1],prec->onam,sizeof(prec->onam)); + if(*prec->onam!=0) pes->no_str=2; + return(0); +} +static long put_enum_str(DBADDR *paddr, char *pstring) +{ + boRecord *prec=(boRecord *)paddr->precord; + + if(strncmp(pstring,prec->znam,sizeof(prec->znam))==0) prec->val = 0; + else if(strncmp(pstring,prec->onam,sizeof(prec->onam))==0) prec->val = 1; + else return(S_db_badChoice); + return(0); +} + + +static void checkAlarms(boRecord *prec) +{ + unsigned short val = prec->val; + + /* check for udf alarm */ + if(prec->udf == TRUE ){ + recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM); + } + + /* check for state alarm */ + if (val == 0){ + recGblSetSevr(prec,STATE_ALARM,prec->zsv); + }else{ + recGblSetSevr(prec,STATE_ALARM,prec->osv); + } + + /* check for cos alarm */ + if(val == prec->lalm) return; + recGblSetSevr(prec,COS_ALARM,prec->cosv); + prec->lalm = val; + return; +} + +static void monitor(boRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + /* check for value change */ + if (prec->mlst != prec->val){ + /* post events for value change and archive change */ + monitor_mask |= (DBE_VALUE | DBE_LOG); + /* update last value monitored */ + prec->mlst = prec->val; + } + + /* send out monitors connected to the value field */ + if (monitor_mask){ + db_post_events(prec,&prec->val,monitor_mask); + } + if(prec->oraw!=prec->rval) { + db_post_events(prec,&prec->rval, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->oraw = prec->rval; + } + if(prec->orbv!=prec->rbv) { + db_post_events(prec,&prec->rbv, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->orbv = prec->rbv; + } + return; +} + +static long writeValue(boRecord *prec) +{ + long status; + struct bodset *pdset = (struct bodset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->write_bo)(prec); + return(status); + } + + status=dbGetLink(&prec->siml,DBR_USHORT, &prec->simm,0,0); + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->write_bo)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbPutLink(&(prec->siol),DBR_USHORT, &(prec->val),1); + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/boRecord.dbd b/src/rec/boRecord.dbd new file mode 100644 index 000000000..adc6704ad --- /dev/null +++ b/src/rec/boRecord.dbd @@ -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. +#************************************************************************* +recordtype(bo) { + include "dbCommon.dbd" + field(VAL,DBF_ENUM) { + prompt("Current Value") + promptgroup(GUI_OUTPUT) + asl(ASL0) + pp(TRUE) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_OUTPUT) + interest(1) + menu(menuOmsl) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("Seconds to Hold High") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(ZNAM,DBF_STRING) { + prompt("Zero Name") + promptgroup(GUI_DISPLAY) + pp(TRUE) + interest(1) + size(26) + } + field(ONAM,DBF_STRING) { + prompt("One Name") + promptgroup(GUI_DISPLAY) + pp(TRUE) + interest(1) + size(26) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(RPVT,DBF_NOACCESS) { + prompt("Record Private") + special(SPC_NOMOD) + interest(4) + extra("void * rpvt") + } + field(WDPT,DBF_NOACCESS) { + prompt("Watch Dog Timer ID") + special(SPC_NOMOD) + interest(4) + extra("void * wdpt") + } + field(ZSV,DBF_MENU) { + prompt("Zero Error Severity") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(OSV,DBF_MENU) { + prompt("One Error Severity") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Sevr") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(RBV,DBF_ULONG) { + prompt("Readback Value") + special(SPC_NOMOD) + } + field(ORBV,DBF_ULONG) { + prompt("Prev Readback Value") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(IVOA,DBF_MENU) { + prompt("INVALID outpt action") + promptgroup(GUI_OUTPUT) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_USHORT) { + prompt("INVALID output value") + promptgroup(GUI_OUTPUT) + interest(2) + } +} diff --git a/src/rec/calcRecord.c b/src/rec/calcRecord.c new file mode 100644 index 000000000..6af2448e7 --- /dev/null +++ b/src/rec/calcRecord.c @@ -0,0 +1,362 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Revision-Id: anj@aps.anl.gov-20101124193504-syc2zmdo7fl70mcz */ + +/* Record Support Routines for Calculation records */ +/* + * Original Author: Julie Sander and Bob Dalesio + * Date: 7-27-87 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "errlog.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "epicsMath.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "calcRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table */ + +#define report NULL +#define initialize NULL +static long init_record(calcRecord *prec, int pass); +static long process(calcRecord *prec); +static long special(DBADDR *paddr, int after); +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +static long get_units(DBADDR *paddr, char *units); +static long get_precision(DBADDR *paddr, long *precision); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd); +static long get_ctrl_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd); +static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad); + +rset calcRSET={ + 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_ctrl_double, + get_alarm_double +}; +epicsExportAddress(rset, calcRSET); + +static void checkAlarms(calcRecord *prec); +static void monitor(calcRecord *prec); +static int fetch_values(calcRecord *prec); + + +static long init_record(calcRecord *prec, int pass) +{ + struct link *plink; + double *pvalue; + int i; + short error_number; + + if (pass==0) return(0); + + plink = &prec->inpa; + pvalue = &prec->a; + for (i = 0; i < CALCPERFORM_NARGS; i++, plink++, pvalue++) { + if (plink->type == CONSTANT) { + recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); + } + } + if (postfix(prec->calc, prec->rpcl, &error_number)) { + recGblRecordError(S_db_badField, (void *)prec, + "calc: init_record: Illegal CALC field"); + errlogPrintf("%s.CALC: %s in expression \"%s\"\n", + prec->name, calcErrorStr(error_number), prec->calc); + } + return 0; +} + +static long process(calcRecord *prec) +{ + prec->pact = TRUE; + if (fetch_values(prec)==0) { + if (calcPerform(&prec->a, &prec->val, prec->rpcl)) { + recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM); + } else prec->udf = isnan(prec->val); + } + recGblGetTimeStamp(prec); + /* check for alarms */ + checkAlarms(prec); + /* check event list */ + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + prec->pact = FALSE; + return 0; +} + +static long special(DBADDR *paddr, int after) +{ + calcRecord *prec = (calcRecord *)paddr->precord; + short error_number; + + if (!after) return 0; + if (paddr->special == SPC_CALC) { + if (postfix(prec->calc, prec->rpcl, &error_number)) { + recGblRecordError(S_db_badField, (void *)prec, + "calc: Illegal CALC field"); + errlogPrintf("%s.CALC: %s in expression \"%s\"\n", + prec->name, calcErrorStr(error_number), prec->calc); + return S_db_badField; + } + return 0; + } + recGblDbaddrError(S_db_badChoice, paddr, "calc::special - bad special value!"); + return S_db_badChoice; +} + +static long get_units(DBADDR *paddr, char *units) +{ + calcRecord *prec = (calcRecord *)paddr->precord; + + strncpy(units, prec->egu, DB_UNITS_SIZE); + return 0; +} + +static long get_precision(DBADDR *paddr, long *pprecision) +{ + calcRecord *prec = (calcRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + *pprecision = prec->prec; + + if (fieldIndex != calcRecordVAL) + recGblGetPrec(paddr, pprecision); + + return 0; +} + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + calcRecord *prec = (calcRecord *)paddr->precord; + + if (paddr->pfield == (void *)&prec->val || + paddr->pfield == (void *)&prec->hihi || + paddr->pfield == (void *)&prec->high || + paddr->pfield == (void *)&prec->low || + paddr->pfield == (void *)&prec->lolo) { + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return 0; + } + + if (paddr->pfield >= (void *)&prec->a && + paddr->pfield <= (void *)&prec->l) { + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return 0; + } + if (paddr->pfield >= (void *)&prec->la && + paddr->pfield <= (void *)&prec->ll) { + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return 0; + } + recGblGetGraphicDouble(paddr, pgd); + return 0; +} + +static long get_ctrl_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) +{ + calcRecord *prec = (calcRecord *)paddr->precord; + + if (paddr->pfield == (void *)&prec->val || + paddr->pfield == (void *)&prec->hihi || + paddr->pfield == (void *)&prec->high || + paddr->pfield == (void *)&prec->low || + paddr->pfield == (void *)&prec->lolo) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return 0; + } + + if (paddr->pfield >= (void *)&prec->a && + paddr->pfield <= (void *)&prec->l) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return 0; + } + if (paddr->pfield >= (void *)&prec->la && + paddr->pfield <= (void *)&prec->ll) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return 0; + } + recGblGetControlDouble(paddr, pcd); + return 0; +} + +static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) +{ + calcRecord *prec = (calcRecord *)paddr->precord; + + if (paddr->pfield == (void *)&prec->val) { + 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(calcRecord *prec) +{ + double val, hyst, lalm; + double alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +static void monitor(calcRecord *prec) +{ + unsigned short monitor_mask; + double delta, *pnew, *pprev; + int i; + + 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); + } + /* check all input fields for changes*/ + pnew = &prec->a; + pprev = &prec->la; + for (i = 0; i < CALCPERFORM_NARGS; i++, pnew++, pprev++) { + if (*pnew != *pprev || + monitor_mask & DBE_ALARM) { + db_post_events(prec, pnew, monitor_mask | DBE_VALUE | DBE_LOG); + *pprev = *pnew; + } + } + return; +} + +static int fetch_values(calcRecord *prec) +{ + struct link *plink; + double *pvalue; + long status = 0; + int i; + + plink = &prec->inpa; + pvalue = &prec->a; + for(i = 0; i < CALCPERFORM_NARGS; i++, plink++, pvalue++) { + int newStatus; + + newStatus = dbGetLink(plink, DBR_DOUBLE, pvalue, 0, 0); + if (status == 0) status = newStatus; + } + return status; +} diff --git a/src/rec/calcRecord.dbd b/src/rec/calcRecord.dbd new file mode 100644 index 000000000..0248f34cd --- /dev/null +++ b/src/rec/calcRecord.dbd @@ -0,0 +1,301 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +recordtype(calc) { + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Result") + asl(ASL0) + } + field(CALC,DBF_STRING) { + prompt("Calculation") + promptgroup(GUI_CALC) + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(INPA,DBF_INLINK) { + prompt("Input A") + promptgroup(GUI_CALC) + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input B") + promptgroup(GUI_CALC) + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input C") + promptgroup(GUI_CALC) + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input D") + promptgroup(GUI_CALC) + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input E") + promptgroup(GUI_CALC) + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input F") + promptgroup(GUI_CALC) + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input G") + promptgroup(GUI_CALC) + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input H") + promptgroup(GUI_CALC) + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input I") + promptgroup(GUI_CALC) + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input J") + promptgroup(GUI_CALC) + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input K") + promptgroup(GUI_CALC) + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input L") + promptgroup(GUI_CALC) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Units Name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Rng") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_DOUBLE) { + 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(A,DBF_DOUBLE) { + prompt("Value of Input A") + pp(TRUE) + } + field(B,DBF_DOUBLE) { + prompt("Value of Input B") + pp(TRUE) + } + field(C,DBF_DOUBLE) { + prompt("Value of Input C") + pp(TRUE) + } + field(D,DBF_DOUBLE) { + prompt("Value of Input D") + pp(TRUE) + } + field(E,DBF_DOUBLE) { + prompt("Value of Input E") + pp(TRUE) + } + field(F,DBF_DOUBLE) { + prompt("Value of Input F") + pp(TRUE) + } + field(G,DBF_DOUBLE) { + prompt("Value of Input G") + pp(TRUE) + } + field(H,DBF_DOUBLE) { + prompt("Value of Input H") + pp(TRUE) + } + field(I,DBF_DOUBLE) { + prompt("Value of Input I") + pp(TRUE) + } + field(J,DBF_DOUBLE) { + prompt("Value of Input J") + pp(TRUE) + } + field(K,DBF_DOUBLE) { + prompt("Value of Input K") + pp(TRUE) + } + field(L,DBF_DOUBLE) { + prompt("Value of Input L") + pp(TRUE) + } + field(LA,DBF_DOUBLE) { + prompt("Prev Value of A") + special(SPC_NOMOD) + interest(3) + } + field(LB,DBF_DOUBLE) { + prompt("Prev Value of B") + special(SPC_NOMOD) + interest(3) + } + field(LC,DBF_DOUBLE) { + prompt("Prev Value of C") + special(SPC_NOMOD) + interest(3) + } + field(LD,DBF_DOUBLE) { + prompt("Prev Value of D") + special(SPC_NOMOD) + interest(3) + } + field(LE,DBF_DOUBLE) { + prompt("Prev Value of E") + special(SPC_NOMOD) + interest(3) + } + field(LF,DBF_DOUBLE) { + prompt("Prev Value of F") + special(SPC_NOMOD) + interest(3) + } + field(LG,DBF_DOUBLE) { + prompt("Prev Value of G") + special(SPC_NOMOD) + interest(3) + } + field(LH,DBF_DOUBLE) { + prompt("Prev Value of H") + special(SPC_NOMOD) + interest(3) + } + field(LI,DBF_DOUBLE) { + prompt("Prev Value of I") + special(SPC_NOMOD) + interest(3) + } + field(LJ,DBF_DOUBLE) { + prompt("Prev Value of J") + special(SPC_NOMOD) + interest(3) + } + field(LK,DBF_DOUBLE) { + prompt("Prev Value of K") + special(SPC_NOMOD) + interest(3) + } + field(LL,DBF_DOUBLE) { + prompt("Prev Value of L") + special(SPC_NOMOD) + interest(3) + } + 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) + } + %#include "postfix.h" + field(RPCL,DBF_NOACCESS) { + prompt("Reverse Polish Calc") + special(SPC_NOMOD) + interest(4) + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + } +} diff --git a/src/rec/calcoutRecord.c b/src/rec/calcoutRecord.c new file mode 100644 index 000000000..062580c76 --- /dev/null +++ b/src/rec/calcoutRecord.c @@ -0,0 +1,713 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* calcout.c - Record Support Routines for calc with output records */ +/* + * Author : Ned Arnold + * Based on recCalc.c by Julie Sander and Bob Dalesio + * Date: 7-27-87 + */ + +#include +#include +#include +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbScan.h" +#include "cantProceed.h" +#include "epicsMath.h" +#include "errMdef.h" +#include "errlog.h" +#include "recSup.h" +#include "devSup.h" +#include "recGbl.h" +#include "special.h" +#include "callback.h" +#include "taskwd.h" + +#define GEN_SIZE_OFFSET +#include "calcoutRecord.h" +#undef GEN_SIZE_OFFSET +#include "menuIvoa.h" +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(calcoutRecord *, int); +static long process(calcoutRecord *); +static long special(DBADDR *, int); +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_ctrl_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); + +rset calcoutRSET = { + 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_ctrl_double, + get_alarm_double +}; +epicsExportAddress(rset, calcoutRSET); + +typedef struct calcoutDSET { + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write; +}calcoutDSET; + + +/* To provide feedback to the user as to the connection status of the + * links (.INxV and .OUTV), the following algorithm has been implemented ... + * + * A new PV_LINK [either in init() or special()] is searched for using + * dbNameToAddr. If local, it is so indicated. If not, a checkLinkCb + * callback is scheduled to check the connectivity later using + * dbCaIsLinkConnected(). Anytime there are unconnected CA_LINKs, another + * callback is scheduled. Once all connections are established, the CA_LINKs + * are checked whenever the record processes. + * + */ + +#define NO_CA_LINKS 0 +#define CA_LINKS_ALL_OK 1 +#define CA_LINKS_NOT_OK 2 + +typedef struct rpvtStruct { + CALLBACK doOutCb; + CALLBACK checkLinkCb; + short cbScheduled; + short caLinkStat; /* NO_CA_LINKS, CA_LINKS_ALL_OK, CA_LINKS_NOT_OK */ +} rpvtStruct; + +static void checkAlarms(calcoutRecord *prec); +static void monitor(calcoutRecord *prec); +static int fetch_values(calcoutRecord *prec); +static void execOutput(calcoutRecord *prec); +static void checkLinks(calcoutRecord *prec); +static void checkLinksCallback(CALLBACK *arg); +static long writeValue(calcoutRecord *prec); + +int calcoutRecDebug; + + +static long init_record(calcoutRecord *prec, int pass) +{ + DBLINK *plink; + int i; + double *pvalue; + epicsEnum16 *plinkValid; + short error_number; + calcoutDSET *pcalcoutDSET; + + DBADDR dbaddr; + DBADDR *pAddr = &dbaddr; + rpvtStruct *prpvt; + + if (pass == 0) { + prec->rpvt = (rpvtStruct *) callocMustSucceed(1, sizeof(rpvtStruct), "calcoutRecord"); + return 0; + } + if (!(pcalcoutDSET = (calcoutDSET *)prec->dset)) { + recGblRecordError(S_dev_noDSET, (void *)prec, "calcout:init_record"); + return S_dev_noDSET; + } + /* must have write defined */ + if ((pcalcoutDSET->number < 5) || (pcalcoutDSET->write ==NULL)) { + recGblRecordError(S_dev_missingSup, (void *)prec, "calcout:init_record"); + return S_dev_missingSup; + } + prpvt = prec->rpvt; + plink = &prec->inpa; + pvalue = &prec->a; + plinkValid = &prec->inav; + for (i = 0; i <= CALCPERFORM_NARGS; i++, plink++, pvalue++, plinkValid++) { + if (plink->type == CONSTANT) { + /* Don't InitConstantLink the .OUT link */ + if (i < CALCPERFORM_NARGS) { + recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); + } + *plinkValid = calcoutINAV_CON; + } else if (!dbNameToAddr(plink->value.pv_link.pvname, pAddr)) { + /* PV resides on this ioc */ + *plinkValid = calcoutINAV_LOC; + } else { + /* pv is not on this ioc. Callback later for connection stat */ + *plinkValid = calcoutINAV_EXT_NC; + prpvt->caLinkStat = CA_LINKS_NOT_OK; + } + } + + prec->clcv = postfix(prec->calc, prec->rpcl, &error_number); + if (prec->clcv){ + recGblRecordError(S_db_badField, (void *)prec, + "calcout: init_record: Illegal CALC field"); + errlogPrintf("%s.CALC: %s in expression \"%s\"\n", + prec->name, calcErrorStr(error_number), prec->calc); + } + + prec->oclv = postfix(prec->ocal, prec->orpc, &error_number); + if (prec->dopt == calcoutDOPT_Use_OVAL && prec->oclv){ + recGblRecordError(S_db_badField, (void *)prec, + "calcout: init_record: Illegal OCAL field"); + errlogPrintf("%s.OCAL: %s in expression \"%s\"\n", + prec->name, calcErrorStr(error_number), prec->ocal); + } + + prpvt = prec->rpvt; + callbackSetCallback(checkLinksCallback, &prpvt->checkLinkCb); + callbackSetPriority(0, &prpvt->checkLinkCb); + callbackSetUser(prec, &prpvt->checkLinkCb); + prpvt->cbScheduled = 0; + + if (pcalcoutDSET->init_record) pcalcoutDSET->init_record(prec); + prec->pval = prec->val; + prec->mlst = prec->val; + prec->alst = prec->val; + prec->lalm = prec->val; + prec->povl = prec->oval; + return 0; +} + +static long process(calcoutRecord *prec) +{ + rpvtStruct *prpvt = prec->rpvt; + int doOutput = 0; + + if (!prec->pact) { + prec->pact = TRUE; + /* if some links are CA, check connections */ + if (prpvt->caLinkStat != NO_CA_LINKS) { + checkLinks(prec); + } + if (fetch_values(prec) == 0) { + if (calcPerform(&prec->a, &prec->val, prec->rpcl)) { + recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM); + } else { + prec->udf = isnan(prec->val); + } + } + checkAlarms(prec); + /* check for output link execution */ + switch (prec->oopt) { + case calcoutOOPT_Every_Time: + doOutput = 1; + break; + case calcoutOOPT_On_Change: + if (fabs(prec->pval - prec->val) > prec->mdel) doOutput = 1; + break; + case calcoutOOPT_Transition_To_Zero: + if ((prec->pval != 0.0) && (prec->val == 0.0)) doOutput = 1; + break; + case calcoutOOPT_Transition_To_Non_zero: + if ((prec->pval == 0.0) && (prec->val != 0.0)) doOutput = 1; + break; + case calcoutOOPT_When_Zero: + if (prec->val == 0.0) doOutput = 1; + break; + case calcoutOOPT_When_Non_zero: + if (prec->val != 0.0) doOutput = 1; + break; + default: + break; + } + prec->pval = prec->val; + if (doOutput) { + if (prec->odly > 0.0) { + prec->dlya = 1; + db_post_events(prec, &prec->dlya, DBE_VALUE); + callbackRequestProcessCallbackDelayed(&prpvt->doOutCb, + prec->prio, prec, (double)prec->odly); + return 0; + } else { + prec->pact = FALSE; + execOutput(prec); + if (prec->pact) return 0; + prec->pact = TRUE; + } + } + } else { /* pact == TRUE */ + if (prec->dlya) { + prec->dlya = 0; + db_post_events(prec, &prec->dlya, DBE_VALUE); + /* Make pact FALSE for asynchronous device support*/ + prec->pact = FALSE; + execOutput(prec); + if (prec->pact) return 0; + prec->pact = TRUE; + } else {/*Device Support is asynchronous*/ + writeValue(prec); + } + } + recGblGetTimeStamp(prec); + monitor(prec); + recGblFwdLink(prec); + prec->pact = FALSE; + return 0; +} + +static long special(DBADDR *paddr, int after) +{ + calcoutRecord *prec = (calcoutRecord *)paddr->precord; + rpvtStruct *prpvt = prec->rpvt; + DBADDR dbaddr; + DBADDR *pAddr = &dbaddr; + short error_number; + int fieldIndex = dbGetFieldIndex(paddr); + int lnkIndex; + DBLINK *plink; + double *pvalue; + epicsEnum16 *plinkValid; + + if (!after) return 0; + switch(fieldIndex) { + case(calcoutRecordCALC): + prec->clcv = postfix(prec->calc, prec->rpcl, &error_number); + if (prec->clcv){ + recGblRecordError(S_db_badField, (void *)prec, + "calcout: special(): Illegal CALC field"); + errlogPrintf("%s.CALC: %s in expression \"%s\"\n", + prec->name, calcErrorStr(error_number), prec->calc); + } + db_post_events(prec, &prec->clcv, DBE_VALUE); + return 0; + + case(calcoutRecordOCAL): + prec->oclv = postfix(prec->ocal, prec->orpc, &error_number); + if (prec->dopt == calcoutDOPT_Use_OVAL && prec->oclv){ + recGblRecordError(S_db_badField, (void *)prec, + "calcout: special(): Illegal OCAL field"); + errlogPrintf("%s.OCAL: %s in expression \"%s\"\n", + prec->name, calcErrorStr(error_number), prec->ocal); + } + db_post_events(prec, &prec->oclv, DBE_VALUE); + return 0; + case(calcoutRecordINPA): + case(calcoutRecordINPB): + case(calcoutRecordINPC): + case(calcoutRecordINPD): + case(calcoutRecordINPE): + case(calcoutRecordINPF): + case(calcoutRecordINPG): + case(calcoutRecordINPH): + case(calcoutRecordINPI): + case(calcoutRecordINPJ): + case(calcoutRecordINPK): + case(calcoutRecordINPL): + case(calcoutRecordOUT): + lnkIndex = fieldIndex - calcoutRecordINPA; + plink = &prec->inpa + lnkIndex; + pvalue = &prec->a + lnkIndex; + plinkValid = &prec->inav + lnkIndex; + if (plink->type == CONSTANT) { + if (fieldIndex != calcoutRecordOUT) { + recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); + db_post_events(prec, pvalue, DBE_VALUE); + } + *plinkValid = calcoutINAV_CON; + } else if (!dbNameToAddr(plink->value.pv_link.pvname, pAddr)) { + /* if the PV resides on this ioc */ + *plinkValid = calcoutINAV_LOC; + } else { + /* pv is not on this ioc. Callback later for connection stat */ + *plinkValid = calcoutINAV_EXT_NC; + /* DO_CALLBACK, if not already scheduled */ + if (!prpvt->cbScheduled) { + callbackRequestDelayed(&prpvt->checkLinkCb, .5); + prpvt->cbScheduled = 1; + prpvt->caLinkStat = CA_LINKS_NOT_OK; + } + } + db_post_events(prec, plinkValid, DBE_VALUE); + return 0; + default: + recGblDbaddrError(S_db_badChoice, paddr, "calc: special"); + return(S_db_badChoice); + } +} + +static long get_units(DBADDR *paddr, char *units) +{ + calcoutRecord *prec = (calcoutRecord *)paddr->precord; + + strncpy(units, prec->egu, DB_UNITS_SIZE); + return 0; +} + +static long get_precision(DBADDR *paddr, long *pprecision) +{ + calcoutRecord *prec = (calcoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + *pprecision = prec->prec; + + if (fieldIndex != calcoutRecordVAL) + recGblGetPrec(paddr, pprecision); + + return 0; +} + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + calcoutRecord *prec = (calcoutRecord *)paddr->precord; + + if (paddr->pfield == (void *)&prec->val || + paddr->pfield == (void *)&prec->hihi || + paddr->pfield == (void *)&prec->high || + paddr->pfield == (void *)&prec->low || + paddr->pfield == (void *)&prec->lolo) { + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return 0; + } + + if (paddr->pfield >= (void *)&prec->a && + paddr->pfield <= (void *)&prec->l) { + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return 0; + } + if (paddr->pfield >= (void *)&prec->la && + paddr->pfield <= (void *)&prec->ll) { + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return 0; + } + recGblGetGraphicDouble(paddr, pgd); + return 0; +} + +static long get_ctrl_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) +{ + calcoutRecord *prec = (calcoutRecord *)paddr->precord; + + if (paddr->pfield == (void *)&prec->val || + paddr->pfield == (void *)&prec->hihi || + paddr->pfield == (void *)&prec->high || + paddr->pfield == (void *)&prec->low || + paddr->pfield == (void *)&prec->lolo) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return 0; + } + + if (paddr->pfield >= (void *)&prec->a && + paddr->pfield <= (void *)&prec->l) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return 0; + } + if (paddr->pfield >= (void *)&prec->la && + paddr->pfield <= (void *)&prec->ll) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return 0; + } + recGblGetControlDouble(paddr, pcd); + return 0; +} + +static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) +{ + calcoutRecord *prec = (calcoutRecord *)paddr->precord; + + if (paddr->pfield == (void *)&prec->val) { + 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(calcoutRecord *prec) +{ + double val, hyst, lalm; + double alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +static void execOutput(calcoutRecord *prec) +{ + long status; + + /* Determine output data */ + switch(prec->dopt) { + case calcoutDOPT_Use_VAL: + prec->oval = prec->val; + break; + case calcoutDOPT_Use_OVAL: + if (calcPerform(&prec->a, &prec->oval, prec->orpc)) { + recGblSetSevr(prec, CALC_ALARM, INVALID_ALARM); + } else { + prec->udf = isnan(prec->oval); + } + break; + } + if (prec->udf){ + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + } + + /* Check to see what to do if INVALID */ + if (prec->nsev < INVALID_ALARM ) { + /* Output the value */ + status = writeValue(prec); + /* post event if output event != 0 */ + if (prec->oevt > 0) { + post_event((int)prec->oevt); + } + } else switch (prec->ivoa) { + case menuIvoaContinue_normally: + status = writeValue(prec); + /* post event if output event != 0 */ + if (prec->oevt > 0) { + post_event((int)prec->oevt); + } + break; + case menuIvoaDon_t_drive_outputs: + break; + case menuIvoaSet_output_to_IVOV: + prec->oval = prec->ivov; + status = writeValue(prec); + /* post event if output event != 0 */ + if (prec->oevt > 0) { + post_event((int)prec->oevt); + } + break; + default: + status = -1; + recGblRecordError(S_db_badField, (void *)prec, + "calcout:process Illegal IVOA field"); + } +} + +static void monitor(calcoutRecord *prec) +{ + unsigned monitor_mask; + double delta; + double *pnew; + double *pprev; + int i; + + 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); + } + /* check all input fields for changes*/ + for (i = 0, pnew = &prec->a, pprev = &prec->la; ipovl != prec->oval) { + db_post_events(prec, &prec->oval, monitor_mask|DBE_VALUE|DBE_LOG); + prec->povl = prec->oval; + } + return; +} + +static int fetch_values(calcoutRecord *prec) +{ + DBLINK *plink; /* structure of the link field */ + double *pvalue; + long status = 0; + int i; + + for (i = 0, plink = &prec->inpa, pvalue = &prec->a; irpvt; + + dbScanLock((dbCommon *)prec); + prpvt->cbScheduled = 0; + checkLinks(prec); + dbScanUnlock((dbCommon *)prec); + +} + +static void checkLinks(calcoutRecord *prec) +{ + + DBLINK *plink; + rpvtStruct *prpvt = prec->rpvt; + int i; + int stat; + int caLink = 0; + int caLinkNc = 0; + epicsEnum16 *plinkValid; + + if (calcoutRecDebug) printf("checkLinks() for %p\n", prec); + + plink = &prec->inpa; + plinkValid = &prec->inav; + + for (i = 0; itype == CA_LINK) { + caLink = 1; + stat = dbCaIsLinkConnected(plink); + if (!stat && (*plinkValid == calcoutINAV_EXT_NC)) { + caLinkNc = 1; + } + else if (!stat && (*plinkValid == calcoutINAV_EXT)) { + *plinkValid = calcoutINAV_EXT_NC; + db_post_events(prec, plinkValid, DBE_VALUE); + caLinkNc = 1; + } + else if (stat && (*plinkValid == calcoutINAV_EXT_NC)) { + *plinkValid = calcoutINAV_EXT; + db_post_events(prec, plinkValid, DBE_VALUE); + } + } + } + if (caLinkNc) + prpvt->caLinkStat = CA_LINKS_NOT_OK; + else if (caLink) + prpvt->caLinkStat = CA_LINKS_ALL_OK; + else + prpvt->caLinkStat = NO_CA_LINKS; + + if (!prpvt->cbScheduled && caLinkNc) { + /* Schedule another CALLBACK */ + prpvt->cbScheduled = 1; + callbackRequestDelayed(&prpvt->checkLinkCb, .5); + } +} + +static long writeValue(calcoutRecord *prec) +{ + calcoutDSET *pcalcoutDSET = (calcoutDSET *)prec->dset; + + + if (!pcalcoutDSET || !pcalcoutDSET->write) { + errlogPrintf("%s DSET write does not exist\n", prec->name); + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + prec->pact = TRUE; + return(-1); + } + return pcalcoutDSET->write(prec); +} diff --git a/src/rec/calcoutRecord.dbd b/src/rec/calcoutRecord.dbd new file mode 100644 index 000000000..fc393a782 --- /dev/null +++ b/src/rec/calcoutRecord.dbd @@ -0,0 +1,506 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +menu(calcoutOOPT) { + choice(calcoutOOPT_Every_Time,"Every Time") + choice(calcoutOOPT_On_Change,"On Change") + choice(calcoutOOPT_When_Zero,"When Zero") + choice(calcoutOOPT_When_Non_zero,"When Non-zero") + choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero") + choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero") +} +menu(calcoutDOPT) { + choice(calcoutDOPT_Use_VAL,"Use CALC") + choice(calcoutDOPT_Use_OVAL,"Use OCAL") +} +menu(calcoutINAV) { + choice(calcoutINAV_EXT_NC,"Ext PV NC") + choice(calcoutINAV_EXT,"Ext PV OK") + choice(calcoutINAV_LOC,"Local PV") + choice(calcoutINAV_CON,"Constant") +} +recordtype(calcout) { + include "dbCommon.dbd" + field(RPVT,DBF_NOACCESS) { + prompt("Record Private") + special(SPC_NOMOD) + interest(4) + extra("struct rpvtStruct *rpvt") + } + field(VAL,DBF_DOUBLE) { + prompt("Result") + promptgroup(GUI_OUTPUT) + asl(ASL0) + } + field(PVAL,DBF_DOUBLE) { + prompt("Previous Value") + } + field(CALC,DBF_STRING) { + prompt("Calculation") + promptgroup(GUI_CALC) + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(CLCV,DBF_LONG) { + prompt("CALC Valid") + interest(1) + } + field(INPA,DBF_INLINK) { + prompt("Input A") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input B") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input C") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input D") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input E") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input F") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input G") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input H") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input I") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input J") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input K") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input L") + special(SPC_MOD) + promptgroup(GUI_CALC) + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + special(SPC_MOD) + promptgroup(GUI_OUTPUT) + interest(1) + } + field(INAV,DBF_MENU) { + prompt("INPA PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INBV,DBF_MENU) { + prompt("INPB PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INCV,DBF_MENU) { + prompt("INPC PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INDV,DBF_MENU) { + prompt("INPD PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INEV,DBF_MENU) { + prompt("INPE PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INFV,DBF_MENU) { + prompt("INPF PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INGV,DBF_MENU) { + prompt("INPG PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INHV,DBF_MENU) { + prompt("INPH PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INIV,DBF_MENU) { + prompt("INPI PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INJV,DBF_MENU) { + prompt("INPJ PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INKV,DBF_MENU) { + prompt("INPK PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INLV,DBF_MENU) { + prompt("INPL PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(OUTV,DBF_MENU) { + prompt("OUT PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + } + field(OOPT,DBF_MENU) { + prompt("Output Execute Opt") + promptgroup(GUI_CALC) + interest(1) + menu(calcoutOOPT) + } + field(ODLY,DBF_DOUBLE) { + prompt("Output Execute Delay") + promptgroup(GUI_ALARMS) + asl(ASL0) + interest(1) + } + field(DLYA,DBF_USHORT) { + prompt("Output Delay Active") + special(SPC_NOMOD) + asl(ASL0) + } + field(DOPT,DBF_MENU) { + prompt("Output Data Opt") + promptgroup(GUI_CALC) + interest(1) + menu(calcoutDOPT) + } + field(OCAL,DBF_STRING) { + prompt("Output Calculation") + promptgroup(GUI_CALC) + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(OCLV,DBF_LONG) { + prompt("OCAL Valid") + interest(1) + } + field(OEVT,DBF_USHORT) { + prompt("Event To Issue") + promptgroup(GUI_CLOCK) + asl(ASL0) + } + field(IVOA,DBF_MENU) { + prompt("INVALID output action") + promptgroup(GUI_OUTPUT) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_DOUBLE) { + prompt("INVALID output value") + promptgroup(GUI_OUTPUT) + interest(2) + } + field(EGU,DBF_STRING) { + prompt("Units Name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Rng") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_DOUBLE) { + 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(A,DBF_DOUBLE) { + prompt("Value of Input A") + pp(TRUE) + } + field(B,DBF_DOUBLE) { + prompt("Value of Input B") + pp(TRUE) + } + field(C,DBF_DOUBLE) { + prompt("Value of Input C") + pp(TRUE) + } + field(D,DBF_DOUBLE) { + prompt("Value of Input D") + pp(TRUE) + } + field(E,DBF_DOUBLE) { + prompt("Value of Input E") + pp(TRUE) + } + field(F,DBF_DOUBLE) { + prompt("Value of Input F") + pp(TRUE) + } + field(G,DBF_DOUBLE) { + prompt("Value of Input G") + pp(TRUE) + } + field(H,DBF_DOUBLE) { + prompt("Value of Input H") + pp(TRUE) + } + field(I,DBF_DOUBLE) { + prompt("Value of Input I") + pp(TRUE) + } + field(J,DBF_DOUBLE) { + prompt("Value of Input J") + pp(TRUE) + } + field(K,DBF_DOUBLE) { + prompt("Value of Input K") + pp(TRUE) + } + field(L,DBF_DOUBLE) { + prompt("Value of Input L") + pp(TRUE) + } + field(OVAL,DBF_DOUBLE) { + prompt("Output Value") + asl(ASL0) + } + field(LA,DBF_DOUBLE) { + prompt("Prev Value of A") + special(SPC_NOMOD) + interest(3) + } + field(LB,DBF_DOUBLE) { + prompt("Prev Value of B") + special(SPC_NOMOD) + interest(3) + } + field(LC,DBF_DOUBLE) { + prompt("Prev Value of C") + special(SPC_NOMOD) + interest(3) + } + field(LD,DBF_DOUBLE) { + prompt("Prev Value of D") + special(SPC_NOMOD) + interest(3) + } + field(LE,DBF_DOUBLE) { + prompt("Prev Value of E") + special(SPC_NOMOD) + interest(3) + } + field(LF,DBF_DOUBLE) { + prompt("Prev Value of F") + special(SPC_NOMOD) + interest(3) + } + field(LG,DBF_DOUBLE) { + prompt("Prev Value of G") + special(SPC_NOMOD) + interest(3) + } + field(LH,DBF_DOUBLE) { + prompt("Prev Value of H") + special(SPC_NOMOD) + interest(3) + } + field(LI,DBF_DOUBLE) { + prompt("Prev Value of I") + special(SPC_NOMOD) + interest(3) + } + field(LJ,DBF_DOUBLE) { + prompt("Prev Value of J") + special(SPC_NOMOD) + interest(3) + } + field(LK,DBF_DOUBLE) { + prompt("Prev Value of K") + special(SPC_NOMOD) + interest(3) + } + field(LL,DBF_DOUBLE) { + prompt("Prev Value of L") + special(SPC_NOMOD) + interest(3) + } + field(POVL,DBF_DOUBLE) { + prompt("Prev Value of OVAL") + asl(ASL0) + } + 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) + } + %#include "postfix.h" + field(RPCL,DBF_NOACCESS) { + prompt("Reverse Polish Calc") + special(SPC_NOMOD) + interest(4) + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + } + field(ORPC,DBF_NOACCESS) { + prompt("Reverse Polish OCalc") + special(SPC_NOMOD) + interest(4) + extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") + } +} diff --git a/src/rec/compressRecord.c b/src/rec/compressRecord.c new file mode 100644 index 000000000..d1ce9f57d --- /dev/null +++ b/src/rec/compressRecord.c @@ -0,0 +1,443 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ +/* + * Original Author: Bob Dalesio + * Date: 7-14-89 + */ + +#include +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbStaticLib.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "errMdef.h" +#include "special.h" +#include "recSup.h" +#include "recGbl.h" +#define GEN_SIZE_OFFSET +#include "compressRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(compressRecord *, int); +static long process(compressRecord *); +static long special(DBADDR *, int); +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +#define get_alarm_double NULL + +rset compressRSET={ + 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,compressRSET); + +static void reset(compressRecord *prec) +{ + prec->nuse = 0; + prec->off = 0; + prec->inx = 0; + prec->cvb = 0.0; + prec->res = 0; + /* allocate memory for the summing buffer for conversions requiring it */ + if (prec->alg == compressALG_Average && prec->sptr == 0){ + prec->sptr = (double *)calloc(prec->nsam,sizeof(double)); + } +} + +static void monitor(compressRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + monitor_mask |= (DBE_LOG|DBE_VALUE); + if(monitor_mask) db_post_events(prec,prec->bptr,monitor_mask); + return; +} + +static void put_value(compressRecord *prec,double *psource, epicsInt32 n) +{ +/* treat bptr as pointer to a circular buffer*/ + double *pdest; + epicsInt32 offset=prec->off; + epicsInt32 nuse=prec->nuse; + epicsInt32 nsam=prec->nsam; + epicsInt32 i; + + pdest = prec->bptr + offset; + for(i=0; i=nsam) { + pdest=prec->bptr; + offset=0; + } else pdest++; + } + nuse = nuse+n; + if(nuse>nsam) nuse=nsam; + prec->off = offset; + prec->nuse = nuse; + return; +} + +/* qsort comparison function (for median calculation) */ +static int compare(const void *arg1, const void *arg2) +{ + double a = *(double *)arg1; + double b = *(double *)arg2; + + if ( a < b ) return -1; + else if ( a == b ) return 0; + else return 1; +} + +static int compress_array(compressRecord *prec, + double *psource,epicsInt32 no_elements) +{ + epicsInt32 i,j; + epicsInt32 nnew; + epicsInt32 nsam=prec->nsam; + double value; + epicsInt32 n; + + /* skip out of limit data */ + if (prec->ilil < prec->ihil){ + while (((*psource < prec->ilil) || (*psource > prec->ihil)) + && (no_elements > 0)){ + no_elements--; + psource++; + } + } + if(prec->n <= 0) prec->n = 1; + n = prec->n; + if(no_elementsalg){ + case (compressALG_N_to_1_Low_Value): + /* compress N to 1 keeping the lowest value */ + for (i = 0; i < nnew; i++){ + value = *psource++; + for (j = 1; j < n; j++, psource++){ + if (value > *psource) value = *psource; + } + put_value(prec,&value,1); + } + break; + case (compressALG_N_to_1_High_Value): + /* compress N to 1 keeping the highest value */ + for (i = 0; i < nnew; i++){ + value = *psource++; + for (j = 1; j < n; j++, psource++){ + if (value < *psource) value = *psource; + } + put_value(prec,&value,1); + } + break; + case (compressALG_N_to_1_Average): + /* compress N to 1 keeping the average value */ + for (i = 0; i < nnew; i++){ + value = 0; + for (j = 0; j < n; j++, psource++) + value += *psource; + value /= n; + put_value(prec,&value,1); + } + break; + case (compressALG_N_to_1_Median): + /* compress N to 1 keeping the median value */ + /* note: sorts source array (OK; it's a work pointer) */ + for (i = 0; i < nnew; i++, psource+=nnew){ + qsort(psource,n,sizeof(double),compare); + value=psource[n/2]; + put_value(prec,&value,1); + } + break; + } + return(0); +} + +static int array_average(compressRecord *prec, + double *psource,epicsInt32 no_elements) +{ + epicsInt32 i; + epicsInt32 nnow; + epicsInt32 nsam=prec->nsam; + double *psum; + double multiplier; + epicsInt32 inx=prec->inx; + epicsInt32 nuse,n; + + nuse = nsam; + if(nuse>no_elements) nuse = no_elements; + nnow=nuse; + if(nnow>no_elements) nnow=no_elements; + psum = (double *)prec->sptr; + + /* add in the new waveform */ + if (inx == 0){ + for (i = 0; i < nnow; i++) + *psum++ = *psource++; + for(i=nnow; in<=0)prec->n=1; + n = prec->n; + if (inxinx = inx; + return(1); + } + if(n>1) { + psum = (double *)prec->sptr; + multiplier = 1.0/((double)n); + for (i = 0; i < nuse; i++, psum++) + *psum = *psum * multiplier; + } + put_value(prec,prec->sptr,nuse); + prec->inx = 0; + return(0); +} + +static int compress_scalar(struct compressRecord *prec,double *psource) +{ + double value = *psource; + double *pdest=&prec->cvb; + epicsInt32 inx = prec->inx; + + /* compress according to specified algorithm */ + switch (prec->alg){ + case (compressALG_N_to_1_Low_Value): + if ((value < *pdest) || (inx == 0)) + *pdest = value; + break; + case (compressALG_N_to_1_High_Value): + if ((value > *pdest) || (inx == 0)) + *pdest = value; + break; + /* for scalars, Median not implemented => use average */ + case (compressALG_N_to_1_Average): + case (compressALG_N_to_1_Median): + if (inx == 0) + *pdest = value; + else { + *pdest += value; + if(inx+1>=(prec->n)) *pdest = *pdest/(inx+1); + } + break; + } + inx++; + if(inx>=prec->n) { + put_value(prec,pdest,1); + prec->inx = 0; + return(0); + } else { + prec->inx = inx; + return(1); + } +} + +/*Beginning of record support routines*/ +static long init_record(compressRecord *prec, int pass) +{ + if (pass==0){ + if(prec->nsam<1) prec->nsam = 1; + prec->bptr = (double *)calloc(prec->nsam,sizeof(double)); + reset(prec); + } + return(0); +} + +static long process(compressRecord *prec) +{ + long status=0; + long nelements = 0; + int alg = prec->alg; + + prec->pact = TRUE; + if(!dbIsLinkConnected(&prec->inp) + || dbGetNelements(&prec->inp,&nelements) + || nelements<=0) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + } else { + if(!prec->wptr || nelements!=prec->inpn) { + if(prec->wptr) { + free(prec->wptr); + reset(prec); + } + prec->wptr = (double *)dbCalloc(nelements,sizeof(double)); + prec->inpn = nelements; + } + status = dbGetLink(&prec->inp,DBF_DOUBLE,prec->wptr,0,&nelements); + if(status || nelements<=0) { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + status = 0; + } else { + if(alg==compressALG_Average) { + status = array_average(prec,prec->wptr,nelements); + } else if(alg==compressALG_Circular_Buffer) { + (void)put_value(prec,prec->wptr,nelements); + status = 0; + } else if(nelements>1) { + status = compress_array(prec,prec->wptr,nelements); + }else if(nelements==1){ + status = compress_scalar(prec,prec->wptr); + }else status=1; + } + } + /* check event list */ + if(status!=1) { + prec->udf=FALSE; + recGblGetTimeStamp(prec); + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + } + prec->pact=FALSE; + return(0); +} + +static long special(DBADDR *paddr, int after) +{ + compressRecord *prec = (compressRecord *)(paddr->precord); + int special_type = paddr->special; + + if(!after) return(0); + switch(special_type) { + case(SPC_RESET): + reset(prec); + return(0); + default: + recGblDbaddrError(S_db_badChoice,paddr,"compress: special"); + return(S_db_badChoice); + } +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + compressRecord *prec=(compressRecord *)paddr->precord; + + paddr->pfield = (void *)(prec->bptr); + paddr->no_elements = prec->nsam; + paddr->field_type = DBF_DOUBLE; + paddr->field_size = sizeof(double); + paddr->dbr_field_type = DBF_DOUBLE; + return(0); +} + +static long get_array_info(DBADDR *paddr,long *no_elements, long *offset) +{ + compressRecord *prec=(compressRecord *)paddr->precord; + + *no_elements = prec->nuse; + if(prec->nuse==prec->nsam) *offset = prec->off; + else *offset = 0; + return(0); +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + compressRecord *prec=(compressRecord *)paddr->precord; + + prec->off = (prec->off + nNew) % (prec->nsam); + prec->nuse = (prec->nuse + nNew); + if(prec->nuse > prec->nsam) prec->nuse = prec->nsam; + return(0); +} + +static long get_units(DBADDR *paddr,char *units) +{ + compressRecord *prec=(compressRecord *)paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + return(0); +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + compressRecord *prec=(compressRecord *)paddr->precord; + + *precision = prec->prec; + if(paddr->pfield == (void *)prec->bptr) return(0); + recGblGetPrec(paddr,precision); + return(0); +} + +static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) +{ + compressRecord *prec=(compressRecord *)paddr->precord; + + if(paddr->pfield==(void *)prec->bptr + || paddr->pfield==(void *)&prec->ihil + || paddr->pfield==(void *)&prec->ilil){ + 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) +{ + compressRecord *prec=(compressRecord *)paddr->precord; + + if(paddr->pfield==(void *)prec->bptr + || paddr->pfield==(void *)&prec->ihil + || paddr->pfield==(void *)&prec->ilil){ + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + } else recGblGetControlDouble(paddr,pcd); + return(0); +} diff --git a/src/rec/compressRecord.dbd b/src/rec/compressRecord.dbd new file mode 100644 index 000000000..e3bcd438e --- /dev/null +++ b/src/rec/compressRecord.dbd @@ -0,0 +1,131 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(compressALG) { + choice(compressALG_N_to_1_Low_Value,"N to 1 Low Value") + choice(compressALG_N_to_1_High_Value,"N to 1 High Value") + choice(compressALG_N_to_1_Average,"N to 1 Average") + choice(compressALG_Average,"Average") + choice(compressALG_Circular_Buffer,"Circular Buffer") + choice(compressALG_N_to_1_Median,"N to 1 Median") +} +recordtype(compress) { + include "dbCommon.dbd" + field(VAL,DBF_NOACCESS) { + prompt("Value") + asl(ASL0) + special(SPC_DBADDR) + pp(TRUE) + extra("void * val") + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_COMPRESS) + interest(1) + } + field(RES,DBF_SHORT) { + prompt("Reset") + asl(ASL0) + special(SPC_RESET) + interest(3) + } + field(ALG,DBF_MENU) { + prompt("Compression Algorithm") + promptgroup(GUI_ALARMS) + special(SPC_RESET) + interest(1) + menu(compressALG) + } + field(NSAM,DBF_ULONG) { + prompt("Number of Values") + promptgroup(GUI_COMPRESS) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(N,DBF_ULONG) { + prompt("N to 1 Compression") + promptgroup(GUI_COMPRESS) + special(SPC_RESET) + interest(1) + initial("1") + } + field(IHIL,DBF_DOUBLE) { + prompt("Init High Interest Lim") + promptgroup(GUI_COMPRESS) + interest(1) + } + field(ILIL,DBF_DOUBLE) { + prompt("Init Low Interest Lim") + promptgroup(GUI_COMPRESS) + interest(1) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("EngineeringUnits") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(OFF,DBF_ULONG) { + prompt("Offset") + special(SPC_NOMOD) + } + field(NUSE,DBF_ULONG) { + prompt("Number Used") + special(SPC_NOMOD) + } + field(BPTR,DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + interest(4) + extra("double *bptr") + } + field(SPTR,DBF_NOACCESS) { + prompt("Summing Buffer Ptr") + special(SPC_NOMOD) + interest(4) + extra("double *sptr") + } + field(WPTR,DBF_NOACCESS) { + prompt("Working Buffer Ptr") + special(SPC_NOMOD) + interest(4) + extra("double *wptr") + } + field(INPN,DBF_LONG) { + prompt("Number of elements in Working Buffer") + special(SPC_NOMOD) + interest(4) + } + field(CVB,DBF_DOUBLE) { + prompt("Compress Value Buffer") + special(SPC_NOMOD) + interest(3) + } + field(INX,DBF_ULONG) { + prompt("Compressed Array Inx") + special(SPC_NOMOD) + interest(3) + } +} diff --git a/src/rec/dfanoutRecord.c b/src/rec/dfanoutRecord.c new file mode 100644 index 000000000..ba6c579aa --- /dev/null +++ b/src/rec/dfanoutRecord.c @@ -0,0 +1,333 @@ +/*************************************************************************\ +* Copyright (c) 2002 Southeastern Universities Research Association, as +* Operator of Thomas Jefferson National Accelerator Facility. +* EPICS BASE is 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 */ + +/* recDfanout.c - Record Support Routines for Dfanout records */ +/* + * Original Author: Matt Bickley (Sometime in 1994) + * + * Modification Log: + * ----------------- + * .01 1994 mhb Started with longout record to make the data fanout + * .02 May 10, 96 jt Bug Fix + * .03 11SEP2000 mrk LONG=>DOUBLE, add SELL,SELN,SELM + */ + + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsMath.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#include "menuOmsl.h" +#define GEN_SIZE_OFFSET +#include "dfanoutRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(dfanoutRecord *, int); +static long process(dfanoutRecord *); +#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(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *,struct dbr_grDouble *); +static long get_control_double(DBADDR *,struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *,struct dbr_alDouble *); + +rset dfanoutRSET={ + 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,dfanoutRSET); + + +static void checkAlarms(dfanoutRecord *); +static void monitor(dfanoutRecord *); +static void push_values(dfanoutRecord *); + +#define OUT_ARG_MAX 8 + + +static long init_record(dfanoutRecord *prec, int pass) +{ + if (pass==0) return(0); + + recGblInitConstantLink(&prec->sell,DBF_USHORT,&prec->seln); + /* get the initial value dol is a constant*/ + if(recGblInitConstantLink(&prec->dol,DBF_DOUBLE,&prec->val)) + prec->udf = isnan(prec->val); + return(0); +} + +static long process(dfanoutRecord *prec) +{ + long status=0; + + if (!prec->pact + && (prec->dol.type != CONSTANT) + && (prec->omsl == menuOmslclosed_loop)){ + status = dbGetLink(&(prec->dol),DBR_DOUBLE,&(prec->val),0,0); + if(prec->dol.type!=CONSTANT && RTN_SUCCESS(status)) + prec->udf = isnan(prec->val); + } + prec->pact = TRUE; + recGblGetTimeStamp(prec); + /* Push out the data to all the forward links */ + dbGetLink(&(prec->sell),DBR_USHORT,&(prec->seln),0,0); + checkAlarms(prec); + push_values(prec); + monitor(prec); + recGblFwdLink(prec); + prec->pact=FALSE; + return(status); +} + +static long get_units(DBADDR *paddr,char *units) +{ + dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + return(0); +} + +static long get_precision(DBADDR *paddr,long *precision) +{ + dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == dfanoutRecordVAL + || fieldIndex == dfanoutRecordHIHI + || fieldIndex == dfanoutRecordHIGH + || fieldIndex == dfanoutRecordLOW + || fieldIndex == dfanoutRecordLOLO + || fieldIndex == dfanoutRecordHOPR + || fieldIndex == dfanoutRecordLOPR) { + *precision = prec->prec; + } else { + recGblGetPrec(paddr,precision); + } + return(0); +} + +static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) +{ + dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == dfanoutRecordVAL + || fieldIndex == dfanoutRecordHIHI + || fieldIndex == dfanoutRecordHIGH + || fieldIndex == dfanoutRecordLOW + || fieldIndex == dfanoutRecordLOLO + || fieldIndex == dfanoutRecordHOPR + || fieldIndex == dfanoutRecordLOPR) { + 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) +{ + dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == dfanoutRecordVAL + || fieldIndex == dfanoutRecordHIHI + || fieldIndex == dfanoutRecordHIGH + || fieldIndex == dfanoutRecordLOW + || fieldIndex == dfanoutRecordLOLO) { + 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) +{ + dfanoutRecord *prec=(dfanoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + + if(fieldIndex == dfanoutRecordVAL) { + 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(dfanoutRecord *prec) +{ + double val, hyst, lalm; + double alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +static void monitor(dfanoutRecord *prec) +{ + unsigned short monitor_mask; + + double delta; + + monitor_mask = recGblResetAlarms(prec); + /* check for value change */ + delta = prec->mlst - prec->val; + if(delta<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) 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; +} + +static void push_values(dfanoutRecord *prec) +{ + struct link *plink; /* structure of the link field */ + int i; + long status; + unsigned short state; + + switch (prec->selm){ + case (dfanoutSELM_All): + for(i=0, plink=&(prec->outa); ival),1); + if(status) recGblSetSevr(prec,LINK_ALARM,MAJOR_ALARM); + } + break; + case (dfanoutSELM_Specified): + if(prec->seln>OUT_ARG_MAX) { + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + break; + } + if(prec->seln==0) break; + plink=&(prec->outa); + plink += (prec->seln -1); + status=dbPutLink(plink,DBR_DOUBLE,&(prec->val),1); + if(status) recGblSetSevr(prec,LINK_ALARM,MAJOR_ALARM); + break; + case (dfanoutSELM_Mask): + if(prec->seln==0) break; + for(i=0, plink=&(prec->outa), state=prec->seln; + i>=1) { + if(state&1) { + status=dbPutLink(plink,DBR_DOUBLE,&(prec->val),1); + if(status) recGblSetSevr(prec,LINK_ALARM,MAJOR_ALARM); + } + } + break; + default: + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + } + +} diff --git a/src/rec/dfanoutRecord.dbd b/src/rec/dfanoutRecord.dbd new file mode 100644 index 000000000..c68e56c74 --- /dev/null +++ b/src/rec/dfanoutRecord.dbd @@ -0,0 +1,192 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(dfanoutSELM) { + choice(dfanoutSELM_All,"All") + choice(dfanoutSELM_Specified,"Specified") + choice(dfanoutSELM_Mask,"Mask") +} +recordtype(dfanout) { + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Desired Output") + asl(ASL0) + pp(TRUE) + } + field(SELM,DBF_MENU) { + prompt("Select Mechanism") + promptgroup(GUI_LINKS) + interest(1) + menu(dfanoutSELM) + } + field(SELN,DBF_USHORT) { + prompt("Link Selection") + interest(1) + initial("1") + } + field(SELL,DBF_INLINK) { + prompt("Link Selection Loc") + promptgroup(GUI_LINKS) + interest(1) + } + field(OUTA,DBF_OUTLINK) { + prompt("Output Spec A") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTB,DBF_OUTLINK) { + prompt("Output Spec B") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTC,DBF_OUTLINK) { + prompt("Output Spec C") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTD,DBF_OUTLINK) { + prompt("Output Spec D") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTE,DBF_OUTLINK) { + prompt("Output Spec E") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTF,DBF_OUTLINK) { + prompt("Output Spec F") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTG,DBF_OUTLINK) { + prompt("Output Spec G") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OUTH,DBF_OUTLINK) { + prompt("Output Spec H") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup(GUI_INPUTS) + interest(1) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_OUTPUT) + interest(1) + menu(menuOmsl) + } + field(EGU,DBF_STRING) { + prompt("Units name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_DOUBLE) { + 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) + } +} diff --git a/src/rec/eventRecord.c b/src/rec/eventRecord.c new file mode 100644 index 000000000..01a71daa4 --- /dev/null +++ b/src/rec/eventRecord.c @@ -0,0 +1,194 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recEvent.c - Record Support Routines for Event records */ +/* + * Author: Janet Anderson + * Date: 12-13-91 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbScan.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "eventRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(eventRecord *, int); +static long process(eventRecord *); +#define special NULL +static long get_value(eventRecord *, struct valueDes *); +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset eventRSET={ + 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,eventRSET); + +struct eventdset { /* event input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_event;/*(0)=> success */ +}; +static void monitor(eventRecord *); +static long readValue(eventRecord *); + + +static long init_record(eventRecord *prec, int pass) +{ + struct eventdset *pdset; + long status=0; + + if (pass==0) return(0); + + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + if (prec->siol.type == CONSTANT) { + recGblInitConstantLink(&prec->siol,DBF_USHORT,&prec->sval); + } + + if( (pdset=(struct eventdset *)(prec->dset)) && (pdset->init_record) ) + status=(*pdset->init_record)(prec); + return(status); +} + +static long process(eventRecord *prec) +{ + struct eventdset *pdset = (struct eventdset *)(prec->dset); + long status=0; + unsigned char pact=prec->pact; + + if((pdset!=NULL) && (pdset->number >= 5) && pdset->read_event ) + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + if(prec->val>0) post_event((int)prec->val); + + recGblGetTimeStamp(prec); + + /* check event list */ + monitor(prec); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return(status); +} + + +static long get_value(eventRecord *prec, struct valueDes *pvdes) +{ + pvdes->field_type = DBF_USHORT; + pvdes->no_elements=1; + pvdes->pvalue = (void *)(&prec->val); + return(0); +} + + +static void monitor(eventRecord *prec) +{ + unsigned short monitor_mask; + + /* get previous stat and sevr and new stat and sevr*/ + monitor_mask = recGblResetAlarms(prec); + db_post_events(prec,&prec->val,monitor_mask|DBE_VALUE); + return; +} + +static long readValue(eventRecord *prec) +{ + long status; + struct eventdset *pdset = (struct eventdset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_event)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); + + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->read_event)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbGetLink(&(prec->siol),DBR_USHORT, + &(prec->sval),0,0); + if (status==0) { + prec->val=prec->sval; + prec->udf=FALSE; + } + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/eventRecord.dbd b/src/rec/eventRecord.dbd new file mode 100644 index 000000000..f63355900 --- /dev/null +++ b/src/rec/eventRecord.dbd @@ -0,0 +1,46 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(event) { + include "dbCommon.dbd" + field(VAL,DBF_USHORT) { + prompt("Event Number To Post") + promptgroup(GUI_INPUTS) + asl(ASL0) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SVAL,DBF_USHORT) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } +} diff --git a/src/rec/fanoutRecord.c b/src/rec/fanoutRecord.c new file mode 100644 index 000000000..c261ec9ab --- /dev/null +++ b/src/rec/fanoutRecord.c @@ -0,0 +1,142 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ +/* + * Original Author: Bob Dalesio + * Date: 12-20-88 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "dbCommon.h" +#define GEN_SIZE_OFFSET +#include "fanoutRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(fanoutRecord *, int); +static long process(fanoutRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset fanoutRSET={ + 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,fanoutRSET); + +static long init_record(fanoutRecord *prec, int pass) +{ + + if (pass==0) return(0); + recGblInitConstantLink(&prec->sell,DBF_USHORT,&prec->seln); + return(0); +} + +static long process(fanoutRecord *prec) +{ + struct link *plink; + unsigned short state; + short i; + unsigned short monitor_mask; + + prec->pact = TRUE; + + /* fetch link selection */ + dbGetLink(&(prec->sell),DBR_USHORT,&(prec->seln),0,0); + switch (prec->selm){ + case (fanoutSELM_All): + plink=&(prec->lnk1); + state=prec->seln; + for ( i=0; i<6; i++, state>>=1, plink++) { + if(plink->type!=CONSTANT) dbScanFwdLink(plink); + } + break; + case (fanoutSELM_Specified): + if(prec->seln>6) { + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + break; + } + if(prec->seln==0) { + break; + } + plink=&(prec->lnk1); + plink += (prec->seln-1); dbScanFwdLink(plink); + break; + case (fanoutSELM_Mask): + if(prec->seln==0) { + break; + } + if(prec->seln>63 ) { + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + break; + } + plink=&(prec->lnk1); + state=prec->seln; + for ( i=0; i<6; i++, state>>=1, plink++) { + if(state & 1 && plink->type!=CONSTANT) dbScanFwdLink(plink); + } + break; + default: + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + } + prec->udf=FALSE; + recGblGetTimeStamp(prec); + /* check monitors*/ + /* get previous stat and sevr and new stat and sevr*/ + monitor_mask = recGblResetAlarms(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + prec->pact=FALSE; + return(0); +} diff --git a/src/rec/fanoutRecord.dbd b/src/rec/fanoutRecord.dbd new file mode 100644 index 000000000..6a03079d9 --- /dev/null +++ b/src/rec/fanoutRecord.dbd @@ -0,0 +1,68 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(fanoutSELM) { + choice(fanoutSELM_All,"All") + choice(fanoutSELM_Specified,"Specified") + choice(fanoutSELM_Mask,"Mask") +} +recordtype(fanout) { + include "dbCommon.dbd" + field(VAL,DBF_LONG) { + prompt("Used to trigger") + asl(ASL0) + pp(TRUE) + } + field(SELM,DBF_MENU) { + prompt("Select Mechanism") + promptgroup(GUI_LINKS) + interest(1) + menu(fanoutSELM) + } + field(SELN,DBF_USHORT) { + prompt("Link Selection") + interest(1) + initial("1") + } + field(SELL,DBF_INLINK) { + prompt("Link Selection Loc") + promptgroup(GUI_LINKS) + interest(1) + } + field(LNK1,DBF_FWDLINK) { + prompt("Forward Link 1") + promptgroup(GUI_LINKS) + interest(1) + } + field(LNK2,DBF_FWDLINK) { + prompt("Forward Link 2") + promptgroup(GUI_LINKS) + interest(1) + } + field(LNK3,DBF_FWDLINK) { + prompt("Forward Link 3") + promptgroup(GUI_LINKS) + interest(1) + } + field(LNK4,DBF_FWDLINK) { + prompt("Forward Link 4") + promptgroup(GUI_LINKS) + interest(1) + } + field(LNK5,DBF_FWDLINK) { + prompt("Forward Link 5") + promptgroup(GUI_LINKS) + interest(1) + } + field(LNK6,DBF_FWDLINK) { + prompt("Forward Link 6") + promptgroup(GUI_LINKS) + interest(1) + } +} diff --git a/src/rec/histogramRecord.c b/src/rec/histogramRecord.c new file mode 100644 index 000000000..a068563d4 --- /dev/null +++ b/src/rec/histogramRecord.c @@ -0,0 +1,427 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recHistogram.c - Record Support Routines for Histogram records */ +/* + * Author: Janet Anderson + * Date: 5/20/91 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "callback.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "epicsPrint.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "special.h" +#include "recSup.h" +#include "recGbl.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "histogramRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(histogramRecord *, int); +static long process(histogramRecord *); +static long special(DBADDR *, int); +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +#define put_array_info NULL +#define get_units NULL +static long get_precision(DBADDR *paddr,long *precision); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_alarm_double NULL +static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd); +static long get_control_double(DBADDR *paddr,struct dbr_ctrlDouble *pcd); + +rset histogramRSET={ + 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,histogramRSET); + +struct histogramdset { /* histogram input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_histogram;/*(0,2)=> success and add_count, don't add_count)*/ + /* if add_count then sgnl added to array */ + DEVSUPFUN special_linconv; +}; + +/* control block for callback*/ +typedef struct myCallback { + CALLBACK callback; + histogramRecord *prec; +}myCallback; + +static long add_count(histogramRecord *); +static long clear_histogram(histogramRecord *); +static void monitor(histogramRecord *); +static long readValue(histogramRecord *); + +static void wdogCallback(CALLBACK *arg) +{ + myCallback *pcallback; + histogramRecord *prec; + + callbackGetUser(pcallback,arg); + prec = pcallback->prec; + /* force post events for any count change */ + if(prec->mcnt>0){ + dbScanLock((struct dbCommon *)prec); + recGblGetTimeStamp(prec); + db_post_events(prec,prec->bptr,DBE_VALUE|DBE_LOG); + prec->mcnt=0; + dbScanUnlock((struct dbCommon *)prec); + } + + if(prec->sdel>0) { + /* start new watchdog timer on monitor */ + callbackRequestDelayed(&pcallback->callback,(double)prec->sdel); + } + + return; +} +static long wdogInit(histogramRecord *prec) +{ + myCallback *pcallback; + + if(prec->wdog==NULL && prec->sdel>0) { + /* initialize a watchdog timer */ + pcallback = (myCallback *)(calloc(1,sizeof(myCallback))); + pcallback->prec = prec; + if(!pcallback) return -1; + callbackSetCallback(wdogCallback,&pcallback->callback); + callbackSetUser(pcallback,&pcallback->callback); + callbackSetPriority(priorityLow,&pcallback->callback); + prec->wdog = (void *)pcallback; + } + + if (!prec->wdog) return -1; + pcallback = (myCallback *)prec->wdog; + if(!pcallback) return -1; + if( prec->sdel>0) { + /* start new watchdog timer on monitor */ + callbackRequestDelayed(&pcallback->callback,(double)prec->sdel); + } + return 0; +} + +static long init_record(histogramRecord *prec, int pass) +{ + struct histogramdset *pdset; + long status; + + if (pass==0){ + + /* allocate space for histogram array */ + if(prec->bptr==NULL) { + if(prec->nelm<=0) prec->nelm=1; + prec->bptr = (epicsUInt32 *)calloc(prec->nelm,sizeof(epicsUInt32)); + } + + /* calulate width of array element */ + prec->wdth=(prec->ulim-prec->llim)/prec->nelm; + + return(0); + } + + wdogInit(prec); + + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + if (prec->siol.type == CONSTANT) { + recGblInitConstantLink(&prec->siol,DBF_DOUBLE,&prec->sval); + } + + /* must have device support defined */ + if(!(pdset = (struct histogramdset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"histogram: init_record"); + return(S_dev_noDSET); + } + /* must have read_histogram function defined */ + if( (pdset->number < 6) || (pdset->read_histogram == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"histogram: init_record"); + return(S_dev_missingSup); + } + /* call device support init_record */ + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + } + return(0); +} + +static long process(histogramRecord *prec) +{ + struct histogramdset *pdset = (struct histogramdset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->read_histogram==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"read_histogram"); + return(S_dev_missingSup); + } + + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + + if(status==0)add_count(prec); + else if(status==2)status=0; + + /* check event list */ + monitor(prec); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return(status); +} + +static long special(DBADDR *paddr,int after) +{ + histogramRecord *prec = (histogramRecord *)(paddr->precord); + int special_type = paddr->special; + int fieldIndex = dbGetFieldIndex(paddr); + + + if(!after) return(0); + switch(special_type) { + case(SPC_CALC): + if(prec->cmd <=1){ + clear_histogram(prec); + prec->cmd =0; + } else if (prec->cmd ==2){ + prec->csta=TRUE; + prec->cmd =0; + } else if (prec->cmd ==3){ + prec->csta=FALSE; + prec->cmd =0; + } + return(0); + case(SPC_MOD): + /* increment frequency in histogram array */ + add_count(prec); + return(0); + case(SPC_RESET): + if (fieldIndex == histogramRecordSDEL) { + wdogInit(prec); + } else { + prec->wdth=(prec->ulim-prec->llim)/prec->nelm; + clear_histogram(prec); + } + return(0); + default: + recGblDbaddrError(S_db_badChoice,paddr,"histogram: special"); + return(S_db_badChoice); + } +} + +static void monitor(histogramRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + /* post events for count change */ + if(prec->mcnt>prec->mdel){ + /* post events for count change */ + monitor_mask |= DBE_VALUE|DBE_LOG; + /* reset counts since monitor */ + prec->mcnt = 0; + } + /* send out monitors connected to the value field */ + if(monitor_mask) db_post_events(prec,prec->bptr,monitor_mask); + + return; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + histogramRecord *prec=(histogramRecord *)paddr->precord; + + paddr->pfield = (void *)(prec->bptr); + paddr->no_elements = prec->nelm; + paddr->field_type = DBF_ULONG; + paddr->field_size = sizeof(epicsUInt32); + paddr->dbr_field_type = DBF_ULONG; + return(0); +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + histogramRecord *prec=(histogramRecord *)paddr->precord; + + *no_elements = prec->nelm; + *offset = 0; + return(0); +} + +static long add_count(histogramRecord *prec) +{ + double temp; + epicsUInt32 *pdest; + int i; + + if(prec->csta==FALSE) return(0); + + if(prec->llim >= prec->ulim) { + if (prec->nsevstat = SOFT_ALARM; + prec->sevr = INVALID_ALARM; + return(-1); + } + } + if(prec->sgnlllim || prec->sgnl >= prec->ulim) return(0); + + temp=prec->sgnl-prec->llim; + for (i=1;i<=prec->nelm;i++){ + if (temp<=(double)i*prec->wdth) break; + } + pdest=prec->bptr+i-1; + if (*pdest == (epicsUInt32) UINT_MAX) *pdest=0; + (*pdest)++; + prec->mcnt++; + + return(0); +} + +static long clear_histogram(histogramRecord *prec) +{ + int i; + + for (i = 0; i < prec->nelm; i++) + prec->bptr[i] = 0; + prec->mcnt = prec->mdel + 1; + prec->udf = FALSE; + + return(0); +} + +static long readValue(histogramRecord *prec) +{ + long status; + struct histogramdset *pdset = (struct histogramdset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_histogram)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); + + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->read_histogram)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbGetLink(&(prec->siol),DBR_DOUBLE, + &(prec->sval),0,0); + + if (status==0){ + prec->sgnl=prec->sval; + } + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} + +static long get_precision(DBADDR *paddr,long *precision) +{ + histogramRecord *prec=(histogramRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + *precision = prec->prec; + if(fieldIndex == histogramRecordULIM + || fieldIndex == histogramRecordLLIM + || fieldIndex == histogramRecordSDEL + || fieldIndex == histogramRecordSGNL + || fieldIndex == histogramRecordSVAL + || fieldIndex == histogramRecordWDTH) { + *precision = prec->prec; + } + recGblGetPrec(paddr,precision); + return(0); +} +static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) +{ + histogramRecord *prec=(histogramRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == histogramRecordBPTR){ + 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) +{ + histogramRecord *prec=(histogramRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == histogramRecordBPTR){ + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + } else recGblGetControlDouble(paddr,pcd); + return(0); +} diff --git a/src/rec/histogramRecord.dbd b/src/rec/histogramRecord.dbd new file mode 100644 index 000000000..6def0f41a --- /dev/null +++ b/src/rec/histogramRecord.dbd @@ -0,0 +1,137 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(histogramCMD) { + choice(histogramCMD_Read,"Read") + choice(histogramCMD_Clear,"Clear") + choice(histogramCMD_Start,"Start") + choice(histogramCMD_Stop,"Stop") +} +recordtype(histogram) { + include "dbCommon.dbd" + field(VAL,DBF_NOACCESS) { + prompt("Value") + asl(ASL0) + special(SPC_DBADDR) + extra("void * val") + } + field(NELM,DBF_USHORT) { + prompt("Num of Array Elements") + promptgroup(GUI_HIST) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(CSTA,DBF_SHORT) { + prompt("Collection Status") + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(CMD,DBF_MENU) { + prompt("Collection Control") + asl(ASL0) + special(SPC_CALC) + interest(1) + menu(histogramCMD) + } + field(ULIM,DBF_DOUBLE) { + prompt("Upper Signal Limit") + promptgroup(GUI_HIST) + special(SPC_RESET) + interest(1) + } + field(LLIM,DBF_DOUBLE) { + prompt("Lower Signal Limit ") + promptgroup(GUI_HIST) + special(SPC_RESET) + interest(1) + } + field(WDTH,DBF_DOUBLE) { + prompt("Element Width") + special(SPC_NOMOD) + interest(3) + } + field(SGNL,DBF_DOUBLE) { + prompt("Signal Value") + special(SPC_MOD) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(SVL,DBF_INLINK) { + prompt("Signal Value Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(BPTR,DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + interest(4) + extra("epicsUInt32 *bptr") + } + field(WDOG,DBF_NOACCESS) { + prompt("Watchdog callback") + special(SPC_NOMOD) + interest(4) + extra("void * wdog") + } + field(MDEL,DBF_SHORT) { + prompt("Monitor Count Deadband") + promptgroup(GUI_HIST) + interest(1) + } + field(MCNT,DBF_SHORT) { + prompt("Counts Since Monitor") + special(SPC_NOMOD) + interest(3) + } + field(SDEL,DBF_DOUBLE) { + prompt("Monitor Seconds Dband") + promptgroup(GUI_HIST) + special(SPC_RESET) + interest(1) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SVAL,DBF_DOUBLE) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(HOPR,DBF_ULONG) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_ULONG) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } +} diff --git a/src/rec/longinRecord.c b/src/rec/longinRecord.c new file mode 100644 index 000000000..2d2833fba --- /dev/null +++ b/src/rec/longinRecord.c @@ -0,0 +1,333 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recLongin.c - Record Support Routines for Longin records */ +/* + * Author: Janet Anderson + * Date: 9/23/91 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "longinRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(longinRecord *, int); +static long process(longinRecord *); +#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(DBADDR *, char *); +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); + +rset longinRSET={ + 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,longinRSET); + + +struct longindset { /* longin input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_longin; /*returns: (-1,0)=>(failure,success)*/ +}; +static void checkAlarms(longinRecord *prec); +static void monitor(longinRecord *prec); +static long readValue(longinRecord *prec); + + +static long init_record(longinRecord *prec, int pass) +{ + struct longindset *pdset; + long status; + + if (pass==0) return(0); + + /* longin.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + /* longin.siol must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siol.type == CONSTANT) { + recGblInitConstantLink(&prec->siol,DBF_LONG,&prec->sval); + } + + if(!(pdset = (struct longindset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"longin: init_record"); + return(S_dev_noDSET); + } + /* must have read_longin function defined */ + if( (pdset->number < 5) || (pdset->read_longin == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"longin: init_record"); + return(S_dev_missingSup); + } + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + } + prec->mlst = prec->val; + prec->alst = prec->val; + prec->lalm = prec->val; + return(0); +} + +static long process(longinRecord *prec) +{ + struct longindset *pdset = (struct longindset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->read_longin==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"read_longin"); + return(S_dev_missingSup); + } + + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + if (status==0) prec->udf = FALSE; + + /* 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) +{ + longinRecord *prec=(longinRecord *)paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + return(0); +} + + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + longinRecord *prec=(longinRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val + || paddr->pfield==(void *)&prec->hihi + || paddr->pfield==(void *)&prec->high + || paddr->pfield==(void *)&prec->low + || paddr->pfield==(void *)&prec->lolo){ + 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) +{ + longinRecord *prec=(longinRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val + || paddr->pfield==(void *)&prec->hihi + || paddr->pfield==(void *)&prec->high + || paddr->pfield==(void *)&prec->low + || paddr->pfield==(void *)&prec->lolo){ + 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) +{ + longinRecord *prec=(longinRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val){ + pad->upper_alarm_limit = prec->hihi; + pad->upper_warning_limit = prec->high; + pad->lower_warning_limit = prec->low; + pad->lower_alarm_limit = prec->lolo; + } else recGblGetAlarmDouble(paddr,pad); + return(0); +} + +static void checkAlarms(longinRecord *prec) +{ + epicsInt32 val, hyst, lalm; + epicsInt32 alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +/* DELTA calculates the absolute difference between its arguments + * expressed as an unsigned 32-bit integer */ +#define DELTA(last, val) \ + ((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last))) + +static void monitor(longinRecord *prec) +{ + unsigned short monitor_mask = recGblResetAlarms(prec); + + if (prec->mdel < 0 || + DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) { + /* post events for value change */ + monitor_mask |= DBE_VALUE; + /* update last value monitored */ + prec->mlst = prec->val; + } + + if (prec->adel < 0 || + DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) { + /* post events for archive value 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); +} + +static long readValue(longinRecord *prec) +{ + long status; + struct longindset *pdset = (struct longindset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_longin)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->read_longin)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbGetLink(&(prec->siol),DBR_LONG, + &(prec->sval),0,0); + + if (status==0) { + prec->val=prec->sval; + prec->udf=FALSE; + } + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/longinRecord.dbd b/src/rec/longinRecord.dbd new file mode 100644 index 000000000..4bd734018 --- /dev/null +++ b/src/rec/longinRecord.dbd @@ -0,0 +1,145 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(longin) { + include "dbCommon.dbd" + field(VAL,DBF_LONG) { + prompt("Current value") + promptgroup(GUI_INPUTS) + asl(ASL0) + pp(TRUE) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Units name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(HOPR,DBF_LONG) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_LONG) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HIHI,DBF_LONG) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_LONG) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_LONG) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_LONG) { + 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_LONG) { + prompt("Alarm Deadband") + promptgroup(GUI_ALARMS) + interest(1) + } + field(ADEL,DBF_LONG) { + prompt("Archive Deadband") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(MDEL,DBF_LONG) { + prompt("Monitor Deadband") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LALM,DBF_LONG) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_LONG) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_LONG) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SVAL,DBF_LONG) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } +} diff --git a/src/rec/longoutRecord.c b/src/rec/longoutRecord.c new file mode 100644 index 000000000..c254176bb --- /dev/null +++ b/src/rec/longoutRecord.c @@ -0,0 +1,377 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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: Janet Anderson + * Date: 9/23/91 + */ +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "longoutRecord.h" +#undef GEN_SIZE_OFFSET +#include "menuIvoa.h" +#include "menuOmsl.h" +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(longoutRecord *, int); +static long process(longoutRecord *); +#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(DBADDR *, char *); +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); + +rset longoutRSET={ + 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,longoutRSET); + + +struct longoutdset { /* longout input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_longout;/*(-1,0)=>(failure,success*/ +}; +static void checkAlarms(longoutRecord *prec); +static void monitor(longoutRecord *prec); +static long writeValue(longoutRecord *prec); +static void convert(longoutRecord *prec, epicsInt32 value); + + +static long init_record(longoutRecord *prec, int pass) +{ + struct longoutdset *pdset; + long status=0; + + if (pass==0) return(0); + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + if(!(pdset = (struct longoutdset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"longout: init_record"); + return(S_dev_noDSET); + } + /* must have write_longout functions defined */ + if( (pdset->number < 5) || (pdset->write_longout == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"longout: init_record"); + return(S_dev_missingSup); + } + if (prec->dol.type == CONSTANT) { + if(recGblInitConstantLink(&prec->dol,DBF_LONG,&prec->val)) + prec->udf=FALSE; + } + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + } + prec->mlst = prec->val; + prec->alst = prec->val; + prec->lalm = prec->val; + return(0); +} + +static long process(longoutRecord *prec) +{ + struct longoutdset *pdset = (struct longoutdset *)(prec->dset); + long status=0; + epicsInt32 value; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->write_longout==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"write_longout"); + return(S_dev_missingSup); + } + if (!prec->pact) { + if((prec->dol.type != CONSTANT) + && (prec->omsl == menuOmslclosed_loop)) { + status = dbGetLink(&(prec->dol),DBR_LONG, + &value,0,0); + if (prec->dol.type!=CONSTANT && RTN_SUCCESS(status)) + prec->udf=FALSE; + } + else { + value = prec->val; + } + if (!status) convert(prec,value); + } + + /* check for alarms */ + checkAlarms(prec); + + if (prec->nsev < INVALID_ALARM ) + status=writeValue(prec); /* write the new value */ + else { + switch (prec->ivoa) { + case (menuIvoaContinue_normally) : + status=writeValue(prec); /* write the new value */ + break; + case (menuIvoaDon_t_drive_outputs) : + break; + case (menuIvoaSet_output_to_IVOV) : + if(prec->pact == FALSE){ + prec->val=prec->ivov; + } + status=writeValue(prec); /* write the new value */ + break; + default : + status=-1; + recGblRecordError(S_db_badField,(void *)prec, + "longout:process Illegal IVOA field"); + } + } + + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(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) +{ + longoutRecord *prec=(longoutRecord *)paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + return(0); +} + +static long get_graphic_double(DBADDR *paddr,struct dbr_grDouble *pgd) +{ + longoutRecord *prec=(longoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == longoutRecordVAL + || fieldIndex == longoutRecordHIHI + || fieldIndex == longoutRecordHIGH + || fieldIndex == longoutRecordLOW + || fieldIndex == longoutRecordLOLO) { + 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) +{ + longoutRecord *prec=(longoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == longoutRecordVAL + || fieldIndex == longoutRecordHIHI + || fieldIndex == longoutRecordHIGH + || fieldIndex == longoutRecordLOW + || fieldIndex == longoutRecordLOLO) { + /* do not change pre drvh/drvl behavior */ + if(prec->drvh > prec->drvl) { + pcd->upper_ctrl_limit = prec->drvh; + pcd->lower_ctrl_limit = prec->drvl; + } else { + 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) +{ + longoutRecord *prec=(longoutRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if(fieldIndex == longoutRecordVAL) { + pad->upper_alarm_limit = prec->hihi; + pad->upper_warning_limit = prec->high; + pad->lower_warning_limit = prec->low; + pad->lower_alarm_limit = prec->lolo; + } else recGblGetAlarmDouble(paddr,pad); + return(0); +} + +static void checkAlarms(longoutRecord *prec) +{ + epicsInt32 val, hyst, lalm; + epicsInt32 alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +/* DELTA calculates the absolute difference between its arguments + * expressed as an unsigned 32-bit integer */ +#define DELTA(last, val) \ + ((epicsUInt32) ((last) > (val) ? (last) - (val) : (val) - (last))) + +static void monitor(longoutRecord *prec) +{ + unsigned short monitor_mask = recGblResetAlarms(prec); + + if (prec->mdel < 0 || + DELTA(prec->mlst, prec->val) > (epicsUInt32) prec->mdel) { + /* post events for value change */ + monitor_mask |= DBE_VALUE; + /* update last value monitored */ + prec->mlst = prec->val; + } + + if (prec->adel < 0 || + DELTA(prec->alst, prec->val) > (epicsUInt32) prec->adel) { + /* post events for archive value 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); +} + +static long writeValue(longoutRecord *prec) +{ + long status; + struct longoutdset *pdset = (struct longoutdset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->write_longout)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); + if (!RTN_SUCCESS(status)) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->write_longout)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbPutLink(&prec->siol,DBR_LONG,&prec->val,1); + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} + +static void convert(longoutRecord *prec, epicsInt32 value) +{ + /* check drive limits */ + if(prec->drvh > prec->drvl) { + if (value > prec->drvh) value = prec->drvh; + else if (value < prec->drvl) value = prec->drvl; + } + prec->val = value; +} diff --git a/src/rec/longoutRecord.dbd b/src/rec/longoutRecord.dbd new file mode 100644 index 000000000..e29251b9b --- /dev/null +++ b/src/rec/longoutRecord.dbd @@ -0,0 +1,176 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(longout) { + include "dbCommon.dbd" + field(VAL,DBF_LONG) { + prompt("Desired Output") + promptgroup(GUI_OUTPUT) + asl(ASL0) + pp(TRUE) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_OUTPUT) + interest(1) + menu(menuOmsl) + } + field(EGU,DBF_STRING) { + prompt("Units name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(DRVH,DBF_LONG) { + prompt("Drive High Limit") + promptgroup(GUI_OUTPUT) + pp(TRUE) + interest(1) + } + field(DRVL,DBF_LONG) { + prompt("Drive Low Limit") + promptgroup(GUI_OUTPUT) + pp(TRUE) + interest(1) + } + field(HOPR,DBF_LONG) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_LONG) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HIHI,DBF_LONG) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_LONG) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_LONG) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_LONG) { + 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_LONG) { + prompt("Alarm Deadband") + promptgroup(GUI_ALARMS) + interest(1) + } + field(ADEL,DBF_LONG) { + prompt("Archive Deadband") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(MDEL,DBF_LONG) { + prompt("Monitor Deadband") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LALM,DBF_LONG) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_LONG) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_LONG) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(IVOA,DBF_MENU) { + prompt("INVALID output action") + promptgroup(GUI_OUTPUT) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_LONG) { + prompt("INVALID output value") + promptgroup(GUI_OUTPUT) + interest(2) + } +} diff --git a/src/rec/mbbiDirectRecord.c b/src/rec/mbbiDirectRecord.c new file mode 100644 index 000000000..a404f43b6 --- /dev/null +++ b/src/rec/mbbiDirectRecord.c @@ -0,0 +1,278 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* Copyright (c) 2002 Southeastern Universities Research Association, as +* Operator of Thomas Jefferson National Accelerator Facility. +* EPICS BASE is 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 */ + +/* recMbbiDirect.c - Record Support routines for mbboDirect records */ +/* + * Original Author: Bob Dalesio and Matthew Needes 10-07-93 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "menuSimm.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "mbbiDirectRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(mbbiDirectRecord *, int); +static long process(mbbiDirectRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset mbbiDirectRSET={ + 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,mbbiDirectRSET); + +struct mbbidset { /* multi bit binary input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_mbbi;/*(0,2)=>(success, success no convert)*/ +}; + +static void refresh_bits(mbbiDirectRecord *, unsigned short); +static void monitor(mbbiDirectRecord *); +static long readValue(mbbiDirectRecord *); + +#define NUM_BITS 16 + +/* refreshes all the bit fields based on a hardware value + and sends monitors if the bit's value or the record's + severity/status have changed */ +static void refresh_bits(mbbiDirectRecord *prec, + unsigned short monitor_mask) +{ + unsigned short i; + unsigned short mask = 1; + unsigned short momask; + unsigned char *bit; + + bit = &(prec->b0); + for (i=0; ival & mask) { + if (*bit == 0) { + *bit = 1; + momask |= DBE_VALUE | DBE_LOG; + } + } else { + if (*bit != 0) { + *bit = 0; + momask |= DBE_VALUE | DBE_LOG; + } + } + if (momask) + db_post_events(prec,bit,momask); + } +} + +static long init_record(mbbiDirectRecord *prec, int pass) +{ + struct mbbidset *pdset; + long status; + + if (pass==0) return(0); + + /* siml must be a CONSTANT or a PV_LINK or a DB_LINK or a CA_LINK*/ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + /* siol must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siol.type == CONSTANT) { + recGblInitConstantLink(&prec->siol,DBF_USHORT,&prec->sval); + } + + if(!(pdset = (struct mbbidset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"mbbiDirect: init_record"); + return(S_dev_noDSET); + } + /* must have read_mbbi function defined */ + if( (pdset->number < 5) || (pdset->read_mbbi == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"mbbiDirect: init_record"); + return(S_dev_missingSup); + } + /* initialize mask*/ + prec->mask = (1 << prec->nobt) - 1; + + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + refresh_bits(prec, 0); + } + prec->mlst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + return(0); +} + +static long process(mbbiDirectRecord *prec) +{ + struct mbbidset *pdset = (struct mbbidset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->read_mbbi==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"read_mbbi"); + return(S_dev_missingSup); + } + + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + + if(status==0) { /* convert the value */ + epicsUInt32 rval = prec->rval; + + if(prec->shft>0) rval >>= prec->shft; + prec->val = (unsigned short)rval; + prec->udf=FALSE; + + } + else if(status == 2) status = 0; + + /* check event list */ + monitor(prec); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return(status); +} + +static void monitor(mbbiDirectRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + + /* send out bit field monitors (value change and sevr change) */ + refresh_bits(prec, monitor_mask); + + /* check for value change */ + if (prec->mlst != prec->val) { + /* post events for value change and archive change */ + monitor_mask |= (DBE_VALUE | DBE_LOG); + /* update last value monitored */ + prec->mlst = prec->val; + } + /* send out monitors connected to the value field */ + if (monitor_mask){ + db_post_events(prec,&prec->val,monitor_mask); + } + if(prec->oraw!=prec->rval) { + db_post_events(prec,&prec->rval, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->oraw = prec->rval; + } + return; +} + +static long readValue(mbbiDirectRecord *prec) +{ + long status; + struct mbbidset *pdset = (struct mbbidset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_mbbi)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_ENUM, + &(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuSimmNO){ + status=(*pdset->read_mbbi)(prec); + return(status); + } + if (prec->simm == menuSimmYES){ + status=dbGetLink(&(prec->siol), + DBR_ULONG,&(prec->sval),0,0); + if (status==0){ + prec->val=(unsigned short)prec->sval; + prec->udf=FALSE; + } + status=2; /* don't convert */ + } + else if (prec->simm == menuSimmRAW){ + status=dbGetLink(&(prec->siol), + DBR_ULONG,&(prec->sval),0,0); + if (status==0){ + prec->rval=prec->sval; + prec->udf=FALSE; + } + status=0; /* convert since we've written RVAL */ + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/mbbiDirectRecord.dbd b/src/rec/mbbiDirectRecord.dbd new file mode 100644 index 000000000..fde6e6c51 --- /dev/null +++ b/src/rec/mbbiDirectRecord.dbd @@ -0,0 +1,167 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(mbbiDirect) { + include "dbCommon.dbd" + field(VAL,DBF_USHORT) { + prompt("Current Value") + promptgroup(GUI_INPUTS) + asl(ASL0) + pp(TRUE) + } + field(NOBT,DBF_SHORT) { + prompt("Number of Bits") + promptgroup(GUI_MBB) + special(SPC_NOMOD) + interest(1) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_MBB) + interest(1) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("Prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(SDEF,DBF_SHORT) { + prompt("States Defined") + special(SPC_NOMOD) + interest(3) + } + field(SHFT,DBF_USHORT) { + prompt("Shift") + promptgroup(GUI_MBB) + interest(1) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_MBB) + interest(1) + } + field(SVAL,DBF_ULONG) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_MBB) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuSimm) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_MBB) + interest(2) + menu(menuAlarmSevr) + } + field(B0,DBF_UCHAR) { + prompt("Bit 0") + pp(TRUE) + interest(1) + } + field(B1,DBF_UCHAR) { + prompt("Bit 1") + pp(TRUE) + interest(1) + } + field(B2,DBF_UCHAR) { + prompt("Bit 2") + pp(TRUE) + interest(1) + } + field(B3,DBF_UCHAR) { + prompt("Bit 3") + pp(TRUE) + interest(1) + } + field(B4,DBF_UCHAR) { + prompt("Bit 4") + pp(TRUE) + interest(1) + } + field(B5,DBF_UCHAR) { + prompt("Bit 5") + pp(TRUE) + interest(1) + } + field(B6,DBF_UCHAR) { + prompt("Bit 6") + pp(TRUE) + interest(1) + } + field(B7,DBF_UCHAR) { + prompt("Bit 7") + pp(TRUE) + interest(1) + } + field(B8,DBF_UCHAR) { + prompt("Bit 8") + pp(TRUE) + interest(1) + } + field(B9,DBF_UCHAR) { + prompt("Bit 9") + pp(TRUE) + interest(1) + } + field(BA,DBF_UCHAR) { + prompt("Bit A") + pp(TRUE) + interest(1) + } + field(BB,DBF_UCHAR) { + prompt("Bit B") + pp(TRUE) + interest(1) + } + field(BC,DBF_UCHAR) { + prompt("Bit C") + pp(TRUE) + interest(1) + } + field(BD,DBF_UCHAR) { + prompt("Bit D") + pp(TRUE) + interest(1) + } + field(BE,DBF_UCHAR) { + prompt("Bit E") + pp(TRUE) + interest(1) + } + field(BF,DBF_UCHAR) { + prompt("Bit F") + pp(TRUE) + interest(1) + } +} diff --git a/src/rec/mbbiRecord.c b/src/rec/mbbiRecord.c new file mode 100644 index 000000000..2bb6a3325 --- /dev/null +++ b/src/rec/mbbiRecord.c @@ -0,0 +1,368 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ +/* + * Original Author: Bob Dalesio + * Date: 5-9-88 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "menuSimm.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "mbbiRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(mbbiRecord *, int); +static long process(mbbiRecord *); +static long special(DBADDR *, int); +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +static long get_enum_str(DBADDR *, char *); +static long get_enum_strs(DBADDR *, struct dbr_enumStrs *); +static long put_enum_str(DBADDR *, char *); +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL +rset mbbiRSET={ + 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,mbbiRSET); + +struct mbbidset { /* multi bit binary input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_mbbi;/*(0,2)=>(success, success no convert)*/ +}; +static void checkAlarms(mbbiRecord *); +static void monitor(mbbiRecord *); +static long readValue(mbbiRecord *); + +static void init_common(mbbiRecord *prec) +{ + epicsUInt32 *pstate_values; + char *pstate_string; + short i; + + /* determine if any states are defined */ + pstate_values = &(prec->zrvl); pstate_string = prec->zrst; + prec->sdef = FALSE; + for (i=0; i<16; i++, pstate_string += sizeof(prec->zrst)) { + if((*(pstate_values+i) != 0) || (*pstate_string !='\0')) { + prec->sdef = TRUE; + return; + } + } + return; +} + +static long init_record(mbbiRecord *prec, int pass) +{ + struct mbbidset *pdset; + long status; + + if (pass==0) return(0); + + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + if (prec->siol.type == CONSTANT) { + recGblInitConstantLink(&prec->siol,DBF_USHORT,&prec->sval); + } + if(!(pdset = (struct mbbidset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"mbbi: init_record"); + return(S_dev_noDSET); + } + /* must have read_mbbi function defined */ + if( (pdset->number < 5) || (pdset->read_mbbi == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"mbbi: init_record"); + return(S_dev_missingSup); + } + /* initialize mask*/ + prec->mask = (1 << prec->nobt) - 1; + + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + } + init_common(prec); + prec->mlst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + return(0); +} + +static long process(mbbiRecord *prec) +{ + struct mbbidset *pdset = (struct mbbidset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->read_mbbi==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"read_mbbi"); + return(S_dev_missingSup); + } + + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + if(status==0) { /* convert the value */ + epicsUInt32 *pstate_values; + short i; + epicsUInt32 rval = prec->rval; + + prec->udf = FALSE; + if(prec->shft>0) rval >>= prec->shft; + if (prec->sdef){ + pstate_values = &(prec->zrvl); + prec->val = 65535; /* initalize to unknown state*/ + for (i = 0; i < 16; i++){ + if (*pstate_values == rval){ + prec->val = i; + break; + } + pstate_values++; + } + }else{ + /* the raw value is the desired value */ + prec->val = (unsigned short)rval; + } + } + else if(status == 2) status = 0; + + /* 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 special(DBADDR *paddr,int after) +{ + mbbiRecord *prec = (mbbiRecord *)(paddr->precord); + int special_type = paddr->special; + int fieldIndex = dbGetFieldIndex(paddr); + + if(!after) return(0); + switch(special_type) { + case(SPC_MOD): + init_common(prec); + if (fieldIndex >= mbbiRecordZRST && fieldIndex <= mbbiRecordFFST) + db_post_events(prec,&prec->val,DBE_PROPERTY); + return(0); + default: + recGblDbaddrError(S_db_badChoice,paddr,"mbbi: special"); + return(S_db_badChoice); + } +} + +static long get_enum_str(DBADDR *paddr,char* pstring) +{ + mbbiRecord *prec=(mbbiRecord *)paddr->precord; + char *psource; + int index; + unsigned short *pfield = (unsigned short *)paddr->pfield; + unsigned short val=*pfield; + + index = dbGetFieldIndex(paddr); + if(index!=mbbiRecordVAL) { + strcpy(pstring,"Illegal_Value"); + } else if(val<= 15) { + psource = (prec->zrst); + psource += (val * sizeof(prec->zrst)); + strncpy(pstring,psource,sizeof(prec->zrst)); + } else { + strcpy(pstring,"Illegal Value"); + } + return(0); +} + +static long get_enum_strs(DBADDR *paddr, struct dbr_enumStrs *pes) +{ + mbbiRecord *prec=(mbbiRecord *)paddr->precord; + char *psource; + int i; + short no_str; + + no_str = 0; + memset(pes->strs,'\0',sizeof(pes->strs)); + for(i=0,psource=(prec->zrst); i<16; i++, psource += sizeof(prec->zrst) ) { + strncpy(pes->strs[i],psource,sizeof(prec->zrst)); + if(*psource!=0) no_str=i+1; + } + pes->no_str=no_str; + return(0); +} + +static long put_enum_str(DBADDR *paddr, char *pstring) +{ + mbbiRecord *prec=(mbbiRecord *)paddr->precord; + char *pstate_name; + short i; + + if (prec->sdef){ + pstate_name = prec->zrst; + for (i = 0; i < 16; i++){ + if(strncmp(pstate_name,pstring,sizeof(prec->zrst))==0){ + prec->val = i; + prec->udf = FALSE; + return(0); + } + pstate_name += sizeof(prec->zrst); + } + } + return(S_db_badChoice); +} + +static void checkAlarms(mbbiRecord *prec) +{ + unsigned short *severities; + unsigned short val=prec->val; + + /* check for udf alarm */ + if(prec->udf == TRUE ){ + recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM); + } + + /* check for state alarm */ + /* unknown state */ + if (val > 15){ + recGblSetSevr(prec,STATE_ALARM,prec->unsv); + } else { + /* in a state which is an error */ + severities = (unsigned short *)&(prec->zrsv); + recGblSetSevr(prec,STATE_ALARM,severities[prec->val]); + } + + /* check for cos alarm */ + if(val == prec->lalm) return; + recGblSetSevr(prec,COS_ALARM,prec->cosv); + prec->lalm = val; + return; +} + +static void monitor(mbbiRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + /* check for value change */ + if (prec->mlst != prec->val){ + /* post events for value change and archive change */ + monitor_mask |= (DBE_VALUE | DBE_LOG); + /* update last value monitored */ + prec->mlst = prec->val; + } + /* send out monitors connected to the value field */ + if (monitor_mask){ + db_post_events(prec,&prec->val,monitor_mask); + } + if(prec->oraw!=prec->rval) { + db_post_events(prec,&prec->rval,monitor_mask|DBE_VALUE); + prec->oraw = prec->rval; + } + return; +} + +static long readValue(mbbiRecord *prec) +{ + long status; + struct mbbidset *pdset = (struct mbbidset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_mbbi)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuSimmNO){ + status=(*pdset->read_mbbi)(prec); + return(status); + } + if (prec->simm == menuSimmYES){ + status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0); + if (status==0){ + prec->val=(unsigned short)prec->sval; + prec->udf=FALSE; + } + status=2; /* dont convert */ + } + else if (prec->simm == menuSimmRAW){ + status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0); + if (status==0){ + prec->rval=prec->sval; + prec->udf=FALSE; + } + status=0; /* convert since we've written RVAL */ + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/mbbiRecord.dbd b/src/rec/mbbiRecord.dbd new file mode 100644 index 000000000..b0c33ad9e --- /dev/null +++ b/src/rec/mbbiRecord.dbd @@ -0,0 +1,469 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(mbbi) { + include "dbCommon.dbd" + field(VAL,DBF_ENUM) { + prompt("Current Value") + promptgroup(GUI_INPUTS) + asl(ASL0) + pp(TRUE) + } + field(NOBT,DBF_SHORT) { + prompt("Number of Bits") + promptgroup(GUI_MBB) + special(SPC_NOMOD) + interest(1) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_MBB) + interest(1) + } + field(ZRVL,DBF_ULONG) { + prompt("Zero Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(ONVL,DBF_ULONG) { + prompt("One Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TWVL,DBF_ULONG) { + prompt("Two Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(THVL,DBF_ULONG) { + prompt("Three Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FRVL,DBF_ULONG) { + prompt("Four Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FVVL,DBF_ULONG) { + prompt("Five Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(SXVL,DBF_ULONG) { + prompt("Six Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(SVVL,DBF_ULONG) { + prompt("Seven Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(EIVL,DBF_ULONG) { + prompt("Eight Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(NIVL,DBF_ULONG) { + prompt("Nine Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TEVL,DBF_ULONG) { + prompt("Ten Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(ELVL,DBF_ULONG) { + prompt("Eleven Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TVVL,DBF_ULONG) { + prompt("Twelve Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TTVL,DBF_ULONG) { + prompt("Thirteen Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FTVL,DBF_ULONG) { + prompt("Fourteen Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FFVL,DBF_ULONG) { + prompt("Fifteen Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(ZRST,DBF_STRING) { + prompt("Zero String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(ONST,DBF_STRING) { + prompt("One String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TWST,DBF_STRING) { + prompt("Two String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(THST,DBF_STRING) { + prompt("Three String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FRST,DBF_STRING) { + prompt("Four String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FVST,DBF_STRING) { + prompt("Five String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(SXST,DBF_STRING) { + prompt("Six String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(SVST,DBF_STRING) { + prompt("Seven String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(EIST,DBF_STRING) { + prompt("Eight String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(NIST,DBF_STRING) { + prompt("Nine String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TEST,DBF_STRING) { + prompt("Ten String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(ELST,DBF_STRING) { + prompt("Eleven String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TVST,DBF_STRING) { + prompt("Twelve String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TTST,DBF_STRING) { + prompt("Thirteen String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FTST,DBF_STRING) { + prompt("Fourteen String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FFST,DBF_STRING) { + prompt("Fifteen String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(ZRSV,DBF_MENU) { + prompt("State Zero Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(ONSV,DBF_MENU) { + prompt("State One Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TWSV,DBF_MENU) { + prompt("State Two Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(THSV,DBF_MENU) { + prompt("State Three Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FRSV,DBF_MENU) { + prompt("State Four Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FVSV,DBF_MENU) { + prompt("State Five Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(SXSV,DBF_MENU) { + prompt("State Six Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(SVSV,DBF_MENU) { + prompt("State Seven Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(EISV,DBF_MENU) { + prompt("State Eight Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(NISV,DBF_MENU) { + prompt("State Nine Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TESV,DBF_MENU) { + prompt("State Ten Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(ELSV,DBF_MENU) { + prompt("State Eleven Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TVSV,DBF_MENU) { + prompt("State Twelve Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TTSV,DBF_MENU) { + prompt("State Thirteen Sevr") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FTSV,DBF_MENU) { + prompt("State Fourteen Sevr") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FFSV,DBF_MENU) { + prompt("State Fifteen Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(UNSV,DBF_MENU) { + prompt("Unknown State Severity") + promptgroup(GUI_MBB) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Svr") + promptgroup(GUI_MBB) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("Prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(SDEF,DBF_SHORT) { + prompt("States Defined") + special(SPC_NOMOD) + interest(3) + } + field(SHFT,DBF_USHORT) { + prompt("Shift") + promptgroup(GUI_MBB) + interest(1) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_MBB) + interest(1) + } + field(SVAL,DBF_ULONG) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_MBB) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuSimm) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_MBB) + interest(2) + menu(menuAlarmSevr) + } +} diff --git a/src/rec/mbboDirectRecord.c b/src/rec/mbboDirectRecord.c new file mode 100644 index 000000000..ec6d24f21 --- /dev/null +++ b/src/rec/mbboDirectRecord.c @@ -0,0 +1,359 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recMbboDirect.c - Record Support for mbboDirect records */ +/* + * Original Author: Bob Dalesio + * Date: 10-06-93 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "mbboDirectRecord.h" +#undef GEN_SIZE_OFFSET +#include "menuOmsl.h" +#include "menuIvoa.h" +#include "menuYesNo.h" +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(mbboDirectRecord *, int); +static long process(mbboDirectRecord *); +static long special(DBADDR *, int); +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset mbboDirectRSET={ + 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,mbboDirectRSET); + +struct mbbodset { /* multi bit binary output dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (0,2)=>(success,success no convert)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; /*returns: (0,2)=>(success,success no convert)*/ +}; + + +static void convert(mbboDirectRecord *); +static void monitor(mbboDirectRecord *); +static long writeValue(mbboDirectRecord *); + +#define NUM_BITS 16 + +static long init_record(mbboDirectRecord *prec, int pass) +{ + struct mbbodset *pdset; + long status = 0; + int i; + + if (pass==0) return(0); + + /* mbboDirect.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + if(!(pdset = (struct mbbodset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"mbboDirect: init_record"); + return(S_dev_noDSET); + } + /* must have write_mbbo function defined */ + if( (pdset->number < 5) || (pdset->write_mbbo == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"mbboDirect: init_record"); + return(S_dev_missingSup); + } + if (prec->dol.type == CONSTANT){ + if(recGblInitConstantLink(&prec->dol,DBF_USHORT,&prec->val)) + prec->udf = FALSE; + } + /* initialize mask*/ + prec->mask = 0; + for (i=0; inobt; i++) { + prec->mask <<= 1; /* shift left 1 bit*/ + prec->mask |= 1; /* set low order bit*/ + } + if(pdset->init_record) { + epicsUInt32 rval; + + status=(*pdset->init_record)(prec); + /* init_record might set status */ + if(status==0){ + rval = prec->rval; + if(prec->shft>0) rval >>= prec->shft; + prec->val = (unsigned short)rval; + prec->udf = FALSE; + } else if (status == 2) status = 0; + } + prec->mlst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + prec->orbv = prec->rbv; + return(status); +} + +static long process(mbboDirectRecord *prec) +{ + struct mbbodset *pdset = (struct mbbodset *)(prec->dset); + long status=0; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->write_mbbo==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"write_mbbo"); + return(S_dev_missingSup); + } + + if (!prec->pact) { + if(prec->dol.type!=CONSTANT && prec->omsl==menuOmslclosed_loop){ + long status; + unsigned short val; + + status = dbGetLink(&prec->dol,DBR_USHORT,&val,0,0); + if(status==0) { + prec->val= val; + prec->udf= FALSE; + } + else { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + goto CONTINUE; + } + } + if(prec->udf) { + recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM); + goto CONTINUE; + } + if(prec->nsev < INVALID_ALARM + && prec->sevr == INVALID_ALARM + && prec->omsl == menuOmslsupervisory) { + /* reload value field with B0 - B15 */ + int offset = 1, i; + unsigned char *bit = &(prec->b0); + for (i=0; ival |= offset; + else + prec->val &= ~offset; + } + } + /* convert val to rval */ + convert(prec); + } + +CONTINUE: + if (prec->nsev < INVALID_ALARM ) + status=writeValue(prec); /* write the new value */ + else + switch (prec->ivoa) { + case (menuIvoaContinue_normally) : + status=writeValue(prec); /* write the new value */ + break; + case (menuIvoaDon_t_drive_outputs) : + break; + case (menuIvoaSet_output_to_IVOV) : + if (prec->pact == FALSE){ + prec->val=prec->ivov; + convert(prec); + } + status=writeValue(prec); /* write the new value */ + break; + default : + status=-1; + recGblRecordError(S_db_badField,(void *)prec, + "mbboDirect: process Illegal IVOA field"); + } + + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + /* check event list */ + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + prec->pact=FALSE; + return(status); +} + +static long special(DBADDR *paddr, int after) +{ + mbboDirectRecord *prec = (mbboDirectRecord *)(paddr->precord); + int special_type = paddr->special, offset = 1, i; + unsigned char *bit; + + if(!after) return(0); + switch(special_type) { + case(SPC_MOD): + /* + * Set a bit in VAL corresponding to the bit changed + * offset equals the offset in bit array. Only do + * this if in supervisory mode. + */ + if (prec->omsl == menuOmslclosed_loop) + return(0); + + offset = 1 << (((unsigned char *)paddr->pfield) - &(prec->b0)); + + if (*((char *)paddr->pfield)) { + /* set field */ + prec->val |= offset; + } + else { + /* zero field */ + prec->val &= ~offset; + } + prec->udf = FALSE; + + convert(prec); + return(0); + case(SPC_RESET): + /* + * If OMSL changes from closed_loop to supervisory, + * reload value field with B0 - B15 + */ + bit = &(prec->b0); + if (prec->omsl == menuOmslsupervisory) { + for (i=0; ival |= offset; + else + prec->val &= ~offset; + } + } + prec->udf = FALSE; + + return(0); + default: + recGblDbaddrError(S_db_badChoice,paddr,"mbboDirect: special"); + return(S_db_badChoice); + } +} + +static void monitor(mbboDirectRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + + /* check for value change */ + if (prec->mlst != prec->val){ + /* post events for value change and archive change */ + monitor_mask |= (DBE_VALUE | DBE_LOG); + /* update last value monitored */ + prec->mlst = prec->val; + } + /* send out monitors connected to the value field */ + if (monitor_mask){ + db_post_events(prec,&prec->val,monitor_mask); + } + if(prec->oraw!=prec->rval) { + db_post_events(prec,&prec->rval, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->oraw = prec->rval; + } + if(prec->orbv!=prec->rbv) { + db_post_events(prec,&prec->rbv, + monitor_mask|DBE_VALUE|DBE_LOG); + prec->orbv = prec->rbv; + } + return; +} + +static void convert(mbboDirectRecord *prec) +{ + /* convert val to rval */ + prec->rval = (epicsUInt32)(prec->val); + if(prec->shft>0) + prec->rval <<= prec->shft; + + return; +} + +static long writeValue(mbboDirectRecord *prec) +{ + long status; + struct mbbodset *pdset = (struct mbbodset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->write_mbbo)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml), + DBR_ENUM,&(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->write_mbbo)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbPutLink(&prec->siol,DBR_USHORT, + &prec->val,1); + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/mbboDirectRecord.dbd b/src/rec/mbboDirectRecord.dbd new file mode 100644 index 000000000..f572377d0 --- /dev/null +++ b/src/rec/mbboDirectRecord.dbd @@ -0,0 +1,225 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(mbboDirect) { + include "dbCommon.dbd" + field(VAL,DBF_USHORT) { + prompt("Word") + promptgroup(GUI_OUTPUT) + asl(ASL0) + pp(TRUE) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_MBB) + special(SPC_RESET) + pp(TRUE) + interest(1) + menu(menuOmsl) + } + field(NOBT,DBF_SHORT) { + prompt("Number of Bits") + promptgroup(GUI_MBB) + special(SPC_NOMOD) + interest(1) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup(GUI_MBB) + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_MBB) + interest(1) + } + field(B0,DBF_UCHAR) { + prompt("Bit 0") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B1,DBF_UCHAR) { + prompt("Bit 1") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B2,DBF_UCHAR) { + prompt("Bit 2") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B3,DBF_UCHAR) { + prompt("Bit 3") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B4,DBF_UCHAR) { + prompt("Bit 4") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B5,DBF_UCHAR) { + prompt("Bit 5") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B6,DBF_UCHAR) { + prompt("Bit 6") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B7,DBF_UCHAR) { + prompt("Bit 7") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B8,DBF_UCHAR) { + prompt("Bit 8") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(B9,DBF_UCHAR) { + prompt("Bit 9") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(BA,DBF_UCHAR) { + prompt("Bit 10") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(BB,DBF_UCHAR) { + prompt("Bit 11") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(BC,DBF_UCHAR) { + prompt("Bit 12") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(BD,DBF_UCHAR) { + prompt("Bit 13") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(BE,DBF_UCHAR) { + prompt("Bit 14") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(BF,DBF_UCHAR) { + prompt("Bit 15") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + special(SPC_NOMOD) + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("Prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(RBV,DBF_ULONG) { + prompt("Readback Value") + special(SPC_NOMOD) + } + field(ORBV,DBF_ULONG) { + prompt("Prev Readback Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(MLST,DBF_ULONG) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_ULONG) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(SHFT,DBF_ULONG) { + prompt("Shift") + promptgroup(GUI_MBB) + interest(1) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_MBB) + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_MBB) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_MBB) + interest(2) + menu(menuAlarmSevr) + } + field(IVOA,DBF_MENU) { + prompt("INVALID outpt action") + promptgroup(GUI_MBB) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_USHORT) { + prompt("INVALID output value") + promptgroup(GUI_MBB) + interest(2) + } +} diff --git a/src/rec/mbboRecord.c b/src/rec/mbboRecord.c new file mode 100644 index 000000000..2d3bca76f --- /dev/null +++ b/src/rec/mbboRecord.c @@ -0,0 +1,459 @@ +/*************************************************************************\ +* Copyright (c) 2009 Helmholtz-Zentrum Berlin fuer Materialien und Energie. +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recMbbo.c - Record Support Routines for multi bit binary Output records */ +/* + * Original Author: Bob Dalesio + * Date: 7-17-87 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "mbboRecord.h" +#undef GEN_SIZE_OFFSET +#include "menuOmsl.h" +#include "menuIvoa.h" +#include "menuYesNo.h" +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(mbboRecord *, int); +static long process(mbboRecord *); +static long special(DBADDR *, int); +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +static long get_enum_str(DBADDR *, char *); +static long get_enum_strs(DBADDR *, struct dbr_enumStrs *); +static long put_enum_str(DBADDR *, char *); +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset mbboRSET={ + 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,mbboRSET); + +struct mbbodset { /* multi bit binary output dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (0,2)=>(success,success no convert)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_mbbo; /*returns: (0,2)=>(success,success no convert)*/ +}; + + +static void checkAlarms(mbboRecord *); +static void convert(mbboRecord *); +static void monitor(mbboRecord *); +static long writeValue(mbboRecord *); + + +static void init_common(mbboRecord *prec) +{ + epicsUInt32 *pstate_values; + char *pstate_string; + short i; + + /* determine if any states are defined */ + pstate_values = &(prec->zrvl); pstate_string = prec->zrst; + prec->sdef = FALSE; + for (i=0; i<16; i++, pstate_string += sizeof(prec->zrst)) { + if((*(pstate_values+i)!= 0) || (*pstate_string !='\0')) { + prec->sdef = TRUE; + return; + } + } + return; +} + +static long init_record(mbboRecord *prec, int pass) +{ + struct mbbodset *pdset; + long status; + int i; + + if (pass==0) { + init_common(prec); + return(0); + } + + /* mbbo.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + if(!(pdset = (struct mbbodset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"mbbo: init_record"); + return(S_dev_noDSET); + } + /* must have write_mbbo function defined */ + if( (pdset->number < 5) || (pdset->write_mbbo == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"mbbo: init_record"); + return(S_dev_missingSup); + } + if (prec->dol.type == CONSTANT){ + if(recGblInitConstantLink(&prec->dol,DBF_USHORT,&prec->val)) + prec->udf = FALSE; + } + + /* initialize mask*/ + prec->mask = 0; + for (i=0; inobt; i++) { + prec->mask <<= 1; /* shift left 1 bit*/ + prec->mask |= 1; /* set low order bit*/ + } + if( pdset->init_record ) { + epicsUInt32 rval; + + status=(*pdset->init_record)(prec); + /* init_record might set status */ + init_common(prec); + if(status==0){ + rval = prec->rval; + if(prec->shft>0) rval >>= prec->shft; + if (prec->sdef){ + epicsUInt32 *pstate_values; + short i; + + pstate_values = &(prec->zrvl); + prec->val = 65535; /* initalize to unknown state*/ + for (i = 0; i < 16; i++){ + if (*pstate_values == rval){ + prec->val = i; + break; + } + pstate_values++; + } + }else{ + /* the raw is the desired val */ + prec->val = (unsigned short)rval; + } + prec->udf = FALSE; + } else if (status==2) status=0; + } + init_common(prec); + /* convert val to rval */ + convert(prec); + prec->mlst = prec->val; + prec->lalm = prec->val; + prec->oraw = prec->rval; + prec->orbv = prec->rbv; + return(0); +} + +static long process(mbboRecord *prec) +{ + struct mbbodset *pdset = (struct mbbodset *)(prec->dset); + long status=0; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->write_mbbo==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"write_mbbo"); + return(S_dev_missingSup); + } + + if (!prec->pact) { + if (prec->dol.type != CONSTANT && prec->omsl == menuOmslclosed_loop) { + long status; + unsigned short val; + + status = dbGetLink(&prec->dol,DBR_USHORT, &val,0,0); + if(status==0) { + prec->val= val; + prec->udf= FALSE; + } else { + recGblSetSevr(prec,LINK_ALARM,INVALID_ALARM); + goto CONTINUE; + } + } + if(prec->udf==TRUE) { + recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM); + goto CONTINUE; + } + /* convert val to rval */ + convert(prec); + } + +CONTINUE: + /* check for alarms */ + checkAlarms(prec); + + if (prec->nsev < INVALID_ALARM ) + status=writeValue(prec); /* write the new value */ + else { + switch (prec->ivoa) { + case (menuIvoaContinue_normally) : + status=writeValue(prec); /* write the new value */ + break; + case (menuIvoaDon_t_drive_outputs) : + break; + case (menuIvoaSet_output_to_IVOV) : + if (prec->pact == FALSE){ + prec->val=prec->ivov; + convert(prec); + } + status=writeValue(prec); /* write the new value */ + break; + default : + status=-1; + recGblRecordError(S_db_badField,(void *)prec, + "mbbo:process Illegal IVOA field"); + } + } + + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + /* check event list */ + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + prec->pact=FALSE; + return(status); +} + +static long special(DBADDR *paddr, int after) +{ + mbboRecord *prec = (mbboRecord *)(paddr->precord); + int special_type = paddr->special; + int fieldIndex = dbGetFieldIndex(paddr); + + if(!after) return(0); + switch(special_type) { + case(SPC_MOD): + init_common(prec); + if (fieldIndex >= mbboRecordZRST && fieldIndex <= mbboRecordFFST) + db_post_events(prec,&prec->val,DBE_PROPERTY); + return(0); + default: + recGblDbaddrError(S_db_badChoice,paddr,"mbbo: special"); + return(S_db_badChoice); + } +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + mbboRecord *prec=(mbboRecord *)paddr->precord; + int index; + + index = dbGetFieldIndex(paddr); + if(index!=mbboRecordVAL) { + recGblDbaddrError(S_db_badField,paddr,"mbbo: cvt_dbaddr"); + return(0); + } + if(!prec->sdef) { + paddr->field_type = DBF_USHORT; + paddr->dbr_field_type = DBF_USHORT; + } + return(0); +} + +static long get_enum_str(DBADDR *paddr, char *pstring) +{ + mbboRecord *prec=(mbboRecord *)paddr->precord; + char *psource; + int index; + unsigned short *pfield = (unsigned short *)paddr->pfield; + unsigned short val=*pfield; + + index = dbGetFieldIndex(paddr); + if(index!=mbboRecordVAL) { + strcpy(pstring,"Illegal_Value"); + } else if(val<= 15) { + psource = (prec->zrst); + psource += (val * sizeof(prec->zrst)); + strncpy(pstring,psource,sizeof(prec->zrst)); + } else { + strcpy(pstring,"Illegal Value"); + } + return(0); +} + +static long get_enum_strs(DBADDR *paddr, struct dbr_enumStrs *pes) +{ + mbboRecord *prec=(mbboRecord *)paddr->precord; + char *psource; + int i; + short no_str; + + no_str=0; + memset(pes->strs,'\0',sizeof(pes->strs)); + for(i=0,psource=(prec->zrst); i<16; i++, psource += sizeof(prec->zrst) ) { + strncpy(pes->strs[i],psource,sizeof(prec->zrst)); + if(*psource!=0)no_str=i+1; + } + pes->no_str = no_str; + + return(0); +} +static long put_enum_str(DBADDR *paddr,char *pstring) +{ + mbboRecord *prec=(mbboRecord *)paddr->precord; + char *pstate_name; + short i; + + if (prec->sdef){ + pstate_name = prec->zrst; + for (i = 0; i < 16; i++){ + if(strncmp(pstate_name,pstring,sizeof(prec->zrst))==0){ + prec->val = i; + return(0); + } + pstate_name += sizeof(prec->zrst); + } + } + return(S_db_badChoice); +} + +static void checkAlarms(mbboRecord *prec) +{ + unsigned short *severities; + unsigned short val=prec->val; + + + /* check for state alarm */ + /* unknown state */ + if (val > 15){ + recGblSetSevr(prec,STATE_ALARM,prec->unsv); + } else { + /* in a state which is an error */ + severities = (unsigned short *)&(prec->zrsv); + recGblSetSevr(prec,STATE_ALARM,severities[prec->val]); + } + + /* check for cos alarm */ + if(val == prec->lalm) return; + if(recGblSetSevr(prec,COS_ALARM,prec->cosv)) return; + prec->lalm = val; + return; +} + +static void monitor(mbboRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + /* check for value change */ + if (prec->mlst != prec->val){ + /* post events for value change and archive change */ + monitor_mask |= (DBE_VALUE | DBE_LOG); + /* update last value monitored */ + prec->mlst = prec->val; + } + /* send out monitors connected to the value field */ + if (monitor_mask){ + db_post_events(prec,&prec->val,monitor_mask); + } + if(prec->oraw!=prec->rval) { + db_post_events(prec,&prec->rval,monitor_mask|DBE_VALUE); + prec->oraw = prec->rval; + } + if(prec->orbv!=prec->rbv) { + db_post_events(prec,&prec->rbv,monitor_mask|DBE_VALUE); + prec->orbv = prec->rbv; + } + return; +} + +static void convert(mbboRecord *prec) +{ + epicsUInt32 *pvalues = &(prec->zrvl); + + /* convert val to rval */ + if(prec->sdef) { + + if(prec->val>15) { + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return; + } + prec->rval = pvalues[prec->val]; + } else prec->rval = (epicsUInt32)(prec->val); + if(prec->shft>0) prec->rval <<= prec->shft; + + return; +} + + + +static long writeValue(mbboRecord *prec) +{ + long status; + struct mbbodset *pdset = (struct mbbodset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->write_mbbo)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->write_mbbo)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbPutLink(&prec->siol,DBR_USHORT, &prec->val,1); + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/mbboRecord.dbd b/src/rec/mbboRecord.dbd new file mode 100644 index 000000000..4f748a7fd --- /dev/null +++ b/src/rec/mbboRecord.dbd @@ -0,0 +1,498 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(mbbo) { + include "dbCommon.dbd" + field(VAL,DBF_ENUM) { + prompt("Desired Value") + promptgroup(GUI_OUTPUT) + special(SPC_DBADDR) + asl(ASL0) + pp(TRUE) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup(GUI_MBB) + interest(1) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_MBB) + interest(1) + menu(menuOmsl) + } + field(NOBT,DBF_SHORT) { + prompt("Number of Bits") + promptgroup(GUI_MBB) + special(SPC_NOMOD) + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_MBB) + interest(1) + } + field(ZRVL,DBF_ULONG) { + prompt("Zero Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(ONVL,DBF_ULONG) { + prompt("One Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TWVL,DBF_ULONG) { + prompt("Two Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(THVL,DBF_ULONG) { + prompt("Three Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FRVL,DBF_ULONG) { + prompt("Four Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FVVL,DBF_ULONG) { + prompt("Five Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(SXVL,DBF_ULONG) { + prompt("Six Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(SVVL,DBF_ULONG) { + prompt("Seven Value") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(EIVL,DBF_ULONG) { + prompt("Eight Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(NIVL,DBF_ULONG) { + prompt("Nine Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TEVL,DBF_ULONG) { + prompt("Ten Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(ELVL,DBF_ULONG) { + prompt("Eleven Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TVVL,DBF_ULONG) { + prompt("Twelve Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(TTVL,DBF_ULONG) { + prompt("Thirteen Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FTVL,DBF_ULONG) { + prompt("Fourteen Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(FFVL,DBF_ULONG) { + prompt("Fifteen Value") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + base(HEX) + interest(1) + } + field(ZRST,DBF_STRING) { + prompt("Zero String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(ONST,DBF_STRING) { + prompt("One String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TWST,DBF_STRING) { + prompt("Two String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(THST,DBF_STRING) { + prompt("Three String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FRST,DBF_STRING) { + prompt("Four String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FVST,DBF_STRING) { + prompt("Five String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(SXST,DBF_STRING) { + prompt("Six String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(SVST,DBF_STRING) { + prompt("Seven String") + promptgroup(GUI_BITS1) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(EIST,DBF_STRING) { + prompt("Eight String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(NIST,DBF_STRING) { + prompt("Nine String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TEST,DBF_STRING) { + prompt("Ten String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(ELST,DBF_STRING) { + prompt("Eleven String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TVST,DBF_STRING) { + prompt("Twelve String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(TTST,DBF_STRING) { + prompt("Thirteen String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FTST,DBF_STRING) { + prompt("Fourteen String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(FFST,DBF_STRING) { + prompt("Fifteen String") + promptgroup(GUI_BITS2) + special(SPC_MOD) + pp(TRUE) + interest(1) + size(26) + } + field(ZRSV,DBF_MENU) { + prompt("State Zero Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(ONSV,DBF_MENU) { + prompt("State One Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TWSV,DBF_MENU) { + prompt("State Two Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(THSV,DBF_MENU) { + prompt("State Three Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FRSV,DBF_MENU) { + prompt("State Four Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FVSV,DBF_MENU) { + prompt("State Five Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(SXSV,DBF_MENU) { + prompt("State Six Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(SVSV,DBF_MENU) { + prompt("State Seven Severity") + promptgroup(GUI_BITS1) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(EISV,DBF_MENU) { + prompt("State Eight Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(NISV,DBF_MENU) { + prompt("State Nine Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TESV,DBF_MENU) { + prompt("State Ten Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(ELSV,DBF_MENU) { + prompt("State Eleven Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TVSV,DBF_MENU) { + prompt("State Twelve Severity") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(TTSV,DBF_MENU) { + prompt("State Thirteen Sevr") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FTSV,DBF_MENU) { + prompt("State Fourteen Sevr") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(FFSV,DBF_MENU) { + prompt("State Fifteen Sevr") + promptgroup(GUI_BITS2) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(UNSV,DBF_MENU) { + prompt("Unknown State Sevr") + promptgroup(GUI_MBB) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Sevr") + promptgroup(GUI_MBB) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("Prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(RBV,DBF_ULONG) { + prompt("Readback Value") + special(SPC_NOMOD) + } + field(ORBV,DBF_ULONG) { + prompt("Prev Readback Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(SDEF,DBF_SHORT) { + prompt("States Defined") + special(SPC_NOMOD) + interest(3) + } + field(SHFT,DBF_USHORT) { + prompt("Shift") + promptgroup(GUI_MBB) + interest(1) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_MBB) + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_MBB) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_MBB) + interest(2) + menu(menuAlarmSevr) + } + field(IVOA,DBF_MENU) { + prompt("INVALID outpt action") + promptgroup(GUI_MBB) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_USHORT) { + prompt("INVALID output value") + promptgroup(GUI_MBB) + interest(2) + } +} diff --git a/src/rec/permissiveRecord.c b/src/rec/permissiveRecord.c new file mode 100644 index 000000000..ca390b3a0 --- /dev/null +++ b/src/rec/permissiveRecord.c @@ -0,0 +1,115 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recPermissive.c - Record Support Routines for Permissive records */ +/* + * Original Author: Bob Dalesio + * Date: 10-10-90 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#define GEN_SIZE_OFFSET +#include "permissiveRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +#define init_record NULL +static long process(permissiveRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset permissiveRSET={ + 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,permissiveRSET); + +static void monitor(permissiveRecord *); + +static long process(permissiveRecord *prec) +{ + + prec->pact=TRUE; + prec->udf=FALSE; + recGblGetTimeStamp(prec); + monitor(prec); + recGblFwdLink(prec); + prec->pact=FALSE; + return(0); +} + +static void monitor(permissiveRecord *prec) +{ + unsigned short monitor_mask; + unsigned short val,oval,wflg,oflg; + + monitor_mask = recGblResetAlarms(prec); + /* get val,oval,wflg,oflg*/ + val=prec->val; + oval=prec->oval; + wflg=prec->wflg; + oflg=prec->oflg; + /*set oval and oflg*/ + prec->oval = val; + prec->oflg = wflg; + if(oval != val) { + db_post_events(prec,&prec->val, + monitor_mask|DBE_VALUE|DBE_LOG); + } + if(oflg != wflg) { + db_post_events(prec,&prec->wflg, + monitor_mask|DBE_VALUE|DBE_LOG); + } + return; +} diff --git a/src/rec/permissiveRecord.dbd b/src/rec/permissiveRecord.dbd new file mode 100644 index 000000000..6e999175f --- /dev/null +++ b/src/rec/permissiveRecord.dbd @@ -0,0 +1,38 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(permissive) { + include "dbCommon.dbd" + field(LABL,DBF_STRING) { + prompt("Button Label") + promptgroup(GUI_DISPLAY) + pp(TRUE) + interest(1) + size(20) + } + field(VAL,DBF_USHORT) { + prompt("Status") + asl(ASL0) + pp(TRUE) + } + field(OVAL,DBF_USHORT) { + prompt("Old Status") + special(SPC_NOMOD) + interest(3) + } + field(WFLG,DBF_USHORT) { + prompt("Wait Flag") + pp(TRUE) + } + field(OFLG,DBF_USHORT) { + prompt("Old Flag") + special(SPC_NOMOD) + interest(3) + } +} diff --git a/src/rec/recIoc.rc b/src/rec/recIoc.rc new file mode 100755 index 000000000..7dad57625 --- /dev/null +++ b/src/rec/recIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Record Support Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Record Support Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "rec\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "rec.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/rec/selRecord.c b/src/rec/selRecord.c new file mode 100644 index 000000000..919438962 --- /dev/null +++ b/src/rec/selRecord.c @@ -0,0 +1,441 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* selRecord.c - Record Support Routines for Select records */ +/* + * Original Author: Bob Dalesio + * Date: 6-2-89 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsMath.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#define GEN_SIZE_OFFSET +#include "selRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(selRecord *, int); +static long process(selRecord *); +#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(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); + +rset selRSET={ + 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,selRSET); + +#define SEL_MAX 12 + +static void checkAlarms(selRecord *); +static void do_sel(selRecord *); +static int fetch_values(selRecord *); +static void monitor(selRecord *); + + +static long init_record(selRecord *prec, int pass) +{ + struct link *plink; + int i; + double *pvalue; + + if (pass==0) return(0); + + /* get seln initial value if nvl is a constant*/ + if (prec->nvl.type == CONSTANT ) { + recGblInitConstantLink(&prec->nvl,DBF_USHORT,&prec->seln); + } + + plink = &prec->inpa; + pvalue = &prec->a; + for(i=0; itype==CONSTANT) { + recGblInitConstantLink(plink,DBF_DOUBLE,pvalue); + } + } + return(0); +} + +static long process(selRecord *prec) +{ + prec->pact = TRUE; + if ( RTN_SUCCESS(fetch_values(prec)) ) { + do_sel(prec); + } + + recGblGetTimeStamp(prec); + /* check for alarms */ + checkAlarms(prec); + + + /* check event list */ + monitor(prec); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return(0); +} + + +static long get_units(DBADDR *paddr, char *units) +{ + selRecord *prec=(selRecord *)paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + return(0); +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + selRecord *prec=(selRecord *)paddr->precord; + double *pvalue,*plvalue; + int i; + + *precision = prec->prec; + if(paddr->pfield==(void *)&prec->val){ + return(0); + } + pvalue = &prec->a; + plvalue = &prec->la; + for(i=0; ipfield==(void *)&pvalue + || paddr->pfield==(void *)&plvalue){ + return(0); + } + } + recGblGetPrec(paddr,precision); + return(0); +} + + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + selRecord *prec=(selRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val + || paddr->pfield==(void *)&prec->hihi + || paddr->pfield==(void *)&prec->high + || paddr->pfield==(void *)&prec->low + || paddr->pfield==(void *)&prec->lolo){ + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return(0); + } + + if(paddr->pfield>=(void *)&prec->a && paddr->pfield<=(void *)&prec->l){ + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return(0); + } + if(paddr->pfield>=(void *)&prec->la && paddr->pfield<=(void *)&prec->ll){ + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + return(0); + } + recGblGetGraphicDouble(paddr,pgd); + return(0); +} + +static long get_control_double(struct dbAddr *paddr, struct dbr_ctrlDouble *pcd) +{ + selRecord *prec=(selRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val + || paddr->pfield==(void *)&prec->hihi + || paddr->pfield==(void *)&prec->high + || paddr->pfield==(void *)&prec->low + || paddr->pfield==(void *)&prec->lolo){ + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return(0); + } + + if(paddr->pfield>=(void *)&prec->a && paddr->pfield<=(void *)&prec->l){ + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return(0); + } + if(paddr->pfield>=(void *)&prec->la && paddr->pfield<=(void *)&prec->ll){ + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + return(0); + } + recGblGetControlDouble(paddr,pcd); + return(0); +} + +static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) +{ + selRecord *prec=(selRecord *)paddr->precord; + + if(paddr->pfield==(void *)&prec->val ){ + 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(selRecord *prec) +{ + double val, hyst, lalm; + double alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +static void monitor(selRecord *prec) +{ + unsigned short monitor_mask; + double delta; + double *pnew; + double *pprev; + int i; + + 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); + + monitor_mask |= DBE_VALUE|DBE_LOG; + + /* trigger monitors of the SELN field */ + if (prec->nlst != prec->seln) { + prec->nlst = prec->seln; + db_post_events(prec, &prec->seln, monitor_mask); + } + + /* check all input fields for changes, even if VAL hasn't changed */ + for(i=0, pnew=&prec->a, pprev=&prec->la; ia; + switch (prec->selm){ + case (selSELM_Specified): + if (prec->seln >= SEL_MAX) { + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return; + } + val = *(pvalue+prec->seln); + break; + case (selSELM_High_Signal): + val = -epicsINF; + for (i = 0; i < SEL_MAX; i++,pvalue++){ + if (!isnan(*pvalue) && val < *pvalue) { + val = *pvalue; + prec->seln = i; + } + } + break; + case (selSELM_Low_Signal): + val = epicsINF; + for (i = 0; i < SEL_MAX; i++,pvalue++){ + if (!isnan(*pvalue) && val > *pvalue) { + val = *pvalue; + prec->seln = i; + } + } + break; + case (selSELM_Median_Signal): + count = 0; + order[0] = epicsNAN; + for (i = 0; i < SEL_MAX; i++,pvalue++){ + if (!isnan(*pvalue)){ + /* Insertion sort */ + j = count; + while ((j > 0) && (order[j-1] > *pvalue)){ + order[j] = order[j-1]; + j--; + } + order[j] = *pvalue; + count++; + } + } + prec->seln = count; + val = order[count / 2]; + break; + default: + recGblSetSevr(prec,CALC_ALARM,INVALID_ALARM); + return; + } + if (!isinf(val)){ + prec->val=val; + prec->udf=isnan(prec->val); + } else { + recGblSetSevr(prec,UDF_ALARM,MAJOR_ALARM); + /* If UDF is TRUE this alarm will be overwritten by checkAlarms*/ + } + return; +} + +/* + * FETCH_VALUES + * + * fetch the values for the variables from which to select + */ +static int fetch_values(selRecord *prec) +{ + struct link *plink; + double *pvalue; + int i; + long status; + + plink = &prec->inpa; + pvalue = &prec->a; + /* If mechanism is selSELM_Specified, only get the selected input*/ + if(prec->selm == selSELM_Specified) { + /* fetch the select index */ + status=dbGetLink(&(prec->nvl),DBR_USHORT,&(prec->seln),0,0); + if (!RTN_SUCCESS(status) || (prec->seln >= SEL_MAX)) + return(status); + + plink += prec->seln; + pvalue += prec->seln; + + status=dbGetLink(plink,DBR_DOUBLE, pvalue,0,0); + return(status); + } + /* fetch all inputs*/ + for(i=0; i +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#include "callback.h" + +#define GEN_SIZE_OFFSET +#include "seqRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +int seqRecDebug = 0; + +/* Create RSET - Record Support Entry Table*/ +static long init_record(seqRecord *prec, int pass); +static long process(seqRecord *prec); +static int processNextLink(seqRecord *prec); +static long asyncFinish(seqRecord *prec); +static void processCallback(CALLBACK *arg); +static long get_precision(dbAddr *paddr, long *pprecision); + +rset seqRSET={ + RSETNUMBER, + NULL, /* report */ + NULL, /* initialize */ + init_record, /* init_record */ + process, /* process */ + NULL, /* special */ + NULL, /* get_value */ + NULL, /* cvt_dbaddr */ + NULL, /* get_array_info */ + NULL, /* put_array_info */ + NULL, /* get_units */ + get_precision, /* get_precision */ + NULL, /* get_enum_str */ + NULL, /* get_enum_strs */ + NULL, /* put_enum_str */ + NULL, /* get_graphic_double */ + NULL, /* get_control_double */ + NULL /* get_alarm_double */ + +}; +epicsExportAddress(rset,seqRSET); + +/* Total number of link-groups in a sequence record */ +#define NUM_LINKS 10 +#define SELN_BIT_MASK ~(0xffff << NUM_LINKS) + +/* This is what a link-group looks like in a sequence record */ +typedef struct linkDesc { + double dly; /* Delay value (in seconds) */ + struct link dol; /* Where to fetch the input value from */ + double dov; /* If dol is CONSTANT, this is the CONSTANT value */ + struct link lnk; /* Where to put the value from dol */ +}linkDesc; + +/* Callback structure used by the watchdog function to queue link processing */ +typedef struct callbackSeq { + CALLBACK callback; /* used for the callback task */ + seqRecord *pseqRecord; + linkDesc *plinks[NUM_LINKS+1]; /* Pointers to links to process */ + int index; +}callbackSeq; + +int processNextLink(); + + +/***************************************************************************** + * + * Initialize a sequence record. + * + * Allocate the callback request structure (tacked on dpvt.) + * Initialize watch-dog ID + * Initialize SELN based on the link-type of SELL + * If SELL is a CA_LINK, inform CA about it + * For each constant input link, fill in the DOV field + * + ******************************************************************************/ +static long init_record(seqRecord *prec, int pass) +{ + int index; + linkDesc *plink; + callbackSeq *pcallbackSeq; + + if (pass==0) return(0); + + if (seqRecDebug > 5) + printf("init_record(%s) entered\n", prec->name); + + /* Allocate a callback structure for use in processing */ + pcallbackSeq = (callbackSeq *)calloc(1,sizeof(callbackSeq)); + pcallbackSeq->pseqRecord = prec; + callbackSetCallback(processCallback,&pcallbackSeq->callback); + callbackSetUser(pcallbackSeq,&pcallbackSeq->callback); + callbackSetPriority(prec->prio,&pcallbackSeq->callback); + prec->dpvt = (void *)pcallbackSeq; + + /* Get link selection if sell is a constant and nonzero */ + if (prec->sell.type==CONSTANT) + { + if (seqRecDebug > 5) + printf("init_record(%s) SELL is a constant\n", prec->name); + recGblInitConstantLink(&prec->sell,DBF_USHORT,&prec->seln); + } + + /* Copy over ALL the input link constants here */ + plink = (linkDesc *)(&(prec->dly1)); + + index = 0; + while (index < NUM_LINKS) + { + + if (plink->dol.type == CONSTANT) + recGblInitConstantLink(&plink->dol,DBF_DOUBLE,&plink->dov); + + index++; + + plink++; + } + + return(0); +} + +/***************************************************************************** + * + * Process a sequence record. + * + * If is async completion phase + * call asyncFinish() to finish things up + * else + * figure out selection mechanism + * build the correct mask value using the mode and the selection value + * build a list of pointers to the selected link-group structures + * If there are no links to process + * call asyncFinish() to finish things up + * else + * call processNextLink() to schecule a delay for the first link-group + * + * + * NOTE: + * dbScanLock is already held for prec before this function is called. + * + * We do NOT call processNextLink() if there is nothing to do, this prevents + * us from calling dbProcess() recursively. + * + ******************************************************************************/ +static long process(seqRecord *prec) +{ + callbackSeq *pcb = (callbackSeq *) (prec->dpvt); + linkDesc *plink; + unsigned short lmask; + int tmp; + + if(seqRecDebug > 10) + printf("seqRecord: process(%s) pact = %d\n", prec->name, prec->pact); + + if (prec->pact) + { /* In async completion phase */ + asyncFinish(prec); + return(0); + } + prec->pact = TRUE; + + /* Reset the PRIO in case it was changed */ + callbackSetPriority(prec->prio,&pcb->callback); + + /* + * We should not bother supporting seqSELM_All or seqSELM_Specified + * they can both be supported by simply providing seqSELM_Mask. + * build the proper mask using these other silly modes if necessary. + */ + + if (prec->selm == seqSELM_All) + { + lmask = (unsigned short) SELN_BIT_MASK; + } + else + { + /* Fill in the SELN field */ + if (prec->sell.type != CONSTANT) + { + dbGetLink(&(prec->sell), DBR_USHORT, &(prec->seln), 0,0); + } + if (prec->selm == seqSELM_Specified) + { + if(prec->seln>10) + { /* Invalid selection number */ + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(asyncFinish(prec)); + } + if (prec->seln == 0) + return(asyncFinish(prec)); /* Nothing selected */ + + lmask = 1; + lmask <<= prec->seln - 1; + } + else if (prec->selm == seqSELM_Mask) + { + lmask = (prec->seln) & SELN_BIT_MASK; + } + else + { /* Invalid selection option */ + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(asyncFinish(prec)); + } + } + /* Figure out which links are going to be processed */ + pcb->index = 0; + plink = (linkDesc *)(&(prec->dly1)); + tmp = 1; + while (lmask) + { + if (seqRecDebug > 4) + printf("seqRec-process Checking link %d - lnk %d dol %d\n", tmp, + plink->lnk.type, plink->dol.type); + + if ((lmask & 1) && ((plink->lnk.type != CONSTANT)||(plink->dol.type != CONSTANT))) + { + if (seqRecDebug > 4) + printf(" seqRec-process Adding link %d at index %d\n", tmp, pcb->index); + + pcb->plinks[pcb->index] = plink; + pcb->index++; + } + lmask >>= 1; + plink++; + tmp++; + } + pcb->plinks[pcb->index] = NULL; /* mark the bottom of the list */ + + if (!pcb->index) + { /* There was nothing to do, finish record processing here */ + return(asyncFinish(prec)); + } + + pcb->index = 0; + /* Start doing the first forward link (We have at least one for sure) */ + processNextLink(prec); + + return(0); +} + +/***************************************************************************** + * + * Find the next link-group that needs processing. + * + * If there are no link-groups left to process + * call bdProcess() to complete the async record processing. + * else + * if the delay is > 0 seconds + * schedule the watch dog task to wake us up later + * else + * invoke the watch-dog wakeup routine now + * + * + * NOTE: + * dbScanLock is already held for prec before this function is called. + * + ******************************************************************************/ +static int processNextLink(seqRecord *prec) +{ + callbackSeq *pcb = (callbackSeq *) (prec->dpvt); + + if (seqRecDebug > 5) + printf("processNextLink(%s) looking for work to do, index = %d\n", prec->name, pcb->index); + + if (pcb->plinks[pcb->index] == NULL) + { + /* None left, finish up. */ + (*(struct rset *)(prec->rset)).process(prec); + } + else + { + if (pcb->plinks[pcb->index]->dly > 0.0) + { + callbackRequestDelayed( &pcb->callback,pcb->plinks[pcb->index]->dly); + } + else + { + /* No delay, do it now. Avoid recursion by using the callback task */ + callbackRequest(&pcb->callback); + } + } + return(0); +} + +/***************************************************************************** + * + * Finish record processing by posting any events and processing forward links. + * + * NOTE: + * dbScanLock is already held for prec before this function is called. + * + ******************************************************************************/ +static long asyncFinish(seqRecord *prec) +{ + unsigned short MonitorMask; + + if (seqRecDebug > 5) + printf("asyncFinish(%s) completing processing\n", prec->name); + prec->udf = FALSE; + + MonitorMask = recGblResetAlarms(prec); + + if (MonitorMask) + db_post_events(prec, &prec->val, MonitorMask); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + recGblGetTimeStamp(prec); + /* tsLocalTime(&prec->time); */ + prec->pact = FALSE; + + return(0); +} + +/***************************************************************************** + * + * Link-group processing function. + * + * if the input link is not a constant + * call dbGetLink() to get the link value + * else + * get the value from the DOV field + * call dbPutLink() to forward the value to destination location + * call processNextLink() to schedule the processing of the next link-group + * + * NOTE: + * dbScanLock is NOT held for prec when this function is called!! + * + ******************************************************************************/ +static void processCallback(CALLBACK *arg) +{ + callbackSeq *pcb; + seqRecord *prec; + double myDouble; + + callbackGetUser(pcb,arg); + prec = pcb->pseqRecord; + dbScanLock((struct dbCommon *)prec); + + if (seqRecDebug > 5) + printf("processCallback(%s) processing field index %d\n", prec->name, pcb->index+1); + + /* Save the old value */ + myDouble = pcb->plinks[pcb->index]->dov; + + dbGetLink(&(pcb->plinks[pcb->index]->dol), DBR_DOUBLE, + &(pcb->plinks[pcb->index]->dov),0,0); + + /* Dump the value to the destination field */ + dbPutLink(&(pcb->plinks[pcb->index]->lnk), DBR_DOUBLE, + &(pcb->plinks[pcb->index]->dov),1); + + if (myDouble != pcb->plinks[pcb->index]->dov) + { + if (seqRecDebug > 0) + printf("link %d changed from %f to %f\n", pcb->index, myDouble, pcb->plinks[pcb->index]->dov); + db_post_events(prec, &pcb->plinks[pcb->index]->dov, DBE_VALUE|DBE_LOG); + } + else + { + if (seqRecDebug > 0) + printf("link %d not changed... %f\n", pcb->index, pcb->plinks[pcb->index]->dov); + } + + /* Find the 'next' link-seq that is ready for processing. */ + pcb->index++; + processNextLink(prec); + + dbScanUnlock((struct dbCommon *)prec); + return; +} + +/***************************************************************************** + * + * Return the precision value from PREC + * + *****************************************************************************/ +static long get_precision(dbAddr *paddr, long *pprecision) +{ + seqRecord *prec = (seqRecord *) paddr->precord; + + *pprecision = prec->prec; + + if(paddr->pfield < (void *)&prec->val) + return 0; /* Field is NOT in dbCommon */ + + recGblGetPrec(paddr, pprecision); /* Field is in dbCommon */ + return 0; +} diff --git a/src/rec/seqRecord.dbd b/src/rec/seqRecord.dbd new file mode 100644 index 000000000..518310220 --- /dev/null +++ b/src/rec/seqRecord.dbd @@ -0,0 +1,233 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(seqSELM) { + choice(seqSELM_All,"All") + choice(seqSELM_Specified,"Specified") + choice(seqSELM_Mask,"Mask") +} +recordtype(seq) { + include "dbCommon.dbd" + field(VAL,DBF_LONG) { + prompt("Used to trigger") + asl(ASL0) + pp(TRUE) + } + field(SELM,DBF_MENU) { + prompt("Select Mechanism") + promptgroup(GUI_INPUTS) + interest(1) + menu(seqSELM) + } + field(SELN,DBF_USHORT) { + prompt("Link Selection") + interest(1) + initial("1") + } + field(SELL,DBF_INLINK) { + prompt("Link Selection Loc") + promptgroup(GUI_INPUTS) + interest(1) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(DLY1,DBF_DOUBLE) { + prompt("Delay 1") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DOL1,DBF_INLINK) { + prompt("Input link1") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DO1,DBF_DOUBLE) { + prompt("Constant input 1") + interest(1) + } + field(LNK1,DBF_OUTLINK) { + prompt("Output Link 1") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DLY2,DBF_DOUBLE) { + prompt("Delay 2") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DOL2,DBF_INLINK) { + prompt("Input link 2") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DO2,DBF_DOUBLE) { + prompt("Constant input 2") + interest(1) + } + field(LNK2,DBF_OUTLINK) { + prompt("Output Link 2") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DLY3,DBF_DOUBLE) { + prompt("Delay 3") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DOL3,DBF_INLINK) { + prompt("Input link 3") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DO3,DBF_DOUBLE) { + prompt("Constant input 3") + interest(1) + } + field(LNK3,DBF_OUTLINK) { + prompt("Output Link 3") + promptgroup(GUI_SEQ1) + interest(1) + } + field(DLY4,DBF_DOUBLE) { + prompt("Delay 4") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DOL4,DBF_INLINK) { + prompt("Input link 4") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DO4,DBF_DOUBLE) { + prompt("Constant input 4") + interest(1) + } + field(LNK4,DBF_OUTLINK) { + prompt("Output Link 4") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DLY5,DBF_DOUBLE) { + prompt("Delay 5") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DOL5,DBF_INLINK) { + prompt("Input link 5") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DO5,DBF_DOUBLE) { + prompt("Constant input 5") + interest(1) + } + field(LNK5,DBF_OUTLINK) { + prompt("Output Link 5") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DLY6,DBF_DOUBLE) { + prompt("Delay 6") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DOL6,DBF_INLINK) { + prompt("Input link 6") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DO6,DBF_DOUBLE) { + prompt("Constant input 6") + interest(1) + } + field(LNK6,DBF_OUTLINK) { + prompt("Output Link 6") + promptgroup(GUI_SEQ2) + interest(1) + } + field(DLY7,DBF_DOUBLE) { + prompt("Delay 7") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DOL7,DBF_INLINK) { + prompt("Input link 7") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DO7,DBF_DOUBLE) { + prompt("Constant input 7") + interest(1) + } + field(LNK7,DBF_OUTLINK) { + prompt("Output Link 7") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DLY8,DBF_DOUBLE) { + prompt("Delay 8") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DOL8,DBF_INLINK) { + prompt("Input link 8") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DO8,DBF_DOUBLE) { + prompt("Constant input 8") + interest(1) + } + field(LNK8,DBF_OUTLINK) { + prompt("Output Link 8") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DLY9,DBF_DOUBLE) { + prompt("Delay 9") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DOL9,DBF_INLINK) { + prompt("Input link 9") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DO9,DBF_DOUBLE) { + prompt("Constant input 9") + interest(1) + } + field(LNK9,DBF_OUTLINK) { + prompt("Output Link 9") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DLYA,DBF_DOUBLE) { + prompt("Delay 10") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DOLA,DBF_INLINK) { + prompt("Input link 10") + promptgroup(GUI_SEQ3) + interest(1) + } + field(DOA,DBF_DOUBLE) { + prompt("Constant input 10") + interest(1) + } + field(LNKA,DBF_OUTLINK) { + prompt("Output Link 10") + promptgroup(GUI_SEQ3) + interest(1) + } +} diff --git a/src/rec/stateRecord.c b/src/rec/stateRecord.c new file mode 100644 index 000000000..05c3a0d96 --- /dev/null +++ b/src/rec/stateRecord.c @@ -0,0 +1,105 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recState.c - Record Support Routines for State records */ +/* + * Original Author: Bob Dalesio + * Date: 10-10-90 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#define GEN_SIZE_OFFSET +#include "stateRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +#define init_record NULL +static long process(stateRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset stateRSET={ + 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,stateRSET); + +static void monitor(stateRecord *); + +static long process(stateRecord *prec) +{ + + prec->udf = FALSE; + prec->pact=TRUE; + recGblGetTimeStamp(prec); + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + prec->pact=FALSE; + return(0); +} + +static void monitor(stateRecord *prec) +{ + unsigned short monitor_mask; + + /* get previous stat and sevr and new stat and sevr*/ + monitor_mask = recGblResetAlarms(prec); + if(strncmp(prec->oval,prec->val,sizeof(prec->val))) { + db_post_events(prec,&(prec->val[0]),monitor_mask|DBE_VALUE|DBE_LOG); + strncpy(prec->oval,prec->val,sizeof(prec->val)); + } + return; +} diff --git a/src/rec/stateRecord.dbd b/src/rec/stateRecord.dbd new file mode 100644 index 000000000..c0a253e1c --- /dev/null +++ b/src/rec/stateRecord.dbd @@ -0,0 +1,25 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(state) { + include "dbCommon.dbd" + field(VAL,DBF_STRING) { + prompt("Value") + promptgroup(GUI_DISPLAY) + asl(ASL0) + pp(TRUE) + size(20) + } + field(OVAL,DBF_STRING) { + prompt("Prev Value") + special(SPC_NOMOD) + interest(3) + size(20) + } +} diff --git a/src/rec/stringinRecord.c b/src/rec/stringinRecord.c new file mode 100644 index 000000000..7e35c6450 --- /dev/null +++ b/src/rec/stringinRecord.c @@ -0,0 +1,208 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recStringin.c - Record Support Routines for Stringin records */ +/* + * Author: Janet Anderson + * Date: 4/23/91 + */ + + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "stringinRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(stringinRecord *, int); +static long process(stringinRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset stringinRSET={ + 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,stringinRSET); + +struct stringindset { /* stringin input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_stringin; /*returns: (-1,0)=>(failure,success)*/ +}; +static void monitor(stringinRecord *); +static long readValue(stringinRecord *); + + +static long init_record(stringinRecord *prec, int pass) +{ + STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); + struct stringindset *pdset; + long status; + + if (pass==0) return(0); + + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + /* stringin.siol must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siol.type == CONSTANT) { + recGblInitConstantLink(&prec->siol,DBF_STRING,prec->sval); + } + + if(!(pdset = (struct stringindset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"stringin: init_record"); + return(S_dev_noDSET); + } + /* must have read_stringin function defined */ + if( (pdset->number < 5) || (pdset->read_stringin == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"stringin: init_record"); + return(S_dev_missingSup); + } + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + } + strcpy(prec->oval,prec->val); + return(0); +} + +/* + */ +static long process(stringinRecord *prec) +{ + struct stringindset *pdset = (struct stringindset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->read_stringin==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"read_stringin"); + return(S_dev_missingSup); + } + + status=readValue(prec); /* read the new value */ + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + + /* check event list */ + monitor(prec); + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return(status); +} + +static void monitor(stringinRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + if(strcmp(prec->oval,prec->val)) { + monitor_mask |= DBE_VALUE|DBE_LOG; + strcpy(prec->oval,prec->val); + } + if (prec->mpst == stringinPOST_Always) + monitor_mask |= DBE_VALUE; + if (prec->apst == stringinPOST_Always) + monitor_mask |= DBE_LOG; + if(monitor_mask) + db_post_events(prec,&(prec->val[0]),monitor_mask); + return; +} + +static long readValue(stringinRecord *prec) +{ + long status; + struct stringindset *pdset = (struct stringindset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->read_stringin)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->read_stringin)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbGetLink(&(prec->siol),DBR_STRING, + prec->sval,0,0); + if (status==0) { + strcpy(prec->val,prec->sval); + prec->udf=FALSE; + } + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/stringinRecord.dbd b/src/rec/stringinRecord.dbd new file mode 100644 index 000000000..acafc8cf4 --- /dev/null +++ b/src/rec/stringinRecord.dbd @@ -0,0 +1,72 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(stringinPOST) { + choice(stringinPOST_OnChange,"On Change") + choice(stringinPOST_Always,"Always") +} +recordtype(stringin) { + include "dbCommon.dbd" + field(VAL,DBF_STRING) { + prompt("Current Value") + promptgroup(GUI_INPUTS) + asl(ASL0) + pp(TRUE) + size(40) + } + field(OVAL,DBF_STRING) { + prompt("Previous Value") + special(SPC_NOMOD) + interest(3) + size(40) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + } + field(MPST,DBF_MENU) { + prompt("Post Value Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(stringinPOST) + } + field(APST,DBF_MENU) { + prompt("Post Archive Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(stringinPOST) + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SVAL,DBF_STRING) { + prompt("Simulation Value") + pp(TRUE) + size(40) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } +} diff --git a/src/rec/stringoutRecord.c b/src/rec/stringoutRecord.c new file mode 100644 index 000000000..982687f2c --- /dev/null +++ b/src/rec/stringoutRecord.c @@ -0,0 +1,233 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recStringout.c - Record Support Routines for Stringout records */ +/* + * Author: Janet Anderson + * Date: 4/23/91 + */ + + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#define GEN_SIZE_OFFSET +#include "stringoutRecord.h" +#undef GEN_SIZE_OFFSET +#include "menuOmsl.h" +#include "menuIvoa.h" +#include "menuYesNo.h" +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(stringoutRecord *, int); +static long process(stringoutRecord *); +#define special NULL +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset stringoutRSET={ + 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,stringoutRSET); + +struct stringoutdset { /* stringout input dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_stringout;/*(-1,0)=>(failure,success)*/ +}; +static void monitor(stringoutRecord *); +static long writeValue(stringoutRecord *); + + +static long init_record(stringoutRecord *prec, int pass) +{ + STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); + struct stringoutdset *pdset; + long status=0; + + if (pass==0) return(0); + + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + if(!(pdset = (struct stringoutdset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"stringout: init_record"); + return(S_dev_noDSET); + } + /* must have write_stringout functions defined */ + if( (pdset->number < 5) || (pdset->write_stringout == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"stringout: init_record"); + return(S_dev_missingSup); + } + /* get the initial value dol is a constant*/ + if (prec->dol.type == CONSTANT){ + if(recGblInitConstantLink(&prec->dol,DBF_STRING,prec->val)) + prec->udf=FALSE; + } + if( pdset->init_record ) { + if((status=(*pdset->init_record)(prec))) return(status); + } + strcpy(prec->oval,prec->val); + return(0); +} + +static long process(stringoutRecord *prec) +{ + struct stringoutdset *pdset = (struct stringoutdset *)(prec->dset); + long status=0; + unsigned char pact=prec->pact; + + if( (pdset==NULL) || (pdset->write_stringout==NULL) ) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup,(void *)prec,"write_stringout"); + return(S_dev_missingSup); + } + if (!prec->pact + && (prec->dol.type != CONSTANT) + && (prec->omsl == menuOmslclosed_loop)) { + status = dbGetLink(&(prec->dol), + DBR_STRING,prec->val,0,0); + if(prec->dol.type!=CONSTANT && RTN_SUCCESS(status)) prec->udf=FALSE; + } + + if(prec->udf == TRUE ){ + recGblSetSevr(prec,UDF_ALARM,INVALID_ALARM); + } + + if (prec->nsev < INVALID_ALARM ) + status=writeValue(prec); /* write the new value */ + else { + switch (prec->ivoa) { + case (menuIvoaContinue_normally) : + status=writeValue(prec); /* write the new value */ + break; + case (menuIvoaDon_t_drive_outputs) : + break; + case (menuIvoaSet_output_to_IVOV) : + if(prec->pact == FALSE){ + strcpy(prec->val,prec->ivov); + } + status=writeValue(prec); /* write the new value */ + break; + default : + status=-1; + recGblRecordError(S_db_badField,(void *)prec, + "stringout:process Illegal IVOA field"); + } + } + + /* check if device support set pact */ + if ( !pact && prec->pact ) return(0); + + prec->pact = TRUE; + recGblGetTimeStamp(prec); + monitor(prec); + recGblFwdLink(prec); + prec->pact=FALSE; + return(status); +} + +static void monitor(stringoutRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + if(strcmp(prec->oval,prec->val)) { + monitor_mask |= DBE_VALUE|DBE_LOG; + strcpy(prec->oval,prec->val); + } + if (prec->mpst == stringoutPOST_Always) + monitor_mask |= DBE_VALUE; + if (prec->apst == stringoutPOST_Always) + monitor_mask |= DBE_LOG; + if(monitor_mask) + db_post_events(prec,&(prec->val[0]),monitor_mask); + return; +} + +static long writeValue(stringoutRecord *prec) +{ + long status; + struct stringoutdset *pdset = (struct stringoutdset *) (prec->dset); + + if (prec->pact == TRUE){ + status=(*pdset->write_stringout)(prec); + return(status); + } + + status=dbGetLink(&(prec->siml),DBR_USHORT, + &(prec->simm),0,0); + if (status) + return(status); + + if (prec->simm == menuYesNoNO){ + status=(*pdset->write_stringout)(prec); + return(status); + } + if (prec->simm == menuYesNoYES){ + status=dbPutLink(&prec->siol,DBR_STRING, + prec->val,1); + } else { + status=-1; + recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); + return(status); + } + recGblSetSevr(prec,SIMM_ALARM,prec->sims); + + return(status); +} diff --git a/src/rec/stringoutRecord.dbd b/src/rec/stringoutRecord.dbd new file mode 100644 index 000000000..25bd2fde9 --- /dev/null +++ b/src/rec/stringoutRecord.dbd @@ -0,0 +1,90 @@ +#************************************************************************* +# 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. +#************************************************************************* +menu(stringoutPOST) { + choice(stringoutPOST_OnChange,"On Change") + choice(stringoutPOST_Always,"Always") +} +recordtype(stringout) { + include "dbCommon.dbd" + field(VAL,DBF_STRING) { + prompt("Current Value") + promptgroup(GUI_OUTPUT) + asl(ASL0) + pp(TRUE) + size(40) + } + field(OVAL,DBF_STRING) { + prompt("Previous Value") + special(SPC_NOMOD) + interest(3) + size(40) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup(GUI_OUTPUT) + interest(1) + menu(menuOmsl) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup(GUI_OUTPUT) + interest(1) + } + field(MPST,DBF_MENU) { + prompt("Post Value Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(stringoutPOST) + } + field(APST,DBF_MENU) { + prompt("Post Archive Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(stringoutPOST) + } + field(SIOL,DBF_OUTLINK) { + prompt("Sim Output Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(IVOA,DBF_MENU) { + prompt("INVALID output action") + promptgroup(GUI_OUTPUT) + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_STRING) { + prompt("INVALID output value") + promptgroup(GUI_OUTPUT) + interest(2) + size(40) + } +} diff --git a/src/rec/subArrayRecord.c b/src/rec/subArrayRecord.c new file mode 100644 index 000000000..5813a7693 --- /dev/null +++ b/src/rec/subArrayRecord.c @@ -0,0 +1,309 @@ +/*************************************************************************\ +* Copyright (c) 2002 Lawrence Berkeley Laboratory,The Control Systems +* Group, Systems Engineering Department +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Revision-Id: anj@aps.anl.gov-20101112220909-zd1pn4qnqsafag0l */ + +/* recSubArray.c - Record Support Routines for SubArray records + * + * + * Author: Carl Lionberger + * Date: 090293 + * + * NOTES: + * Derived from waveform record. + * Modification Log: + * ----------------- + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "dbScan.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#define GEN_SIZE_OFFSET +#include "subArrayRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(subArrayRecord *prec, int pass); +static long process(subArrayRecord *prec); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *paddr); +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset); +static long put_array_info(DBADDR *paddr, long nNew); +static long get_units(DBADDR *paddr, char *units); +static long get_precision(DBADDR *paddr, long *precision); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd); +static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd); +#define get_alarm_double NULL + +rset subArrayRSET={ + 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,subArrayRSET); + +struct sadset { /* subArray dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_sa; /*returns: (-1,0)=>(failure,success)*/ +}; + +static void monitor(subArrayRecord *prec); +static long readValue(subArrayRecord *prec); + + + +static long init_record(subArrayRecord *prec, int pass) +{ + struct sadset *pdset; + + if (pass==0){ + if (prec->malm <= 0) + prec->malm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + prec->bptr = callocMustSucceed(prec->malm, dbValueSize(prec->ftvl), + "subArrayRecord calloc failed"); + prec->nord = 0; + return 0; + } + + /* must have dset defined */ + if (!(pdset = (struct sadset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"sa: init_record"); + return S_dev_noDSET; + } + + /* must have read_sa function defined */ + if ( (pdset->number < 5) || (pdset->read_sa == NULL) ) { + recGblRecordError(S_dev_missingSup,(void *)prec,"sa: init_record"); + return S_dev_missingSup; + } + + if (pdset->init_record) + return (*pdset->init_record)(prec); + + return 0; +} + +static long process(subArrayRecord *prec) +{ + struct sadset *pdset = (struct sadset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if ((pdset==NULL) || (pdset->read_sa==NULL)) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup, (void *)prec, "read_sa"); + return S_dev_missingSup; + } + + if (pact && prec->busy) return 0; + + status=readValue(prec); /* read the new value */ + if (!pact && prec->pact) return 0; + prec->pact = TRUE; + + recGblGetTimeStamp(prec); + + prec->udf = !!status; /* 0 or 1 */ + if (status) + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + + monitor(prec); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return 0; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + subArrayRecord *prec = (subArrayRecord *) paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->malm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + subArrayRecord *prec = (subArrayRecord *) paddr->precord; + + if (prec->udf) + *no_elements = 0; + else + *no_elements = prec->nord; + *offset = 0; + + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + subArrayRecord *prec = (subArrayRecord *) paddr->precord; + + if (nNew > prec->malm) + nNew = prec->malm; + prec->nord = nNew; + + return 0; +} + +static long get_units(DBADDR *paddr, char *units) +{ + subArrayRecord *prec = (subArrayRecord *) paddr->precord; + + strncpy(units, prec->egu, DB_UNITS_SIZE); + + return 0; +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + subArrayRecord *prec = (subArrayRecord *) paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + *precision = prec->prec; + + if (fieldIndex != subArrayRecordVAL) + recGblGetPrec(paddr, precision); + + return 0; +} + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + subArrayRecord *prec = (subArrayRecord *) paddr->precord; + + switch (dbGetFieldIndex(paddr)) { + case subArrayRecordVAL: + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + break; + + case subArrayRecordINDX: + pgd->upper_disp_limit = prec->malm - 1; + pgd->lower_disp_limit = 0; + break; + + case subArrayRecordNELM: + pgd->upper_disp_limit = prec->malm; + pgd->lower_disp_limit = 1; + break; + + default: + recGblGetGraphicDouble(paddr, pgd); + } + + return 0; +} + +static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) +{ + subArrayRecord *prec = (subArrayRecord *) paddr->precord; + + switch (dbGetFieldIndex(paddr)) { + case subArrayRecordVAL: + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + break; + + case subArrayRecordINDX: + pcd->upper_ctrl_limit = prec->malm - 1; + pcd->lower_ctrl_limit = 0; + break; + + case subArrayRecordNELM: + pcd->upper_ctrl_limit = prec->malm; + pcd->lower_ctrl_limit = 1; + break; + + default: + recGblGetControlDouble(paddr, pcd); + } + + return 0; +} + +static void monitor(subArrayRecord *prec) +{ + unsigned short monitor_mask; + + monitor_mask = recGblResetAlarms(prec); + monitor_mask |= (DBE_LOG|DBE_VALUE); + + db_post_events(prec, prec->bptr, monitor_mask); + + return; +} + +static long readValue(subArrayRecord *prec) +{ + long status; + struct sadset *pdset = (struct sadset *) (prec->dset); + + if (prec->nelm > prec->malm) + prec->nelm = prec->malm; + + if (prec->indx >= prec->malm) + prec->indx = prec->malm - 1; + + status = (*pdset->read_sa)(prec); + + if (prec->nord <= 0) + status = -1; + + return status; +} + diff --git a/src/rec/subArrayRecord.dbd b/src/rec/subArrayRecord.dbd new file mode 100644 index 000000000..8eeb6c0d9 --- /dev/null +++ b/src/rec/subArrayRecord.dbd @@ -0,0 +1,84 @@ +#************************************************************************* +# 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. +#************************************************************************* +recordtype(subArray) { + include "dbCommon.dbd" + field(VAL,DBF_NOACCESS) { + prompt("Value") + asl(ASL0) + special(SPC_DBADDR) + pp(TRUE) + extra("void * val") + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_COMMON) + interest(1) + } + field(FTVL,DBF_MENU) { + prompt("Field Type of Value") + promptgroup(GUI_ALARMS) + special(SPC_NOMOD) + interest(1) + menu(menuFtype) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_BITS1) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units Name") + promptgroup(GUI_BITS2) + interest(1) + size(16) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_CALC) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_CLOCK) + interest(1) + } + field(MALM,DBF_ULONG) { + prompt("Maximum Elements ") + promptgroup(GUI_CLOCK) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(NELM,DBF_ULONG) { + prompt("Number of Elements") + promptgroup(GUI_COMPRESS) + pp(TRUE) + initial("1") + } + field(INDX,DBF_ULONG) { + prompt("Substring Index") + promptgroup(GUI_CONVERT) + pp(TRUE) + } + field(BUSY,DBF_SHORT) { + prompt("Busy Indicator") + special(SPC_NOMOD) + } + field(NORD,DBF_LONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(BPTR,DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + interest(4) + extra("void * bptr") + } +} diff --git a/src/rec/subRecord.c b/src/rec/subRecord.c new file mode 100644 index 000000000..eb92e137b --- /dev/null +++ b/src/rec/subRecord.c @@ -0,0 +1,416 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* Record Support Routines for Subroutine records */ +/* + * Original Author: Bob Dalesio + * Date: 01-25-90 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "epicsMath.h" +#include "registryFunction.h" +#include "alarm.h" +#include "cantProceed.h" +#include "dbAccess.h" +#include "epicsPrint.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "special.h" +#define GEN_SIZE_OFFSET +#include "subRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(subRecord *, int); +static long process(subRecord *); +static long special(DBADDR *, int); +#define get_value NULL +#define cvt_dbaddr NULL +#define get_array_info NULL +#define put_array_info NULL +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +static long get_alarm_double(DBADDR *, struct dbr_alDouble *); + +rset subRSET={ + 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, subRSET); + +static void checkAlarms(subRecord *); +static long do_sub(subRecord *); +static long fetch_values(subRecord *); +static void monitor(subRecord *); + +#define INP_ARG_MAX 12 + +static long init_record(subRecord *prec, int pass) +{ + SUBFUNCPTR psubroutine; + long status = 0; + struct link *plink; + int i; + double *pvalue; + + if (pass==0) return(0); + + plink = &prec->inpa; + pvalue = &prec->a; + for (i = 0; i < INP_ARG_MAX; i++, plink++, pvalue++) { + if (plink->type == CONSTANT) { + recGblInitConstantLink(plink, DBF_DOUBLE, pvalue); + } + } + + if (prec->inam[0]) { + /* convert the initialization subroutine name */ + psubroutine = (SUBFUNCPTR)registryFunctionFind(prec->inam); + if (psubroutine == 0) { + recGblRecordError(S_db_BadSub, (void *)prec, "recSub(init_record)"); + return S_db_BadSub; + } + /* invoke the initialization subroutine */ + status = (*psubroutine)(prec); + } + + if (prec->snam[0] == 0) { + epicsPrintf("%s.SNAM is empty\n", prec->name); + prec->pact = TRUE; + return 0; + } + prec->sadr = (SUBFUNCPTR)registryFunctionFind(prec->snam); + if (prec->sadr == NULL) { + recGblRecordError(S_db_BadSub, (void *)prec, "recSub(init_record)"); + return S_db_BadSub; + } + prec->mlst = prec->val; + prec->alst = prec->val; + prec->lalm = prec->val; + return 0; +} + +static long process(subRecord *prec) +{ + long status = 0; + int pact = prec->pact; + + if (!pact) { + prec->pact = TRUE; + status = fetch_values(prec); + prec->pact = FALSE; + } + if (status == 0) status = do_sub(prec); + + /* Is subroutine asynchronous? */ + if (!pact && prec->pact) return 0; + prec->pact = TRUE; + + /* Asynchronous function (documented API!) */ + if (status == 1) return 0; + + recGblGetTimeStamp(prec); + + /* check for alarms */ + checkAlarms(prec); + + /* publish changes */ + monitor(prec); + + recGblFwdLink(prec); + prec->pact = FALSE; + + return 0; +} + +static long special(DBADDR *paddr, int after) +{ + subRecord *prec = (subRecord *)paddr->precord; + + if (!after) { + if (prec->snam[0] == 0 && prec->pact) + prec->pact = FALSE; + prec->rpro = FALSE; + return 0; + } + + if (prec->snam[0] == 0) { + epicsPrintf("%s.SNAM is empty\n", prec->name); + prec->pact = TRUE; + return 0; + } + + prec->sadr = (SUBFUNCPTR)registryFunctionFind(prec->snam); + if (prec->sadr) return 0; + + recGblRecordError(S_db_BadSub, (void *)prec, + "subRecord(special) registryFunctionFind failed"); + return S_db_BadSub; +} + +static long get_units(DBADDR *paddr, char *units) +{ + subRecord *prec = (subRecord *)paddr->precord; + + strncpy(units, prec->egu, DB_UNITS_SIZE); + return 0; +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + *precision = prec->prec; + if (fieldIndex != subRecordVAL) + recGblGetPrec(paddr, precision); + + return 0; +} + + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + switch (fieldIndex) { + case subRecordVAL: + case subRecordHIHI: case subRecordHIGH: + case subRecordLOW: case subRecordLOLO: + case subRecordA: case subRecordB: + case subRecordC: case subRecordD: + case subRecordE: case subRecordF: + case subRecordG: case subRecordH: + case subRecordI: case subRecordJ: + case subRecordK: case subRecordL: + case subRecordLA: case subRecordLB: + case subRecordLC: case subRecordLD: + case subRecordLE: case subRecordLF: + case subRecordLG: case subRecordLH: + case subRecordLI: case subRecordLJ: + case subRecordLK: case subRecordLL: + pgd->upper_disp_limit = prec->hopr; + pgd->lower_disp_limit = prec->lopr; + break; + + default: + recGblGetGraphicDouble(paddr, pgd); + } + return 0; +} + +static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) +{ + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + switch (fieldIndex) { + case subRecordVAL: + case subRecordHIHI: case subRecordHIGH: + case subRecordLOW: case subRecordLOLO: + case subRecordA: case subRecordB: + case subRecordC: case subRecordD: + case subRecordE: case subRecordF: + case subRecordG: case subRecordH: + case subRecordI: case subRecordJ: + case subRecordK: case subRecordL: + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + break; + + default: + recGblGetControlDouble(paddr, pcd); + } + return 0; +} + +static long get_alarm_double(DBADDR *paddr, struct dbr_alDouble *pad) +{ + subRecord *prec = (subRecord *)paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (fieldIndex == subRecordVAL) { + 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(subRecord *prec) +{ + double val, hyst, lalm; + double alev; + epicsEnum16 asev; + + if (prec->udf) { + recGblSetSevr(prec, UDF_ALARM, INVALID_ALARM); + return; + } + + val = prec->val; + hyst = prec->hyst; + lalm = prec->lalm; + + /* alarm condition hihi */ + asev = prec->hhsv; + alev = prec->hihi; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIHI_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition lolo */ + asev = prec->llsv; + alev = prec->lolo; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOLO_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition high */ + asev = prec->hsv; + alev = prec->high; + if (asev && (val >= alev || ((lalm == alev) && (val >= alev - hyst)))) { + if (recGblSetSevr(prec, HIGH_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* alarm condition low */ + asev = prec->lsv; + alev = prec->low; + if (asev && (val <= alev || ((lalm == alev) && (val <= alev + hyst)))) { + if (recGblSetSevr(prec, LOW_ALARM, asev)) + prec->lalm = alev; + return; + } + + /* we get here only if val is out of alarm by at least hyst */ + prec->lalm = val; + return; +} + +static void monitor(subRecord *prec) +{ + unsigned monitor_mask; + double delta; + double *pnew; + double *pold; + int i; + + /* get alarm mask */ + monitor_mask = recGblResetAlarms(prec); + /* check for value change */ + delta = prec->val - prec->mlst; + 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->val - prec->alst; + 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); + } + /* check all input fields for changes */ + for (i = 0, pnew = &prec->a, pold = &prec->la; + i < INP_ARG_MAX; i++, pnew++, pold++) { + if (*pnew != *pold) { + db_post_events(prec, pnew, monitor_mask | DBE_VALUE | DBE_LOG); + *pold = *pnew; + } + } + return; +} + +static long fetch_values(subRecord *prec) +{ + struct link *plink = &prec->inpa; + double *pvalue = &prec->a; + int i; + + for (i = 0; i < INP_ARG_MAX; i++, plink++, pvalue++) { + if (dbGetLink(plink, DBR_DOUBLE, pvalue, 0, 0)) + return -1; + } + return 0; +} + +static long do_sub(subRecord *prec) +{ + SUBFUNCPTR psubroutine = prec->sadr; + long status; + + if (psubroutine == NULL) { + recGblSetSevr(prec, BAD_SUB_ALARM, INVALID_ALARM); + return 0; + } + + status = (*psubroutine)(prec); + if (status < 0) { + recGblSetSevr(prec, SOFT_ALARM, prec->brsv); + } else { + prec->udf = isnan(prec->val); + } + return status; +} diff --git a/src/rec/subRecord.dbd b/src/rec/subRecord.dbd new file mode 100644 index 000000000..6b21c2348 --- /dev/null +++ b/src/rec/subRecord.dbd @@ -0,0 +1,316 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +recordtype(sub) { + include "dbCommon.dbd" + field(VAL,DBF_DOUBLE) { + prompt("Result") + asl(ASL0) + pp(TRUE) + } + field(INAM,DBF_STRING) { + prompt("Init Routine Name") + promptgroup(GUI_SUB) + special(SPC_NOMOD) + interest(1) + size(40) + } + field(SNAM,DBF_STRING) { + prompt("Subroutine Name") + promptgroup(GUI_SUB) + special(SPC_MOD) + interest(1) + size(40) + } + %struct subRecord; + %typedef long (*SUBFUNCPTR)(struct subRecord *); + field(SADR,DBF_NOACCESS) { + prompt("Subroutine Address") + special(SPC_NOMOD) + interest(4) + extra("SUBFUNCPTR sadr") + } + field(INPA,DBF_INLINK) { + prompt("Input A") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input B") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input C") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input D") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input E") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input F") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input G") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input H") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input I") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input J") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input K") + promptgroup(GUI_INPUTS) + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input L") + promptgroup(GUI_INPUTS) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Units Name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Rng") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(LOW,DBF_DOUBLE) { + prompt("Low Alarm Limit") + promptgroup(GUI_ALARMS) + pp(TRUE) + interest(1) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(BRSV,DBF_MENU) { + prompt("Bad Return Severity") + promptgroup(GUI_SUB) + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + 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(A,DBF_DOUBLE) { + prompt("Value of Input A") + pp(TRUE) + } + field(B,DBF_DOUBLE) { + prompt("Value of Input B") + pp(TRUE) + } + field(C,DBF_DOUBLE) { + prompt("Value of Input C") + pp(TRUE) + } + field(D,DBF_DOUBLE) { + prompt("Value of Input D") + pp(TRUE) + } + field(E,DBF_DOUBLE) { + prompt("Value of Input E") + pp(TRUE) + } + field(F,DBF_DOUBLE) { + prompt("Value of Input F") + pp(TRUE) + } + field(G,DBF_DOUBLE) { + prompt("Value of Input G") + pp(TRUE) + } + field(H,DBF_DOUBLE) { + prompt("Value of Input H") + pp(TRUE) + } + field(I,DBF_DOUBLE) { + prompt("Value of Input I") + pp(TRUE) + } + field(J,DBF_DOUBLE) { + prompt("Value of Input J") + pp(TRUE) + } + field(K,DBF_DOUBLE) { + prompt("Value of Input K") + pp(TRUE) + } + field(L,DBF_DOUBLE) { + prompt("Value of Input L") + pp(TRUE) + } + field(LA,DBF_DOUBLE) { + prompt("Prev Value of A") + special(SPC_NOMOD) + interest(3) + } + field(LB,DBF_DOUBLE) { + prompt("Prev Value of B") + special(SPC_NOMOD) + interest(3) + } + field(LC,DBF_DOUBLE) { + prompt("Prev Value of C") + special(SPC_NOMOD) + interest(3) + } + field(LD,DBF_DOUBLE) { + prompt("Prev Value of D") + special(SPC_NOMOD) + interest(3) + } + field(LE,DBF_DOUBLE) { + prompt("Prev Value of E") + special(SPC_NOMOD) + interest(3) + } + field(LF,DBF_DOUBLE) { + prompt("Prev Value of F") + special(SPC_NOMOD) + interest(3) + } + field(LG,DBF_DOUBLE) { + prompt("Prev Value of G") + special(SPC_NOMOD) + interest(3) + } + field(LH,DBF_DOUBLE) { + prompt("Prev Value of H") + special(SPC_NOMOD) + interest(3) + } + field(LI,DBF_DOUBLE) { + prompt("Prev Value of I") + special(SPC_NOMOD) + interest(3) + } + field(LJ,DBF_DOUBLE) { + prompt("Prev Value of J") + special(SPC_NOMOD) + interest(3) + } + field(LK,DBF_DOUBLE) { + prompt("Prev Value of K") + special(SPC_NOMOD) + interest(3) + } + field(LL,DBF_DOUBLE) { + prompt("Prev Value of L") + special(SPC_NOMOD) + interest(3) + } + 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 Value Monitored") + special(SPC_NOMOD) + interest(3) + } +} diff --git a/src/rec/waveformRecord.c b/src/rec/waveformRecord.c new file mode 100644 index 000000000..6031be4ed --- /dev/null +++ b/src/rec/waveformRecord.c @@ -0,0 +1,314 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, 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 is 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 */ + +/* recWaveform.c - Record Support Routines for Waveform records */ +/* + * Original Author: Bob Dalesio + * Date: 7-14-89 + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "alarm.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "dbScan.h" +#include "devSup.h" +#include "errMdef.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#include "menuYesNo.h" +#define GEN_SIZE_OFFSET +#include "waveformRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" +#include "epicsString.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(waveformRecord *, int); +static long process(waveformRecord *); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +static long get_units(DBADDR *, char *); +static long get_precision(DBADDR *, long *); +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +static long get_graphic_double(DBADDR *, struct dbr_grDouble *); +static long get_control_double(DBADDR *, struct dbr_ctrlDouble *); +#define get_alarm_double NULL +rset waveformRSET={ + 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,waveformRSET); +struct wfdset { /* waveform dset */ + long number; + DEVSUPFUN dev_report; + DEVSUPFUN init; + DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_wf; /*returns: (-1,0)=>(failure,success)*/ +}; + +static void monitor(waveformRecord *); +static long readValue(waveformRecord *); + +static long init_record(waveformRecord *prec, int pass) +{ + struct wfdset *pdset; + + if (pass==0){ + if (prec->nelm <= 0) + prec->nelm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), + "waveform calloc failed"); + if (prec->nelm == 1) { + prec->nord = 1; + } else { + prec->nord = 0; + } + return 0; + } + + /* wf.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ + if (prec->siml.type == CONSTANT) { + recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + } + + /* must have dset defined */ + if (!(pdset = (struct wfdset *)(prec->dset))) { + recGblRecordError(S_dev_noDSET,(void *)prec,"wf: init_record"); + return S_dev_noDSET; + } + /* must have read_wf function defined */ + if ((pdset->number < 5) || (pdset->read_wf == NULL)) { + recGblRecordError(S_dev_missingSup,(void *)prec,"wf: init_record"); + return S_dev_missingSup; + } + if (! pdset->init_record) return 0; + + return (*pdset->init_record)(prec); +} + +static long process(waveformRecord *prec) +{ + struct wfdset *pdset = (struct wfdset *)(prec->dset); + long status; + unsigned char pact=prec->pact; + + if ((pdset==NULL) || (pdset->read_wf==NULL)) { + prec->pact=TRUE; + recGblRecordError(S_dev_missingSup, (void *)prec, "read_wf"); + return S_dev_missingSup; + } + + if (pact && prec->busy) return 0; + + status=readValue(prec); /* read the new value */ + if (!pact && prec->pact) return 0; + + prec->pact = TRUE; + prec->udf = FALSE; + recGblGetTimeStamp(prec); + + monitor(prec); + + /* process the forward scan link record */ + recGblFwdLink(prec); + + prec->pact=FALSE; + return 0; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + waveformRecord *prec = (waveformRecord *) paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->nelm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + waveformRecord *prec = (waveformRecord *) paddr->precord; + + *no_elements = prec->nord; + *offset = 0; + + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + waveformRecord *prec = (waveformRecord *) paddr->precord; + + prec->nord = nNew; + if (prec->nord > prec->nelm) + prec->nord = prec->nelm; + + return 0; +} + +static long get_units(DBADDR *paddr, char *units) +{ + waveformRecord *prec = (waveformRecord *) paddr->precord; + + strncpy(units,prec->egu,DB_UNITS_SIZE); + + return 0; +} + +static long get_precision(DBADDR *paddr, long *precision) +{ + waveformRecord *prec = (waveformRecord *) paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + *precision = prec->prec; + + if (fieldIndex != waveformRecordVAL) + recGblGetPrec(paddr, precision); + + return 0; +} + +static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) +{ + waveformRecord *prec = (waveformRecord *) paddr->precord; + + if (dbGetFieldIndex(paddr) == waveformRecordVAL) { + 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) +{ + waveformRecord *prec = (waveformRecord *) paddr->precord; + + if (dbGetFieldIndex(paddr) == waveformRecordVAL) { + pcd->upper_ctrl_limit = prec->hopr; + pcd->lower_ctrl_limit = prec->lopr; + } else + recGblGetControlDouble(paddr, pcd); + return 0; +} + +static void monitor(waveformRecord *prec) +{ + unsigned short monitor_mask = 0; + unsigned int hash = 0; + + monitor_mask = recGblResetAlarms(prec); + + if (prec->mpst == waveformPOST_Always) + monitor_mask |= DBE_VALUE; + if (prec->apst == waveformPOST_Always) + monitor_mask |= DBE_LOG; + + /* Calculate hash if we are interested in OnChange events. */ + if ((prec->mpst == waveformPOST_OnChange) || + (prec->apst == waveformPOST_OnChange)) { + hash = epicsMemHash((char *)prec->bptr, + prec->nord * dbValueSize(prec->ftvl), 0); + + /* Only post OnChange values if the hash is different. */ + if (hash != prec->hash) { + if (prec->mpst == waveformPOST_OnChange) + monitor_mask |= DBE_VALUE; + if (prec->apst == waveformPOST_OnChange) + monitor_mask |= DBE_LOG; + + /* Store hash for next process. */ + prec->hash = hash; + /* Post HASH. */ + db_post_events(prec, &prec->hash, DBE_VALUE); + } + } + + if (monitor_mask) { + db_post_events(prec, prec->bptr, monitor_mask); + } +} + +static long readValue(waveformRecord *prec) +{ + long status; + struct wfdset *pdset = (struct wfdset *) prec->dset; + + if (prec->pact == TRUE){ + return (*pdset->read_wf)(prec); + } + + status = dbGetLink(&(prec->siml), DBR_ENUM, &(prec->simm),0,0); + if (status) + return status; + + if (prec->simm == menuYesNoNO){ + return (*pdset->read_wf)(prec); + } + + if (prec->simm == menuYesNoYES){ + long nRequest = prec->nelm; + status = dbGetLink(&(prec->siol), prec->ftvl, prec->bptr, 0, &nRequest); + /* nord set only for db links: needed for old db_access */ + if (prec->siol.type != CONSTANT) { + prec->nord = nRequest; + if (status == 0) + prec->udf=FALSE; + } + } else { + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + return -1; + } + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + + return status; +} + diff --git a/src/rec/waveformRecord.dbd b/src/rec/waveformRecord.dbd new file mode 100644 index 000000000..443f716da --- /dev/null +++ b/src/rec/waveformRecord.dbd @@ -0,0 +1,119 @@ +#************************************************************************* +# 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +menu(waveformPOST) { + choice(waveformPOST_Always,"Always") + choice(waveformPOST_OnChange,"On Change") +} +recordtype(waveform) { + include "dbCommon.dbd" + field(VAL,DBF_NOACCESS) { + prompt("Value") + asl(ASL0) + special(SPC_DBADDR) + pp(TRUE) + extra("void * val") + } + field(RARM,DBF_SHORT) { + prompt("Rearm the waveform") + promptgroup(GUI_WAVE) + pp(TRUE) + interest(1) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup(GUI_INPUTS) + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units Name") + promptgroup(GUI_DISPLAY) + interest(1) + size(16) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup(GUI_DISPLAY) + interest(1) + } + field(NELM,DBF_ULONG) { + prompt("Number of Elements") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + initial("1") + } + field(FTVL,DBF_MENU) { + prompt("Field Type of Value") + promptgroup(GUI_WAVE) + special(SPC_NOMOD) + interest(1) + menu(menuFtype) + } + field(BUSY,DBF_SHORT) { + prompt("Busy Indicator") + special(SPC_NOMOD) + } + field(NORD,DBF_ULONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(BPTR,DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + interest(4) + extra("void * bptr") + } + field(SIOL,DBF_INLINK) { + prompt("Sim Input Specifctn") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Sim Mode Location") + promptgroup(GUI_INPUTS) + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Sim mode Alarm Svrty") + promptgroup(GUI_INPUTS) + interest(2) + menu(menuAlarmSevr) + } + field(MPST,DBF_MENU) { + prompt("Post Value Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(waveformPOST) + } + field(APST,DBF_MENU) { + prompt("Post Archive Monitors") + promptgroup(GUI_DISPLAY) + interest(1) + menu(waveformPOST) + } + field(HASH,DBF_ULONG) { + prompt("Hash of OnChange data.") + interest(3) + } +} diff --git a/src/registry/Makefile b/src/registry/Makefile new file mode 100644 index 000000000..bacb70471 --- /dev/null +++ b/src/registry/Makefile @@ -0,0 +1,40 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../.. + +include $(TOP)/configure/CONFIG + +INC += registryRecordType.h +INC += registryDeviceSupport.h +INC += registryDriverSupport.h +INC += registryFunction.h +INC += registryCommon.h +INC += registryIocRegister.h + +SCRIPTS_HOST += registerRecordDeviceDriver.pl + +LIB_SRCS += registryRecordType.c +LIB_SRCS += registryDeviceSupport.c +LIB_SRCS += registryDriverSupport.c +LIB_SRCS += registryFunction.c +LIB_SRCS += registryCommon.c +LIB_SRCS += registryIocRegister.c + +LIBRARY_IOC = registryIoc +registryIoc_LIBS = dbStaticIoc Com + +registryIoc_RCS = registryIoc.rc + +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks = registryIoc +OBJLIB_SRCS = $(LIB_SRCS) +endif + +include $(TOP)/configure/RULES + diff --git a/src/registry/registerRecordDeviceDriver.pl b/src/registry/registerRecordDeviceDriver.pl new file mode 100755 index 000000000..87f6ba985 --- /dev/null +++ b/src/registry/registerRecordDeviceDriver.pl @@ -0,0 +1,229 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # registerRecordDeviceDriver +#************************************************************************* +# Copyright (c) 2009 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +($file, $subname, $bldTop) = @ARGV; +$numberRecordType = 0; +$numberDeviceSupport = 0; +$numberDriverSupport = 0; + +# Eliminate chars not allowed in C symbol names +$c_bad_ident_chars = '[^0-9A-Za-z_]'; +$subname =~ s/$c_bad_ident_chars/_/g; + +open(INP,"$file") or die "$! opening file"; +while() { + next if m/ ^ \s* \# /x; + if (m/ \b recordtype \s* \( \s* (\w+) \s* \) /x) { + $recordType[$numberRecordType++] = $1; + } + elsif (m/ \b device \s* \( \s* (\w+) \W+ \w+ \W+ (\w+) /x) { + $deviceRecordType[$numberDeviceSupport] = $1; + $deviceSupport[$numberDeviceSupport] = $2; + $numberDeviceSupport++; + } + elsif (m/ \b driver \s* \( \s* (\w+) \s* \) /x) { + $driverSupport[$numberDriverSupport++] = $1; + } + elsif (m/ \b registrar \s* \( \s* (\w+) \s* \) /x) { + push @registrars, $1; + } + elsif (m/ \b function \s* \( \s* (\w+) \s* \) /x) { + push @registrars, "register_func_$1"; + } + elsif (m/ \b variable \s* \( \s* (\w+) \s* , \s* (\w+) \s* \) /x) { + $varType{$1} = $2; + push @variables, $1; + } +} +close(INP) or die "$! closing file"; + + +# beginning of generated routine +print << "END" ; +/* THIS IS A GENERATED FILE. DO NOT EDIT! */ +/* Generated from $file */ + +#include + +#include "epicsStdlib.h" +#include "iocsh.h" +#include "iocshRegisterCommon.h" +#include "registryCommon.h" + +extern "C" { + +END + +#definitions for recordtype +if($numberRecordType>0) { + for ($i=0; $i<$numberRecordType; $i++) { + print "epicsShareExtern rset *pvar_rset_$recordType[$i]RSET;\n"; + print "epicsShareExtern int (*pvar_func_$recordType[$i]RecordSizeOffset)(dbRecordType *pdbRecordType);\n" + } + print "\nstatic const char * const recordTypeNames[$numberRecordType] = {\n"; + for ($i=0; $i<$numberRecordType; $i++) { + print " \"$recordType[$i]\""; + if($i < $numberRecordType-1) { print ",";} + print "\n"; + } + print "};\n\n"; + + print "static const recordTypeLocation rtl[$i] = {\n"; + for ($i=0; $i<$numberRecordType; $i++) { + print " {pvar_rset_$recordType[$i]RSET, pvar_func_$recordType[$i]RecordSizeOffset}"; + if($i < $numberRecordType-1) { print ",";} + print "\n"; + } + print "};\n\n"; +} + +#definitions for device +if($numberDeviceSupport>0) { + for ($i=0; $i<$numberDeviceSupport; $i++) { + print "epicsShareExtern dset *pvar_dset_$deviceSupport[$i];\n"; + } + print "\nstatic const char * const deviceSupportNames[$numberDeviceSupport] = {\n"; + for ($i=0; $i<$numberDeviceSupport; $i++) { + print " \"$deviceSupport[$i]\""; + if($i < $numberDeviceSupport-1) { print ",";} + print "\n"; + } + print "};\n\n"; + + print "static const dset * const devsl[$i] = {\n"; + for ($i=0; $i<$numberDeviceSupport; $i++) { + print " pvar_dset_$deviceSupport[$i]"; + if($i < $numberDeviceSupport-1) { print ",";} + print "\n"; + } + print "};\n\n"; +} + +#definitions for driver +if($numberDriverSupport>0) { + for ($i=0; $i<$numberDriverSupport; $i++) { + print "epicsShareExtern drvet *pvar_drvet_$driverSupport[$i];\n"; + } + print "\nstatic const char *driverSupportNames[$numberDriverSupport] = {\n"; + for ($i=0; $i<$numberDriverSupport; $i++) { + print " \"$driverSupport[$i]\""; + if($i < $numberDriverSupport-1) { print ",";} + print "\n"; + } + print "};\n\n"; + + print "static struct drvet *drvsl[$i] = {\n"; + for ($i=0; $i<$numberDriverSupport; $i++) { + print " pvar_drvet_$driverSupport[$i]"; + if($i < $numberDriverSupport-1) { print ",";} + print "\n"; + } + print "};\n\n"; +} + +#definitions registrar +if(@registrars) { + foreach $reg (@registrars) { + print "epicsShareExtern void (*pvar_func_$reg)(void);\n"; + } + print "\n"; +} + +if (@variables) { + foreach $var (@variables) { + print "epicsShareExtern $varType{$var} *pvar_$varType{$var}_$var;\n"; + } + %iocshTypes = ( + 'int' => 'iocshArgInt', + 'double' => 'iocshArgDouble' + ); + print "static struct iocshVarDef vardefs[] = {\n"; + foreach $var (@variables) { + $argType = $iocshTypes{$varType{$var}}; + die "Unknown variable type $varType{$var} for variable $var" + unless $argType; + print "\t{\"$var\", $argType, (void * const)pvar_$varType{$var}_$var},\n"; + } + print "\t{NULL, iocshArgInt, NULL}\n};\n\n"; +} + +#Now actual registration code. + +print "int $subname(DBBASE *pbase)\n{\n"; + +print << "END" if ($bldTop ne '') ; + const char *bldTop = "$bldTop"; + const char *envTop = getenv("TOP"); + + if (envTop && strcmp(envTop, bldTop)) { + printf("Warning: IOC is booting with TOP = \\"%s\\"\\n" + " but was built with TOP = \\"%s\\"\\n", + envTop, bldTop); + } + +END + +print << "END" ; + if (!pbase) { + printf("pdbbase is NULL; you must load a DBD file first.\\n"); + return -1; + } + +END + +if($numberRecordType>0) { + print " registerRecordTypes(pbase, $numberRecordType, ", + "recordTypeNames, rtl);\n"; +} +if($numberDeviceSupport>0) { + print " registerDevices(pbase, $numberDeviceSupport, ", + "deviceSupportNames, devsl);\n"; +} +if($numberDriverSupport>0) { + print " registerDrivers(pbase, $numberDriverSupport, ", + "driverSupportNames, drvsl);\n"; +} +foreach $reg (@registrars) { + print " (*pvar_func_$reg)();\n"; +} + +if (@variables) { + print " iocshRegisterVariable(vardefs);\n"; +} +print << "END" ; + return 0; +} + +/* registerRecordDeviceDriver */ +static const iocshArg registerRecordDeviceDriverArg0 = + {"pdbbase",iocshArgPdbbase}; +static const iocshArg *registerRecordDeviceDriverArgs[1] = + {®isterRecordDeviceDriverArg0}; +static const iocshFuncDef registerRecordDeviceDriverFuncDef = + {"$subname",1,registerRecordDeviceDriverArgs}; +static void registerRecordDeviceDriverCallFunc(const iocshArgBuf *) +{ + $subname(*iocshPpdbbase); +} + +} // extern "C" +/* + * Register commands on application startup + */ +static int Registration() { + iocshRegisterCommon(); + iocshRegister(®isterRecordDeviceDriverFuncDef, + registerRecordDeviceDriverCallFunc); + return 0; +} + +static int done = Registration(); +END diff --git a/src/registry/registryCommon.c b/src/registry/registryCommon.c new file mode 100644 index 000000000..0d2de4b15 --- /dev/null +++ b/src/registry/registryCommon.c @@ -0,0 +1,80 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* registryCommon.c */ + +/* Author: Andrew Johnson + * Date: 2004-03-19 + */ + +#include "dbBase.h" +#include "shareLib.h" +#include "errlog.h" + +#define epicsExportSharedSymbols +#include "registryDeviceSupport.h" +#include "registryDriverSupport.h" +#include "registryCommon.h" + + +void epicsShareAPI registerRecordTypes(DBBASE *pbase, int nRecordTypes, + const char* const* recordTypeNames, const recordTypeLocation *rtl) +{ + int i; + for(i=0; i< nRecordTypes; i++) { + recordTypeLocation *precordTypeLocation; + computeSizeOffset sizeOffset; + DBENTRY dbEntry; + + if(registryRecordTypeFind(recordTypeNames[i])) continue; + if(!registryRecordTypeAdd(recordTypeNames[i],&rtl[i])) { + errlogPrintf("registryRecordTypeAdd failed %s\n", + recordTypeNames[i]); + continue; + } + dbInitEntry(pbase,&dbEntry); + precordTypeLocation = registryRecordTypeFind(recordTypeNames[i]); + sizeOffset = precordTypeLocation->sizeOffset; + if(dbFindRecordType(&dbEntry,recordTypeNames[i])) { + errlogPrintf("registerRecordDeviceDriver failed %s\n", + recordTypeNames[i]); + } else { + sizeOffset(dbEntry.precordType); + } + } +} + +void epicsShareAPI registerDevices(DBBASE *pbase, int nDevices, + const char* const* deviceSupportNames, const dset* const* devsl) +{ + int i; + for(i=0; i< nDevices; i++) { + if(registryDeviceSupportFind(deviceSupportNames[i])) continue; + if(!registryDeviceSupportAdd(deviceSupportNames[i],devsl[i])) { + errlogPrintf("registryDeviceSupportAdd failed %s\n", + deviceSupportNames[i]); + continue; + } + } +} + +void epicsShareAPI registerDrivers(DBBASE *pbase, int nDrivers, + const char* const* driverSupportNames, struct drvet* const* drvsl) +{ + int i; + for(i=0; i< nDrivers; i++) { + if(registryDriverSupportFind(driverSupportNames[i])) continue; + if(!registryDriverSupportAdd(driverSupportNames[i], drvsl[i])) { + errlogPrintf("registryDriverSupportAdd failed %s\n", + driverSupportNames[i]); + continue; + } + } +} + diff --git a/src/registry/registryCommon.h b/src/registry/registryCommon.h new file mode 100644 index 000000000..ea851f9e9 --- /dev/null +++ b/src/registry/registryCommon.h @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* registryCommon.h */ + +#include "dbStaticLib.h" +#include "registryRecordType.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI registerRecordTypes( + DBBASE *pbase, int nRecordTypes, + const char* const* recordTypeNames, const recordTypeLocation *rtl); +epicsShareFunc void epicsShareAPI registerDevices( + DBBASE *pbase, int nDevices, + const char* const* deviceSupportNames, const dset* const* devsl); +epicsShareFunc void epicsShareAPI registerDrivers( + DBBASE *pbase, int nDrivers, + const char* const* driverSupportNames, struct drvet* const* drvsl); + +#ifdef __cplusplus +} +#endif diff --git a/src/registry/registryDeviceSupport.c b/src/registry/registryDeviceSupport.c new file mode 100644 index 000000000..85c65df5d --- /dev/null +++ b/src/registry/registryDeviceSupport.c @@ -0,0 +1,38 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* registryDeviceSupport.c */ + +/* Author: Marty Kraimer Date: 08JUN99 */ + +#include +#include +#include + +#include "dbBase.h" +#include "devSup.h" +#define epicsExportSharedSymbols +#include "registry.h" +#include "registryDeviceSupport.h" + +const char *deviceSupport = "device support"; +static void *registryID = (void *)&deviceSupport; + + +epicsShareFunc int epicsShareAPI registryDeviceSupportAdd( + const char *name,const struct dset *pdset) +{ + return(registryAdd(registryID,name,(void *)pdset)); +} + +epicsShareFunc struct dset * epicsShareAPI registryDeviceSupportFind( + const char *name) +{ + return((struct dset *)registryFind(registryID,name)); +} diff --git a/src/registry/registryDeviceSupport.h b/src/registry/registryDeviceSupport.h new file mode 100644 index 000000000..9c5f9a0e2 --- /dev/null +++ b/src/registry/registryDeviceSupport.h @@ -0,0 +1,30 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INCregistryDeviceSupporth +#define INCregistryDeviceSupporth + +#include "shareLib.h" +#include "devSup.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc int epicsShareAPI registryDeviceSupportAdd( + const char *name,const struct dset *pdset); +epicsShareFunc struct dset * epicsShareAPI registryDeviceSupportFind( + const char *name); + +#ifdef __cplusplus +} +#endif + + +#endif /* INCregistryDeviceSupporth */ diff --git a/src/registry/registryDriverSupport.c b/src/registry/registryDriverSupport.c new file mode 100644 index 000000000..309695d93 --- /dev/null +++ b/src/registry/registryDriverSupport.c @@ -0,0 +1,38 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* registryDriverSupport.c */ + +/* Author: Marty Kraimer Date: 08JUN99 */ + +#include +#include +#include + +#include "dbBase.h" +#include "drvSup.h" +#define epicsExportSharedSymbols +#include "registry.h" +#include "registryDriverSupport.h" + +const char *driverSupport = "driver support"; +static void *registryID = (void *)&driverSupport; + + +epicsShareFunc int epicsShareAPI registryDriverSupportAdd( + const char *name,struct drvet *pdrvet) +{ + return(registryAdd(registryID,name,(void *)pdrvet)); +} + +epicsShareFunc struct drvet * epicsShareAPI registryDriverSupportFind( + const char *name) +{ + return((struct drvet *)registryFind(registryID,name)); +} diff --git a/src/registry/registryDriverSupport.h b/src/registry/registryDriverSupport.h new file mode 100644 index 000000000..e8e427452 --- /dev/null +++ b/src/registry/registryDriverSupport.h @@ -0,0 +1,31 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INCregistryDriverSupporth +#define INCregistryDriverSupporth + +#include "shareLib.h" +#include "drvSup.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* c interface definitions */ +epicsShareFunc int epicsShareAPI registryDriverSupportAdd( + const char *name,struct drvet *pdrvet); +epicsShareFunc struct drvet * epicsShareAPI registryDriverSupportFind( + const char *name); + +#ifdef __cplusplus +} +#endif + + +#endif /* INCregistryDriverSupporth */ diff --git a/src/registry/registryFunction.c b/src/registry/registryFunction.c new file mode 100644 index 000000000..627ac3e23 --- /dev/null +++ b/src/registry/registryFunction.c @@ -0,0 +1,58 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* registryFunction.c */ + +/* Author: Marty Kraimer Date: 01MAY2000 */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "registry.h" +#include "registryFunction.h" + +const char *function = "function"; +static void *registryID = (void *)&function; + + +epicsShareFunc int epicsShareAPI registryFunctionAdd( + const char *name,REGISTRYFUNCTION func) +{ + return(registryAdd(registryID,name,(void *)func)); +} + +epicsShareFunc REGISTRYFUNCTION epicsShareAPI registryFunctionFind( + const char *name) +{ + REGISTRYFUNCTION func; + func = (REGISTRYFUNCTION)registryFind(registryID,name); + if(!func) { + func = (REGISTRYFUNCTION)registryFind(0,name); + if(func)registryFunctionAdd(name,func); + } + return(func); +} + +epicsShareFunc int epicsShareAPI registryFunctionRefAdd( + registryFunctionRef ref[],int nfunctions) +{ + int ind; + + for(ind=0; ind +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Registry Ioc Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Registry Ioc Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "registryIoc\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "registryIoc.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/registry/registryIocRegister.c b/src/registry/registryIocRegister.c new file mode 100644 index 000000000..cedc10607 --- /dev/null +++ b/src/registry/registryIocRegister.c @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "iocsh.h" + +#define epicsExportSharedSymbols +#include "registryRecordType.h" +#include "registryDeviceSupport.h" +#include "registryDriverSupport.h" +#include "registryFunction.h" +#include "registryIocRegister.h" + +static const iocshArg registryXxxFindArg0 = { "name",iocshArgString}; +static const iocshArg * const registryXxxFindArgs[1] = {®istryXxxFindArg0}; + +/* registryRecordTypeFind */ +static const iocshFuncDef registryRecordTypeFindFuncDef = { + "registryRecordTypeFind",1,registryXxxFindArgs}; +static void registryRecordTypeFindCallFunc(const iocshArgBuf *args) { + printf("%p\n", (void*) registryRecordTypeFind(args[0].sval)); +} + +/* registryDeviceSupportFind */ +static const iocshFuncDef registryDeviceSupportFindFuncDef = { + "registryDeviceSupportFind",1,registryXxxFindArgs}; +static void registryDeviceSupportFindCallFunc(const iocshArgBuf *args) { + printf("%p\n", (void*) registryDeviceSupportFind(args[0].sval)); +} + +/* registryDriverSupportFind */ +static const iocshFuncDef registryDriverSupportFindFuncDef = { + "registryDriverSupportFind",1,registryXxxFindArgs}; +static void registryDriverSupportFindCallFunc(const iocshArgBuf *args) { + printf("%p\n", (void*) registryDriverSupportFind(args[0].sval)); +} + +/* registryFunctionFind */ +static const iocshFuncDef registryFunctionFindFuncDef = { + "registryFunctionFind",1,registryXxxFindArgs}; +static void registryFunctionFindCallFunc(const iocshArgBuf *args) { + printf("%p\n", (void*) registryFunctionFind(args[0].sval)); +} + +void epicsShareAPI registryIocRegister(void) +{ + iocshRegister(®istryRecordTypeFindFuncDef,registryRecordTypeFindCallFunc); + iocshRegister(®istryDeviceSupportFindFuncDef,registryDeviceSupportFindCallFunc); + iocshRegister(®istryDriverSupportFindFuncDef,registryDriverSupportFindCallFunc); + iocshRegister(®istryFunctionFindFuncDef,registryFunctionFindCallFunc); +} diff --git a/src/registry/registryIocRegister.h b/src/registry/registryIocRegister.h new file mode 100644 index 000000000..2f9fdd3c3 --- /dev/null +++ b/src/registry/registryIocRegister.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_registryIocRegister_H +#define INC_registryIocRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI registryIocRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_registryIocRegister_H */ diff --git a/src/registry/registryRecordType.c b/src/registry/registryRecordType.c new file mode 100644 index 000000000..986a1b85a --- /dev/null +++ b/src/registry/registryRecordType.c @@ -0,0 +1,37 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* registryRecordType.c */ + +/* Author: Marty Kraimer Date: 08JUN99 */ + +#include +#include +#include + +#include "dbBase.h" +#define epicsExportSharedSymbols +#include "registry.h" +#include "registryRecordType.h" + +const char *recordType = "record type"; +static void *registryID = (void *)&recordType; + + +epicsShareFunc int epicsShareAPI registryRecordTypeAdd( + const char *name,const recordTypeLocation *prtl) +{ + return(registryAdd(registryID,name,(void *)prtl)); +} + +epicsShareFunc recordTypeLocation * epicsShareAPI registryRecordTypeFind( + const char *name) +{ + return((recordTypeLocation *)registryFind(registryID,name)); +} diff --git a/src/registry/registryRecordType.h b/src/registry/registryRecordType.h new file mode 100644 index 000000000..49582bb66 --- /dev/null +++ b/src/registry/registryRecordType.h @@ -0,0 +1,44 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +#ifndef INCregistryRecordTypeh +#define INCregistryRecordTypeh + +#include "recSup.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct dbRecordType; +struct rset; +struct dbBase; + +typedef int (*computeSizeOffset)(struct dbRecordType *pdbRecordType); + +typedef struct recordTypeLocation { + struct rset *prset; + computeSizeOffset sizeOffset; +}recordTypeLocation; + + +epicsShareFunc int epicsShareAPI registryRecordTypeAdd( + const char *name,const recordTypeLocation *prtl); +epicsShareFunc recordTypeLocation * epicsShareAPI registryRecordTypeFind( + const char *name); + +int registerRecordDeviceDriver(struct dbBase *pdbbase); + +#ifdef __cplusplus +} +#endif + + +#endif /* INCregistryRecordTypeh */ diff --git a/src/rsrv/Makefile b/src/rsrv/Makefile new file mode 100644 index 000000000..e420ba848 --- /dev/null +++ b/src/rsrv/Makefile @@ -0,0 +1,39 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../.. + +include $(TOP)/configure/CONFIG + +USR_INCLUDES = -I../../ca + +INC += rsrv.h +INC += rsrvIocRegister.h + +LIB_SRCS += caserverio.c +LIB_SRCS += caservertask.c +LIB_SRCS += camsgtask.c +LIB_SRCS += camessage.c +LIB_SRCS += cast_server.c +LIB_SRCS += online_notify.c +LIB_SRCS += rsrvIocRegister.c + +LIBRARY_IOC = rsrvIoc + +rsrvIoc_LIBS = asIoc dbIoc ca Com + +rsrvIoc_SYS_LIBS_WIN32 := ws2_32 +rsrvIoc_RCS = rsrvIoc.rc + +# For R3.13 compatibility only +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks=rsrvIoc +OBJLIB_SRCS = $(LIB_SRCS) +endif + +include $(TOP)/configure/RULES diff --git a/src/rsrv/camessage.c b/src/rsrv/camessage.c new file mode 100644 index 000000000..09015f667 --- /dev/null +++ b/src/rsrv/camessage.c @@ -0,0 +1,2605 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + * + */ + +#include +#include +#include +#include +#include +#include + +#include "osiSock.h" +#include "osiPoolStatus.h" +#include "epicsEvent.h" +#include "epicsStdio.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsTime.h" +#include "errlog.h" +#include "db_access_routines.h" +#include "db_access.h" +#include "special.h" +#include "freeList.h" +#include "caerr.h" +#include "db_field_log.h" +#include "dbEvent.h" +#include "dbCommon.h" +#include "db_field_log.h" +#include "callback.h" +#include "asDbLib.h" + +#include "net_convert.h" + +#define epicsExportSharedSymbols +#include "rsrv.h" +#include "server.h" + +#define RECORD_NAME(PADDR) ((PADDR)->precord->name) + +static EVENTFUNC read_reply; + +#define logBadId(CLIENT, MP, PPL)\ +logBadIdWithFileAndLineno(CLIENT, MP, PPL, __FILE__, __LINE__) + +/* + * for tracking db put notifies + */ +typedef struct rsrv_put_notify { + ELLNODE node; + putNotify dbPutNotify; + caHdrLargeArray msg; + /* + * Include a union of all scalar types + * including fixed length strings so + * that in many cases we can avoid + * allocating another buffer and only + * use an rsrv_put_notify from its + * free list. + */ + union { + dbr_string_t strval; + dbr_short_t shrtval; + dbr_short_t intval; + dbr_float_t fltval; + dbr_enum_t enmval; + dbr_char_t charval; + dbr_long_t longval; + dbr_double_t doubleval; + } dbrScalarValue; + void * asWritePvt; + unsigned valueSize; /* size of block pointed to by dbPutNotify */ + char busy; /* put notify in progress */ + char onExtraLaborQueue; +} RSRVPUTNOTIFY; + +/* + * casCalloc() + * + * (dont drop below some max block threshold) + */ +static void *casCalloc(size_t count, size_t size) +{ + if ( UINT_MAX / size >= count ) { + if (!osiSufficentSpaceInPool(size*count)) { + return NULL; + } + return calloc(count, size); + } + else { + return NULL; + } +} + +/* + * MPTOPCIU() + * + * used to be a macro + */ +static struct channel_in_use *MPTOPCIU (const caHdrLargeArray *mp) +{ + struct channel_in_use *pciu; + const unsigned id = mp->m_cid; + + LOCK_CLIENTQ; + pciu = bucketLookupItemUnsignedId (pCaBucket, &id); + UNLOCK_CLIENTQ; + + return pciu; +} + +/* vsend_err() + * + * reflect error msg back to the client + * + * send buffer lock must be on while in this routine + * + */ +static void vsend_err( +const caHdrLargeArray *curp, +int status, +struct client *client, +const char *pformat, +va_list args +) +{ + static const ca_uint32_t maxDiagLen = 512; + struct channel_in_use *pciu; + caHdr *pReqOut; + char *pMsgString; + ca_uint32_t size; + ca_uint32_t cid; + int localStatus; + + switch ( curp->m_cmmd ) { + case CA_PROTO_EVENT_ADD: + case CA_PROTO_EVENT_CANCEL: + case CA_PROTO_READ: + case CA_PROTO_READ_NOTIFY: + case CA_PROTO_WRITE: + case CA_PROTO_WRITE_NOTIFY: + pciu = MPTOPCIU(curp); + if(pciu){ + cid = pciu->cid; + } + else{ + cid = 0xffffffff; + } + break; + + case CA_PROTO_SEARCH: + cid = curp->m_cid; + break; + + case CA_PROTO_EVENTS_ON: + case CA_PROTO_EVENTS_OFF: + case CA_PROTO_READ_SYNC: + case CA_PROTO_SNAPSHOT: + default: + cid = 0xffffffff; + break; + } + + /* + * allocate plenty of space for a sprintf() buffer + */ + localStatus = cas_copy_in_header ( client, + CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status, + ( void * ) &pReqOut ); + if ( localStatus != ECA_NORMAL ) { + errlogPrintf ( "caserver: Unable to deliver err msg \"%s\" to client because \"%s\"\n", + ca_message (status), ca_message (localStatus) ); + errlogVprintf ( pformat, args ); + return; + } + + /* + * copy back the request protocol + * (in network byte order) + */ + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + CA_V49( client->minor_version_number ) ) { + ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); + pReqOut->m_cmmd = htons ( curp->m_cmmd ); + pReqOut->m_postsize = htons ( 0xffff ); + pReqOut->m_dataType = htons ( curp->m_dataType ); + pReqOut->m_count = htons ( 0u ); + pReqOut->m_cid = htonl ( curp->m_cid ); + pReqOut->m_available = htonl ( curp->m_available ); + pLW[0] = htonl ( curp->m_postsize ); + pLW[1] = htonl ( curp->m_count ); + pMsgString = ( char * ) ( pLW + 2 ); + size = sizeof ( caHdr ) + 2 * sizeof ( *pLW ); + } + else { + pReqOut->m_cmmd = htons (curp->m_cmmd); + pReqOut->m_postsize = htons ( ( (ca_uint16_t) curp->m_postsize ) ); + pReqOut->m_dataType = htons (curp->m_dataType); + pReqOut->m_count = htons ( ( (ca_uint16_t) curp->m_count ) ); + pReqOut->m_cid = htonl (curp->m_cid); + pReqOut->m_available = htonl (curp->m_available); + pMsgString = ( char * ) ( pReqOut + 1 ); + size = sizeof ( caHdr ); + } + + /* + * add their context string into the protocol + */ + localStatus = epicsVsnprintf ( pMsgString, maxDiagLen, pformat, args ); + if ( localStatus >= 1 ) { + unsigned diagLen = ( unsigned ) localStatus; + if ( diagLen < maxDiagLen ) { + size += (ca_uint32_t) (diagLen + 1u); + } + else { + errlogPrintf ( + "caserver: vsend_err: epicsVsnprintf detected " + "error message truncation, pFormat = \"%s\"\n", + pformat ); + size += maxDiagLen; + pMsgString [ maxDiagLen - 1 ] = '\0'; + } + } + cas_commit_msg ( client, size ); +} + +/* send_err() + * + * reflect error msg back to the client + * + * send buffer lock must be on while in this routine + * + */ +static void send_err ( +const caHdrLargeArray *curp, +int status, +struct client *client, +const char *pformat, + ... ) +{ + va_list args; + va_start ( args, pformat ); + vsend_err ( curp, status, client, pformat, args ); + va_end ( args ); +} + +/* log_header() + * + * Debug aid - print the header part of a message. + * + */ +static void log_header ( + const char *pContext, + struct client *client, + const caHdrLargeArray *mp, + const void *pPayLoad, + unsigned mnum +) +{ + struct channel_in_use *pciu; + char hostName[256]; + + ipAddrToDottedIP (&client->addr, hostName, sizeof(hostName)); + + pciu = MPTOPCIU(mp); + + if (pContext) { + epicsPrintf ("CAS: request from %s => \"%s\"\n", + hostName, pContext); + } + + epicsPrintf ( +"CAS: Request from %s => cmmd=%d cid=0x%x type=%d count=%d postsize=%u\n", + hostName, mp->m_cmmd, mp->m_cid, mp->m_dataType, mp->m_count, mp->m_postsize); + + epicsPrintf ( +"CAS: Request from %s => available=0x%x \tN=%u paddr=%p\n", + hostName, mp->m_available, mnum, (pciu?(void *)&pciu->addr:NULL)); + + if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING && pPayLoad ) { + epicsPrintf ( +"CAS: Request from %s => \tThe string written: %s \n", + hostName, (char *)pPayLoad ); + } +} + +/* + * logBadIdWithFileAndLineno() + */ +static void logBadIdWithFileAndLineno( +struct client *client, +caHdrLargeArray *mp, +const void *pPayload, +char *pFileName, +unsigned lineno +) +{ + log_header ( "bad resource ID", client, mp, pPayload, 0 ); + SEND_LOCK ( client ); + send_err ( mp, ECA_INTERNAL, client, "Bad Resource ID at %s.%d", + pFileName, lineno ); + SEND_UNLOCK ( client ); +} + +/* + * bad_udp_cmd_action() + */ +static int bad_udp_cmd_action ( caHdrLargeArray *mp, + void *pPayload, struct client *pClient ) +{ + log_header ("invalid (damaged?) request code from UDP", + pClient, mp, pPayload, 0); + return RSRV_ERROR; +} + +/* + * udp_echo_action() + */ +static int udp_echo_action ( caHdrLargeArray *mp, + void *pPayload, struct client *pClient ) +{ + char *pPayloadOut; + int status; + SEND_LOCK ( pClient ); + status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, + ( void * ) &pPayloadOut ); + if ( status == ECA_NORMAL ) { + memcpy ( pPayloadOut, pPayload, mp->m_postsize ); + cas_commit_msg ( pClient, mp->m_postsize ); + } + SEND_UNLOCK ( pClient ); + return RSRV_OK; +} + +/* + * bad_tcp_cmd_action() + */ +static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, + struct client *client ) +{ + const char *pCtx = "invalid (damaged?) request code from TCP"; + log_header ( pCtx, client, mp, pPayload, 0 ); + + /* + * by default, clients dont recover + * from this + */ + SEND_LOCK (client); + send_err (mp, ECA_INTERNAL, client, pCtx); + SEND_UNLOCK (client); + + return RSRV_ERROR; +} + +/* + * tcp_version_action() + */ +static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, + struct client *client ) +{ + double tmp; + unsigned epicsPriorityNew; + unsigned epicsPrioritySelf; + + if ( mp->m_dataType > CA_PROTO_PRIORITY_MAX ) { + return RSRV_ERROR; + } + + tmp = mp->m_dataType - CA_PROTO_PRIORITY_MIN; + tmp *= epicsThreadPriorityCAServerHigh - epicsThreadPriorityCAServerLow; + tmp /= CA_PROTO_PRIORITY_MAX - CA_PROTO_PRIORITY_MIN; + tmp += epicsThreadPriorityCAServerLow; + epicsPriorityNew = (unsigned) tmp; + epicsPrioritySelf = epicsThreadGetPrioritySelf(); + if ( epicsPriorityNew != epicsPrioritySelf ) { + epicsThreadBooleanStatus tbs; + unsigned priorityOfEvents; + tbs = epicsThreadHighestPriorityLevelBelow ( epicsPriorityNew, &priorityOfEvents ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + priorityOfEvents = epicsPriorityNew; + } + + if ( epicsPriorityNew > epicsPrioritySelf ) { + epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsPriorityNew ); + db_event_change_priority ( client->evuser, priorityOfEvents ); + } + else { + db_event_change_priority ( client->evuser, priorityOfEvents ); + epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsPriorityNew ); + } + client->priority = mp->m_dataType; + } + return RSRV_OK; +} + +/* + * tcp_echo_action() + */ +static int tcp_echo_action ( caHdrLargeArray *mp, + void *pPayload, struct client *pClient ) +{ + char *pPayloadOut; + int status; + SEND_LOCK ( pClient ); + status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, + ( void * ) &pPayloadOut ); + if ( status == ECA_NORMAL ) { + memcpy ( pPayloadOut, pPayload, mp->m_postsize ); + cas_commit_msg ( pClient, mp->m_postsize ); + } + SEND_UNLOCK ( pClient ); + return RSRV_OK; +} + +/* + * events_on_action () + */ +static int events_on_action ( caHdrLargeArray *mp, + void *pPayload, struct client *pClient ) +{ + db_event_flow_ctrl_mode_off ( pClient->evuser ); + return RSRV_OK; +} + +/* + * events_off_action () + */ +static int events_off_action ( caHdrLargeArray *mp, + void *pPayload, struct client *pClient ) +{ + db_event_flow_ctrl_mode_on ( pClient->evuser ); + return RSRV_OK; +} + +/* + * no_read_access_event() + * + * !! LOCK needs to applied by caller !! + * + * substantial complication introduced here by the need for backwards + * compatibility + */ +static void no_read_access_event ( struct client *pClient, + struct event_ext *pevext ) +{ + char *pPayloadOut; + int status; + + /* + * continue to return an exception + * on failure to pre v41 clients + */ + if ( ! CA_V41 ( pClient->minor_version_number ) ) { + send_err ( &pevext->msg, ECA_GETFAIL, pClient, + RECORD_NAME ( &pevext->pciu->addr ) ); + return; + } + + /* + * New clients recv the status of the + * operation directly to the + * event/put/get callback. + * + * Fetched value is zerod in case they + * use it even when the status indicates + * failure. + * + * The m_cid field in the protocol + * header is abused to carry the status + */ + status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, + pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, + pevext->msg.m_available, ( void * ) &pPayloadOut ); + if ( status == ECA_NORMAL ) { + memset ( pPayloadOut, 0, pevext->size ); + cas_commit_msg ( pClient, pevext->size ); + } + else { + send_err ( &pevext->msg, status, pClient, + "server unable to load read access denied response into protocol buffer PV=\"%s max bytes=%u\"", + RECORD_NAME ( &pevext->pciu->addr ), rsrvSizeofLargeBufTCP ); + } +} + +/* + * read_reply() + */ +static void read_reply ( void *pArg, struct dbAddr *paddr, + int eventsRemaining, db_field_log *pfl ) +{ + ca_uint32_t cid; + void *pPayload; + struct event_ext *pevext = pArg; + struct client *pClient = pevext->pciu->client; + struct channel_in_use *pciu = pevext->pciu; + const int readAccess = asCheckGet ( pciu->asClientPVT ); + int status; + int v41; + int autosize; + long item_count; + ca_uint32_t payload_size; + + SEND_LOCK ( pClient ); + + /* + * New clients recv the status of the + * operation directly to the + * event/put/get callback. + * + * The m_cid field in the protocol + * header is abused to carry the status, + * but get calls still use the + * m_cid field to identify the channel + */ + v41 = CA_V41 ( pClient->minor_version_number ); + if ( v41 ) { + cid = ECA_NORMAL; + } + else { + cid = pciu->cid; + } + + /* If the client has requested a zero element count we interpret this as a + * request for all avaiable elements. In this case we initialise the + * header with the maximum element size specified by the database. */ + autosize = pevext->msg.m_count == 0; + item_count = + autosize ? paddr->no_elements : pevext->msg.m_count; + payload_size = dbr_size_n(pevext->msg.m_dataType, item_count); + status = cas_copy_in_header( + pClient, pevext->msg.m_cmmd, payload_size, + pevext->msg.m_dataType, item_count, cid, pevext->msg.m_available, + &pPayload ); + if ( status != ECA_NORMAL ) { + send_err ( &pevext->msg, status, pClient, + "server unable to load read (or subscription update) response " + "into protocol buffer PV=\"%s\" max bytes=%u", + RECORD_NAME ( paddr ), rsrvSizeofLargeBufTCP ); + if ( ! eventsRemaining ) + cas_send_bs_msg ( pClient, FALSE ); + SEND_UNLOCK ( pClient ); + return; + } + + /* + * verify read access + */ + if ( ! readAccess ) { + no_read_access_event ( pClient, pevext ); + if ( ! eventsRemaining ) + cas_send_bs_msg ( pClient, FALSE ); + SEND_UNLOCK ( pClient ); + return; + } + + status = db_get_field_and_count( + paddr, pevext->msg.m_dataType, pPayload, &item_count, pfl); + if ( status < 0 ) { + /* + * I cant wait to redesign this protocol from scratch! + */ + if ( ! v41 ) { + /* + * old client & plain get + * continue to return an exception + * on failure + */ + send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( paddr ) ); + } + else { + /* New clients recv the status of the operation directly to the + * event/put/get callback. + * + * Fetched value is set to zero in case they use it even when the + * status indicates failure -- unless the client selected autosizing + * data, in which case they'd better know what they're doing! + * + * The m_cid field in the protocol header is abused to carry the + * status */ + if (autosize) { + payload_size = dbr_size_n(pevext->msg.m_dataType, 0); + cas_set_header_count(pClient, 0); + } + memset ( pPayload, 0, payload_size ); + cas_set_header_cid ( pClient, ECA_GETFAIL ); + cas_commit_msg ( pClient, payload_size ); + } + } + else { + int cacStatus = caNetConvert ( + pevext->msg.m_dataType, pPayload, pPayload, + TRUE /* host -> net format */, item_count ); + if ( cacStatus == ECA_NORMAL ) { + ca_uint32_t data_size = + dbr_size_n(pevext->msg.m_dataType, item_count); + if (autosize) { + payload_size = data_size; + cas_set_header_count(pClient, item_count); + } + else if (payload_size > data_size) + memset( + (char *) pPayload + data_size, 0, payload_size - data_size); + } + else { + if (autosize) { + payload_size = dbr_size_n(pevext->msg.m_dataType, 0); + cas_set_header_count(pClient, 0); + } + memset ( pPayload, 0, payload_size ); + cas_set_header_cid ( pClient, cacStatus ); + } + cas_commit_msg ( pClient, payload_size ); + } + + /* + * Ensures timely response for events, but does queue + * them up like db requests when the OPI does not keep up. + */ + if ( ! eventsRemaining ) + cas_send_bs_msg ( pClient, FALSE ); + + SEND_UNLOCK ( pClient ); + + return; +} + +/* + * read_action () + */ +static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *pClient ) +{ + struct channel_in_use *pciu = MPTOPCIU ( mp ); + const int readAccess = asCheckGet ( pciu->asClientPVT ); + ca_uint32_t payloadSize; + void *pPayload; + int status; + int v41; + + if ( ! pciu ) { + logBadId ( pClient, mp, 0 ); + return RSRV_ERROR; + } + + SEND_LOCK ( pClient ); + + if ( INVALID_DB_REQ ( mp->m_dataType ) ) { + send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + return RSRV_ERROR; + } + + payloadSize = dbr_size_n ( mp->m_dataType, mp->m_count ); + status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, + mp->m_dataType, mp->m_count, pciu->cid, mp->m_available, &pPayload ); + if ( status != ECA_NORMAL ) { + send_err ( mp, status, pClient, + "server unable to load read response into protocol buffer PV=\"%s\" max bytes=%u", + RECORD_NAME ( &pciu->addr ), rsrvSizeofLargeBufTCP ); + SEND_UNLOCK ( pClient ); + return RSRV_OK; + } + + /* + * verify read access + */ + v41 = CA_V41 ( pClient->minor_version_number ); + if ( ! readAccess ) { + if ( v41 ) { + status = ECA_NORDACCESS; + } + else{ + status = ECA_GETFAIL; + } + send_err ( mp, status, + pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + return RSRV_OK; + } + + status = db_get_field ( &pciu->addr, mp->m_dataType, + pPayload, mp->m_count, 0 ); + if ( status < 0 ) { + send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + return RSRV_OK; + } + + status = caNetConvert ( + mp->m_dataType, pPayload, pPayload, + TRUE /* host -> net format */, mp->m_count ); + if ( status != ECA_NORMAL ) { + send_err ( mp, status, pClient, RECORD_NAME ( &pciu->addr ) ); + SEND_UNLOCK ( pClient ); + return RSRV_OK; + } + + /* + * force string message size to be the true size rounded to even + * boundary + */ + if ( mp->m_dataType == DBR_STRING && mp->m_count == 1 ) { + char * pStr = (char *) pPayload; + size_t strcnt = strlen ( pStr ); + if ( strcnt < payloadSize ) { + payloadSize = ( ca_uint32_t ) ( strcnt + 1u ); + } + else { + pStr[payloadSize-1] = '\0'; + errlogPrintf ( + "caserver: read_action: detected DBR_STRING w/o nill termination " + "in response from db_get_field, pPayload = \"%s\"\n", + pStr ); + } + } + cas_commit_msg ( pClient, payloadSize ); + + SEND_UNLOCK ( pClient ); + + return RSRV_OK; +} + +/* + * read_notify_action() + */ +static int read_notify_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ + struct channel_in_use *pciu; + struct event_ext evext; + + pciu = MPTOPCIU ( mp ); + if ( !pciu ) { + logBadId ( client, mp, pPayload ); + return RSRV_ERROR; + } + + evext.msg = *mp; + evext.pciu = pciu; + evext.pdbev = NULL; + evext.size = dbr_size_n ( mp->m_dataType, mp->m_count ); + + /* + * Arguments to this routine organized in + * favor of the standard db event calling + * mechanism- routine(userarg, paddr). See + * events added above. + * + * Hold argument set true so the send message + * buffer is not flushed once each call. + */ + read_reply ( &evext, &pciu->addr, TRUE, NULL ); + + return RSRV_OK; +} + +/* + * write_action() + */ +static int write_action ( caHdrLargeArray *mp, + void *pPayload, struct client *client ) +{ + struct channel_in_use *pciu; + int v41; + int status; + long dbStatus; + void *asWritePvt; + + pciu = MPTOPCIU(mp); + if(!pciu){ + logBadId(client, mp, pPayload); + return RSRV_ERROR; + } + + if(!rsrvCheckPut(pciu)){ + v41 = CA_V41(client->minor_version_number); + if(v41){ + status = ECA_NOWTACCESS; + } + else{ + status = ECA_PUTFAIL; + } + SEND_LOCK(client); + send_err( + mp, + status, + client, + RECORD_NAME(&pciu->addr)); + SEND_UNLOCK(client); + return RSRV_OK; + } + + status = caNetConvert ( + mp->m_dataType, pPayload, pPayload, + FALSE /* net -> host format */, mp->m_count ); + if ( status != ECA_NORMAL ) { + log_header ("invalid data type", client, mp, pPayload, 0); + SEND_LOCK(client); + send_err( + mp, + status, + client, + RECORD_NAME(&pciu->addr)); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, + pciu->client->pUserName ? pciu->client->pUserName : "", + pciu->client->pHostName ? pciu->client->pHostName : "", + (void *) &pciu->addr ); + + dbStatus = db_put_field( + &pciu->addr, + mp->m_dataType, + pPayload, + mp->m_count); + + asTrapWriteAfter(asWritePvt); + + if (dbStatus < 0) { + SEND_LOCK(client); + send_err( + mp, + ECA_PUTFAIL, + client, + RECORD_NAME(&pciu->addr)); + SEND_UNLOCK(client); + } + + return RSRV_OK; +} + +/* + * host_name_action() + */ +static int host_name_action ( caHdrLargeArray *mp, void *pPayload, + struct client *client ) +{ + unsigned size; + char *pName; + char *pMalloc; + int chanCount; + + epicsMutexMustLock ( client->chanListLock ); + chanCount = + ellCount ( &client->chanList ) + + ellCount ( &client->chanPendingUpdateARList ); + epicsMutexUnlock( client->chanListLock ); + + if ( chanCount != 0 ) { + SEND_LOCK ( client ); + send_err( + mp, + ECA_INTERNAL, + client, + "attempts to use protocol to set host name " + "after creating first channel ignored by server" ); + SEND_UNLOCK ( client ); + return RSRV_OK; + } + + pName = (char *) pPayload; + size = strlen(pName)+1; + if (size > 512) { + log_header ( "bad (very long) host name", + client, mp, pPayload, 0 ); + SEND_LOCK(client); + send_err( + mp, + ECA_INTERNAL, + client, + "bad (very long) host name"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + /* + * user name will not change if there isnt enough memory + */ + pMalloc = malloc(size); + if(!pMalloc){ + log_header ( "no space in pool for new host name", + client, mp, pPayload, 0 ); + SEND_LOCK(client); + send_err( + mp, + ECA_ALLOCMEM, + client, + "no space in pool for new host name"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + strncpy( + pMalloc, + pName, + size-1); + pMalloc[size-1]='\0'; + + pName = client->pHostName; + client->pHostName = pMalloc; + if ( pName ) { + free ( pName ); + } + + DLOG (2, ( "CAS: host_name_action for \"%s\"\n", + client->pHostName ? client->pHostName : "" ) ); + + return RSRV_OK; +} + + +/* + * client_name_action() + */ +static int client_name_action ( caHdrLargeArray *mp, void *pPayload, + struct client *client ) +{ + unsigned size; + char *pName; + char *pMalloc; + int chanCount; + + epicsMutexMustLock ( client->chanListLock ); + chanCount = + ellCount ( &client->chanList ) + + ellCount ( &client->chanPendingUpdateARList ); + epicsMutexUnlock( client->chanListLock ); + + if ( chanCount != 0 ) { + SEND_LOCK ( client ); + send_err( + mp, + ECA_INTERNAL, + client, + "attempts to use protocol to set user name " + "after creating first channel ignored by server" ); + SEND_UNLOCK ( client ); + return RSRV_OK; + } + + pName = (char *) pPayload; + size = strlen(pName)+1; + if (size > 512) { + log_header ("a very long user name was specified", + client, mp, pPayload, 0); + SEND_LOCK(client); + send_err( + mp, + ECA_INTERNAL, + client, + "a very long user name was specified"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + /* + * user name will not change if there isnt enough memory + */ + pMalloc = malloc(size); + if(!pMalloc){ + log_header ("no memory for new user name", + client, mp, pPayload, 0); + SEND_LOCK(client); + send_err( + mp, + ECA_ALLOCMEM, + client, + "no memory for new user name"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + strncpy( + pMalloc, + pName, + size-1); + pMalloc[size-1]='\0'; + + pName = client->pUserName; + client->pUserName = pMalloc; + if ( pName ) { + free ( pName ); + } + + return RSRV_OK; +} + +/* + * casCreateChannel () + */ +static struct channel_in_use *casCreateChannel ( +struct client *client, +struct dbAddr *pAddr, +unsigned cid +) +{ + static unsigned bucketID; + unsigned *pCID; + struct channel_in_use *pchannel; + int status; + + /* get block off free list if possible */ + pchannel = (struct channel_in_use *) + freeListCalloc(rsrvChanFreeList); + if (!pchannel) { + return NULL; + } + ellInit(&pchannel->eventq); + epicsTimeGetCurrent(&pchannel->time_at_creation); + pchannel->addr = *pAddr; + pchannel->client = client; + /* + * bypass read only warning + */ + pCID = (unsigned *) &pchannel->cid; + *pCID = cid; + + /* + * allocate a server id and enter the channel pointer + * in the table + * + * NOTE: This detects the case where the PV id wraps + * around and we attempt to have two resources on the same id. + * The lock is applied here because on some architectures the + * ++ operator isnt atomic. + */ + LOCK_CLIENTQ; + + do { + /* + * bypass read only warning + */ + pCID = (unsigned *) &pchannel->sid; + *pCID = bucketID++; + + /* + * Verify that this id is not in use + */ + status = bucketAddItemUnsignedId ( + pCaBucket, + &pchannel->sid, + pchannel); + } while (status == S_bucket_idInUse); + + if ( status == S_bucket_success ) { + rsrvChannelCount++; + } + + UNLOCK_CLIENTQ; + + if(status!=S_bucket_success){ + freeListFree(rsrvChanFreeList, pchannel); + errMessage (status, "Unable to allocate server id"); + return NULL; + } + + epicsMutexMustLock( client->chanListLock ); + pchannel->state = rsrvCS_pendConnectResp; + ellAdd ( &client->chanList, &pchannel->node ); + epicsMutexUnlock ( client->chanListLock ); + + return pchannel; +} + +/* + * casAccessRightsCB() + * + * If access right state changes then inform the client. + * + */ +static void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type) +{ + struct client * pclient; + struct channel_in_use * pciu; + struct event_ext * pevext; + + pciu = asGetClientPvt(ascpvt); + assert(pciu); + + pclient = pciu->client; + assert(pclient); + + if(pclient == prsrv_cast_client){ + return; + } + + switch(type) + { + case asClientCOAR: + { + const int readAccess = asCheckGet ( pciu->asClientPVT ); + unsigned sigReq = 0; + + epicsMutexMustLock ( pclient->chanListLock ); + if ( pciu->state == rsrvCS_pendConnectResp ) { + ellDelete ( &pclient->chanList, &pciu->node ); + pciu->state = rsrvCS_pendConnectRespUpdatePendAR; + ellAdd ( &pclient->chanPendingUpdateARList, &pciu->node ); + sigReq = 1; + } + else if ( pciu->state == rsrvCS_inService ) { + ellDelete ( &pclient->chanList, &pciu->node ); + pciu->state = rsrvCS_inServiceUpdatePendAR; + ellAdd ( &pclient->chanPendingUpdateARList, &pciu->node ); + sigReq = 1; + } + epicsMutexUnlock ( pclient->chanListLock ); + + /* + * Update all event call backs + */ + epicsMutexMustLock(pclient->eventqLock); + for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); + pevext; + pevext = (struct event_ext *) ellNext(&pevext->node)){ + + if ( pevext->pdbev ) { + if ( readAccess ){ + db_event_enable ( pevext->pdbev ); + db_post_single_event ( pevext->pdbev ); + } + else { + db_post_single_event ( pevext->pdbev ); + db_event_disable ( pevext->pdbev ); + } + } + } + epicsMutexUnlock(pclient->eventqLock); + + if ( sigReq ) { + db_post_extra_labor( pclient->evuser ); + } + + break; + } + default: + break; + } +} + +/* + * access_rights_reply() + */ +static void access_rights_reply ( struct channel_in_use * pciu ) +{ + unsigned ar; + int v41; + int status; + + assert ( pciu->client != prsrv_cast_client ); + + /* + * noop if this is an old client + */ + v41 = CA_V41 ( pciu->client->minor_version_number ); + if ( ! v41 ){ + return; + } + + ar = 0; /* none */ + if ( asCheckGet ( pciu->asClientPVT ) ) { + ar |= CA_PROTO_ACCESS_RIGHT_READ; + } + if ( rsrvCheckPut ( pciu ) ) { + ar |= CA_PROTO_ACCESS_RIGHT_WRITE; + } + + SEND_LOCK ( pciu->client ); + status = cas_copy_in_header ( + pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, + 0, 0, pciu->cid, ar, 0 ); + /* + * OK to just ignore the request if the connection drops + */ + if ( status == ECA_NORMAL ) { + cas_commit_msg ( pciu->client, 0u ); + } + SEND_UNLOCK ( pciu->client ); +} + +/* + * claim_ciu_reply() + */ +static void claim_ciu_reply ( struct channel_in_use * pciu ) +{ + int v42 = CA_V42 ( pciu->client->minor_version_number ); + access_rights_reply ( pciu ); + if ( v42 ) { + int status; + ca_uint32_t nElem; + SEND_LOCK ( pciu->client ); + if ( pciu->addr.no_elements < 0 ) { + nElem = 0; + } + else { + if ( ! CA_V49 ( pciu->client->minor_version_number ) ) { + if ( pciu->addr.no_elements >= 0xffff ) { + nElem = 0xfffe; + } + else { + nElem = (ca_uint32_t) pciu->addr.no_elements; + } + } + else { + nElem = (ca_uint32_t) pciu->addr.no_elements; + } + } + status = cas_copy_in_header ( + pciu->client, CA_PROTO_CREATE_CHAN, 0u, + pciu->addr.dbr_field_type, nElem, pciu->cid, + pciu->sid, NULL ); + if ( status == ECA_NORMAL ) { + cas_commit_msg ( pciu->client, 0u ); + } + SEND_UNLOCK(pciu->client); + } +} + +/* + * claim_ciu_action() + */ +static int claim_ciu_action ( caHdrLargeArray *mp, + void *pPayload, client *client ) +{ + int status; + struct channel_in_use *pciu; + + /* + * The available field is used (abused) + * here to communicate the miner version number + * starting with CA 4.1. The field was set to zero + * prior to 4.1 + */ + client->minor_version_number = mp->m_available; + + if (CA_V44(client->minor_version_number)) { + struct dbAddr tmp_addr; + char *pName = (char *) pPayload; + + /* + * check the sanity of the message + */ + if (mp->m_postsize<=1) { + log_header ( "empty PV name in UDP search request?", + client, mp, pPayload, 0 ); + return RSRV_OK; + } + pName[mp->m_postsize-1] = '\0'; + + status = db_name_to_addr (pName, &tmp_addr); + if (status) { + return RSRV_OK; + } + + DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", + pName, tmp_addr.dbr_field_type, tmp_addr.no_elements) ); + + pciu = casCreateChannel ( + client, + &tmp_addr, + mp->m_cid); + if (!pciu) { + log_header ("no memory to create new channel", + client, mp, pPayload, 0); + SEND_LOCK(client); + send_err(mp, + ECA_ALLOCMEM, + client, + RECORD_NAME(&tmp_addr)); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + } + else { + epicsMutexMustLock(prsrv_cast_client->chanListLock); + /* + * clients which dont claim their + * channel in use block prior to + * timeout must reconnect + */ + pciu = MPTOPCIU(mp); + if(!pciu){ + errlogPrintf("CAS: client timeout disconnect id=%d\n", + mp->m_cid); + epicsMutexUnlock(prsrv_cast_client->chanListLock); + SEND_LOCK(client); + send_err( + mp, + ECA_INTERNAL, + client, + "old connect protocol timed out"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + /* + * duplicate claim message are unacceptable + * (so we disconnect the client) + */ + if (pciu->client!=prsrv_cast_client) { + errlogPrintf("CAS: duplicate claim disconnect id=%d\n", + mp->m_cid); + epicsMutexUnlock(prsrv_cast_client->chanListLock); + SEND_LOCK(client); + send_err( + mp, + ECA_INTERNAL, + client, + "duplicate claim in old connect protocol"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + /* + * remove channel in use block from + * the UDP client where it could time + * out and place it on the client + * who is claiming it + */ + ellDelete( + &prsrv_cast_client->chanList, + &pciu->node); + epicsMutexUnlock(prsrv_cast_client->chanListLock); + + epicsMutexMustLock(client->chanListLock); + pciu->state = rsrvCS_pendConnectResp; + pciu->client = client; + ellAdd(&client->chanList, &pciu->node); + epicsMutexUnlock(client->chanListLock); + } + + /* + * set up access security for this channel + */ + status = asAddClient( + &pciu->asClientPVT, + asDbGetMemberPvt(&pciu->addr), + asDbGetAsl(&pciu->addr), + client->pUserName ? client->pUserName : "", + client->pHostName ? client->pHostName : ""); + if(status != 0 && status != S_asLib_asNotActive){ + log_header ("No room for security table", + client, mp, pPayload, 0); + SEND_LOCK(client); + send_err(mp, ECA_ALLOCMEM, client, "No room for security table"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + /* + * store ptr to channel in use block + * in access security private + */ + asPutClientPvt(pciu->asClientPVT, pciu); + + /* + * register for asynch updates of access rights changes + */ + status = asRegisterClientCallback( + pciu->asClientPVT, + casAccessRightsCB); + if ( status == S_asLib_asNotActive ) { + epicsMutexMustLock ( client->chanListLock ); + pciu->state = rsrvCS_inService; + epicsMutexUnlock ( client->chanListLock ); + /* + * force the initial AR update followed by claim response + */ + claim_ciu_reply ( pciu ); + } + else if (status!=0) { + log_header ("No room for access security state change subscription", + client, mp, pPayload, 0); + SEND_LOCK(client); + send_err(mp, ECA_ALLOCMEM, client, + "No room for access security state change subscription"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + return RSRV_OK; +} + +/* + * write_notify_call_back() + * + * (called by the db call back thread) + */ +static void write_notify_call_back(putNotify *ppn) +{ + struct channel_in_use * pciu = (struct channel_in_use *) ppn->usrPvt; + struct client * pClient; + + /* + * independent lock used here in order to + * avoid any possibility of blocking + * the database (or indirectly blocking + * one client on another client). + */ + assert(pciu); + assert(pciu->pPutNotify); + pClient = pciu->client; + epicsMutexMustLock(pClient->putNotifyLock); + + if ( ! pciu->pPutNotify->busy || pciu->pPutNotify->onExtraLaborQueue ) { + epicsMutexUnlock(pClient->putNotifyLock); + errlogPrintf("Double DB put notify call back!!\n"); + return; + } + + ellAdd(&pClient->putNotifyQue, &pciu->pPutNotify->node); + pciu->pPutNotify->onExtraLaborQueue = TRUE; + + epicsMutexUnlock(pClient->putNotifyLock); + + /* + * offload the labor for this to the + * event task so that we never block + * the db or another client. + */ + db_post_extra_labor(pClient->evuser); +} + +/* + * write_notify_reply() + * (called by the CA server event task via the extra labor interface) + */ +static void write_notify_reply ( struct client * pClient ) +{ + while(TRUE){ + caHdrLargeArray msgtmp; + void * asWritePvtTmp; + ca_uint32_t status; + int localStatus; + + /* + * independent lock used here in order to + * avoid any possibility of blocking + * the database (or indirectly blocking + * one client on another client). + */ + epicsMutexMustLock(pClient->putNotifyLock); + { + RSRVPUTNOTIFY * ppnb = (RSRVPUTNOTIFY *) + ellGet ( &pClient->putNotifyQue ); + if ( ! ppnb ) { + epicsMutexUnlock(pClient->putNotifyLock); + break; + } + /* + * + * Map from DB status to CA status + * + */ + if ( ppnb->dbPutNotify.status != putNotifyOK ) { + status = ECA_PUTFAIL; + } + else{ + status = ECA_NORMAL; + } + msgtmp = ppnb->msg; + asWritePvtTmp = ppnb->asWritePvt; + ppnb->asWritePvt = 0; + ppnb->onExtraLaborQueue = FALSE; + ppnb->busy = FALSE; + } + epicsMutexUnlock(pClient->putNotifyLock); + + asTrapWriteAfter ( asWritePvtTmp ); + + /* + * the channel id field is being abused to carry + * status here + */ + SEND_LOCK(pClient); + localStatus = cas_copy_in_header ( + pClient, CA_PROTO_WRITE_NOTIFY, + 0u, msgtmp.m_dataType, msgtmp.m_count, status, + msgtmp.m_available, 0 ); + if ( localStatus != ECA_NORMAL ) { + /* + * inability to aquire buffer space + * Indicates corruption + */ + errlogPrintf("CA server corrupted - put call back(s) discarded\n"); + SEND_UNLOCK ( pClient ); + break; + } + + /* commit the message */ + cas_commit_msg ( pClient, 0u ); + SEND_UNLOCK ( pClient ); + } + + /* + * wakeup the TCP thread if it is waiting for a cb to complete + */ + epicsEventSignal ( pClient->blockSem ); +} + +/* + * sendAllUpdateAS() + */ +static void sendAllUpdateAS ( struct client *client ) +{ + struct channel_in_use *pciu; + + epicsMutexMustLock ( client->chanListLock ); + + pciu = ( struct channel_in_use * ) + ellGet ( & client->chanPendingUpdateARList ); + while ( pciu ) { + if ( pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) { + claim_ciu_reply ( pciu ); + } + else if ( pciu->state == rsrvCS_inServiceUpdatePendAR ) { + access_rights_reply ( pciu ); + } + else { + errlogPrintf ( + "%s at %d: corrupt channel state detected durring AR update\n", + __FILE__, __LINE__); + } + pciu->state = rsrvCS_inService; + ellAdd ( & client->chanList, & pciu->node ); + pciu = ( struct channel_in_use * ) + ellGet ( & client->chanPendingUpdateARList ); + } + + epicsMutexUnlock( client->chanListLock ); +} + +/* + * rsrv_extra_labor() + * (called by the CA server event task via the extra labor interface) + */ +void rsrv_extra_labor ( void * pArg ) +{ + struct client * pClient = pArg; + write_notify_reply ( pClient ); + sendAllUpdateAS ( pClient ); + cas_send_bs_msg ( pClient, TRUE ); +} + +/* + * putNotifyErrorReply + */ +static void putNotifyErrorReply ( struct client *client, caHdrLargeArray *mp, int statusCA ) +{ + int status; + + SEND_LOCK ( client ); + /* + * the cid field abused to contain status + * during put cb replies + */ + status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, + 0u, mp->m_dataType, mp->m_count, statusCA, + mp->m_available, 0 ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK ( client ); + errlogPrintf ("%s at %d: should always get sufficent space for put notify error reply\n", + __FILE__, __LINE__); + return; + } + cas_commit_msg ( client, 0u ); + SEND_UNLOCK ( client ); +} + +void initializePutNotifyFreeList (void) +{ + if ( ! rsrvPutNotifyFreeList ) { + freeListInitPvt ( &rsrvPutNotifyFreeList, + sizeof(struct rsrv_put_notify), 512 ); + assert ( rsrvPutNotifyFreeList ); + } +} + +static struct rsrv_put_notify * + rsrvAllocPutNotify ( struct channel_in_use * pciu ) +{ + struct rsrv_put_notify *pNotify; + + if ( rsrvPutNotifyFreeList ) { + pNotify = (RSRVPUTNOTIFY *) + freeListCalloc ( rsrvPutNotifyFreeList ); + if ( pNotify ) { + pNotify->dbPutNotify.pbuffer = + &pNotify->dbrScalarValue; + pNotify->valueSize = + sizeof (pNotify->dbrScalarValue); + pNotify->dbPutNotify.usrPvt = pciu; + pNotify->dbPutNotify.paddr = &pciu->addr; + pNotify->dbPutNotify.userCallback = + write_notify_call_back; + } + } + else { + pNotify = NULL; + } + return pNotify; +} + +static int rsrvExpandPutNotify ( + struct rsrv_put_notify * pNotify, unsigned sizeNeeded ) +{ + int booleanStatus; + + if ( sizeNeeded > pNotify->valueSize ) { + /* + * try to use the union embeded in the free list + * item, but allocate a random sized block if they + * writing a vector. + */ + if ( pNotify->valueSize > + sizeof (pNotify->dbrScalarValue) ) { + free ( pNotify->dbPutNotify.pbuffer ); + } + pNotify->dbPutNotify.pbuffer = casCalloc(1,sizeNeeded); + if ( pNotify->dbPutNotify.pbuffer ) { + pNotify->valueSize = sizeNeeded; + booleanStatus = TRUE; + } + else { + /* + * revert back to the embedded union + */ + pNotify->dbPutNotify.pbuffer = + &pNotify->dbrScalarValue; + pNotify->valueSize = + sizeof (pNotify->dbrScalarValue); + booleanStatus = FALSE; + } + } + else { + booleanStatus = TRUE; + } + + return booleanStatus; +} + +unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ) +{ + unsigned size = sizeof ( *pNotify ); + if ( pNotify ) { + if ( pNotify->valueSize > + sizeof ( pNotify->dbrScalarValue ) ) { + size += pNotify->valueSize; + } + } + return size; +} + +void rsrvFreePutNotify ( client *pClient, + struct rsrv_put_notify *pNotify ) +{ + if ( pNotify ) { + char busyTmp; + void * asWritePvtTmp = 0; + + epicsMutexMustLock ( pClient->putNotifyLock ); + busyTmp = pNotify->busy; + epicsMutexUnlock ( pClient->putNotifyLock ); + + /* + * if any possiblity that the put notify is + * outstanding then cancel it + */ + if ( busyTmp ) { + dbNotifyCancel ( &pNotify->dbPutNotify ); + } + + epicsMutexMustLock ( pClient->putNotifyLock ); + if ( pNotify->onExtraLaborQueue ) { + ellDelete ( &pClient->putNotifyQue, + &pNotify->node ); + } + busyTmp = pNotify->busy; + asWritePvtTmp = pNotify->asWritePvt; + pNotify->asWritePvt = 0; + epicsMutexUnlock ( pClient->putNotifyLock ); + + if ( busyTmp ) { + asTrapWriteAfter ( asWritePvtTmp ); + } + + if ( pNotify->valueSize > + sizeof(pNotify->dbrScalarValue) ) { + free ( pNotify->dbPutNotify.pbuffer ); + } + freeListFree ( rsrvPutNotifyFreeList, pNotify ); + } +} + +/* + * write_notify_action() + */ +static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, + struct client *client ) +{ + unsigned size; + int status; + struct channel_in_use *pciu; + + pciu = MPTOPCIU(mp); + if(!pciu){ + logBadId ( client, mp, pPayload ); + return RSRV_ERROR; + } + + if (mp->m_dataType > LAST_BUFFER_TYPE) { + log_header ("bad put notify data type", client, mp, pPayload, 0); + putNotifyErrorReply (client, mp, ECA_BADTYPE); + return RSRV_ERROR; + } + + if(!rsrvCheckPut(pciu)){ + putNotifyErrorReply (client, mp, ECA_NOWTACCESS); + return RSRV_OK; + } + + size = dbr_size_n (mp->m_dataType, mp->m_count); + + if ( pciu->pPutNotify ) { + + /* + * serialize concurrent put notifies + */ + epicsMutexMustLock(client->putNotifyLock); + while(pciu->pPutNotify->busy){ + epicsMutexUnlock(client->putNotifyLock); + status = epicsEventWaitWithTimeout(client->blockSem,60.0); + if ( status != epicsEventWaitOK ) { + char busyTmp; + void * asWritePvtTmp = 0; + + epicsMutexMustLock(client->putNotifyLock); + busyTmp = pciu->pPutNotify->busy; + epicsMutexUnlock(client->putNotifyLock); + + /* + * if any possibility of put notify still running + * then cancel it + */ + if ( busyTmp ) { + dbNotifyCancel(&pciu->pPutNotify->dbPutNotify); + } + epicsMutexMustLock(client->putNotifyLock); + busyTmp = pciu->pPutNotify->busy; + if ( busyTmp ) { + if ( pciu->pPutNotify->onExtraLaborQueue ) { + ellDelete ( &client->putNotifyQue, + &pciu->pPutNotify->node ); + } + pciu->pPutNotify->busy = FALSE; + asWritePvtTmp = pciu->pPutNotify->asWritePvt; + pciu->pPutNotify->asWritePvt = 0; + } + epicsMutexUnlock(client->putNotifyLock); + + if ( busyTmp ) { + log_header("put call back time out", client, + &pciu->pPutNotify->msg, pciu->pPutNotify->dbPutNotify.pbuffer, 0); + asTrapWriteAfter ( asWritePvtTmp ); + putNotifyErrorReply (client, &pciu->pPutNotify->msg, ECA_PUTCBINPROG); + } + } + epicsMutexMustLock(client->putNotifyLock); + } + epicsMutexUnlock(client->putNotifyLock); + } + else { + pciu->pPutNotify = rsrvAllocPutNotify ( pciu ); + if ( ! pciu->pPutNotify ) { + /* + * send error and go to next request + * if there isnt enough memory left + */ + log_header ( "no memory to initiate put notify", + client, mp, pPayload, 0 ); + putNotifyErrorReply (client, mp, ECA_ALLOCMEM); + return RSRV_ERROR; + } + } + + if ( ! rsrvExpandPutNotify ( pciu->pPutNotify, size ) ) { + log_header ( "no memory to initiate vector put notify", + client, mp, pPayload, 0 ); + putNotifyErrorReply ( client, mp, ECA_ALLOCMEM ); + return RSRV_ERROR; + } + + pciu->pPutNotify->busy = TRUE; + pciu->pPutNotify->onExtraLaborQueue = FALSE; + pciu->pPutNotify->msg = *mp; + pciu->pPutNotify->dbPutNotify.nRequest = mp->m_count; + + status = caNetConvert ( + mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, + FALSE /* net -> host format */, mp->m_count ); + if ( status != ECA_NORMAL ) { + log_header ("invalid data type", client, mp, pPayload, 0); + putNotifyErrorReply ( client, mp, status ); + return RSRV_ERROR; + } + + status = dbPutNotifyMapType(&pciu->pPutNotify->dbPutNotify, mp->m_dataType); + if(status){ + putNotifyErrorReply (client, mp, ECA_PUTFAIL); + pciu->pPutNotify->busy = FALSE; + return RSRV_OK; + } + + pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( + pciu->asClientPVT, + pciu->client->pUserName ? pciu->client->pUserName : "", + pciu->client->pHostName ? pciu->client->pHostName : "", + (void *) &pciu->addr ); + + dbPutNotify(&pciu->pPutNotify->dbPutNotify); + + return RSRV_OK; +} + +/* + * + * event_add_action() + * + */ +static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client *client) +{ + struct mon_info *pmi = (struct mon_info *) pPayload; + int spaceAvailOnFreeList; + struct channel_in_use *pciu; + struct event_ext *pevext; + + pciu = MPTOPCIU ( mp ); + if ( ! pciu ) { + logBadId ( client, mp, pPayload ); + return RSRV_ERROR; + } + + /* + * stop further use of server if memory becomes scarse + */ + spaceAvailOnFreeList = freeListItemsAvail ( rsrvEventFreeList ) > 0; + if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) { + pevext = (struct event_ext *) freeListCalloc (rsrvEventFreeList); + } + else { + pevext = 0; + } + + if (!pevext) { + log_header ("no memory to add subscription", + client, mp, pPayload, 0); + SEND_LOCK(client); + send_err( + mp, + ECA_ALLOCMEM, + client, + RECORD_NAME(&pciu->addr)); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + pevext->msg = *mp; + pevext->pciu = pciu; + pevext->size = dbr_size_n(mp->m_dataType, mp->m_count); + pevext->mask = ntohs ( pmi->m_mask ); + + epicsMutexMustLock(client->eventqLock); + ellAdd( &pciu->eventq, &pevext->node); + epicsMutexUnlock(client->eventqLock); + + pevext->pdbev = db_add_event (client->evuser, &pciu->addr, + read_reply, pevext, pevext->mask); + if (pevext->pdbev == NULL) { + log_header ("no memory to add subscription to db", + client, mp, pPayload, 0); + SEND_LOCK(client); + send_err (mp, ECA_ALLOCMEM, client, + "subscription install into record %s failed", + RECORD_NAME(&pciu->addr)); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + /* + * always send it once at event add + */ + /* + * if the client program issues many monitors + * in a row then I recv when the send side + * of the socket would block. This prevents + * a application program initiated deadlock. + * + * However when I am reconnecting I reissue + * the monitors and I could get deadlocked. + * The client is blocked sending and the server + * task for the client is blocked sending in + * this case. I cant check the recv part of the + * socket in the client since I am still handling an + * outstanding recv ( they must be processed in order). + * I handle this problem in the server by using + * post_single_event() below instead of calling + * read_reply() in this module. This is a complete + * fix since a monitor setup is the only request + * soliciting a reply in the client which is + * issued from inside of service.c (from inside + * of the part of the ca client which services + * messages sent by the server). + */ + + DLOG ( 3, ("event_add_action: db_post_single_event (0x%X)\n", + pevext->pdbev) ); + db_post_single_event(pevext->pdbev); + + /* + * enable future labor if we have read access + */ + if(asCheckGet(pciu->asClientPVT)){ + db_event_enable(pevext->pdbev); + } + else { + DLOG ( 3, ( "Disable event because cannot read\n" ) ); + } + + return RSRV_OK; +} + +/* + * clear_channel_reply() + */ +static int clear_channel_reply ( caHdrLargeArray *mp, + void *pPayload, struct client *client ) +{ + struct event_ext *pevext; + struct channel_in_use *pciu; + int status; + + /* + * + * Verify the channel + * + */ + pciu = MPTOPCIU(mp); + if(pciu?pciu->client!=client:TRUE){ + logBadId ( client, mp, pPayload ); + return RSRV_ERROR; + } + + rsrvFreePutNotify ( client, pciu->pPutNotify ); + + while (TRUE){ + epicsMutexMustLock(client->eventqLock); + pevext = (struct event_ext *) ellGet(&pciu->eventq); + epicsMutexUnlock(client->eventqLock); + + if(!pevext){ + break; + } + + if (pevext->pdbev) { + db_cancel_event (pevext->pdbev); + } + freeListFree(rsrvEventFreeList, pevext); + } + + db_flush_extra_labor_event ( client->evuser ); + + /* + * send delete confirmed message + */ + SEND_LOCK(client); + status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, + mp->m_available, NULL ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + cas_commit_msg ( client, 0u ); + SEND_UNLOCK(client); + + epicsMutexMustLock ( client->chanListLock ); + if ( pciu->state == rsrvCS_inService || + pciu->state == rsrvCS_pendConnectResp ) { + ellDelete ( &client->chanList, &pciu->node ); + } + else if ( pciu->state == rsrvCS_inServiceUpdatePendAR || + pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) { + ellDelete ( &client->chanPendingUpdateARList, &pciu->node ); + } + else { + epicsMutexUnlock( client->chanListLock ); + SEND_LOCK(client); + send_err(mp, ECA_INTERNAL, client, + "channel was in strange state or corrupted during cleanup"); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + epicsMutexUnlock( client->chanListLock ); + + /* + * remove from access control list + */ + status = asRemoveClient(&pciu->asClientPVT); + if(status != 0 && status != S_asLib_asNotActive){ + errMessage(status, RECORD_NAME(&pciu->addr)); + return RSRV_ERROR; + } + + LOCK_CLIENTQ; + status = bucketRemoveItemUnsignedId (pCaBucket, &pciu->sid); + if(status != S_bucket_success){ + UNLOCK_CLIENTQ; + errMessage (status, "Bad resource id during channel clear"); + logBadId ( client, mp, pPayload ); + return RSRV_ERROR; + } + rsrvChannelCount--; + UNLOCK_CLIENTQ; + + freeListFree(rsrvChanFreeList, pciu); + + return RSRV_OK; +} + +/* + * + * event_cancel_reply() + * + * + * Much more efficient now since the event blocks hang off the channel in use + * blocks not all together off the client block. + */ +static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ + struct channel_in_use *pciu; + struct event_ext *pevext; + int status; + + /* + * + * Verify the channel + * + */ + pciu = MPTOPCIU(mp); + if (pciu?pciu->client!=client:TRUE) { + logBadId ( client, mp, pPayload ); + return RSRV_ERROR; + } + + /* + * search events on this channel for a match + * (there are usually very few monitors per channel) + */ + epicsMutexMustLock(client->eventqLock); + for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); + pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){ + + if (pevext->msg.m_available == mp->m_available) { + ellDelete(&pciu->eventq, &pevext->node); + break; + } + } + epicsMutexUnlock(client->eventqLock); + + /* + * Not Found- return an exception event + */ + if(!pevext){ + SEND_LOCK(client); + send_err(mp, ECA_BADMONID, client, RECORD_NAME(&pciu->addr)); + SEND_UNLOCK(client); + return RSRV_ERROR; + } + + /* + * cancel monitor activity in progress + */ + if (pevext->pdbev) { + db_cancel_event (pevext->pdbev); + } + + /* + * send delete confirmed message + */ + SEND_LOCK(client); + + status = cas_copy_in_header ( client, pevext->msg.m_cmmd, + 0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, + pevext->msg.m_available, NULL ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK(client); + return RSRV_ERROR; + } + cas_commit_msg ( client, 0 ); + SEND_UNLOCK(client); + + freeListFree (rsrvEventFreeList, pevext); + + return RSRV_OK; +} + +/* + * read_sync_reply() + */ +static int read_sync_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ + int status; + SEND_LOCK(client); + status = cas_copy_in_header ( client, mp->m_cmmd, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, + mp->m_available, NULL ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK(client); + return RSRV_ERROR; + } + cas_commit_msg ( client, 0 ); + SEND_UNLOCK(client); + return RSRV_OK; +} + +/* + * search_fail_reply() + * + * Only when requested by the client + * send search failed reply + */ +static void search_fail_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client) +{ + int status; + SEND_LOCK ( client ); + status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK ( client ); + errlogPrintf ( "%s at %d: should always get sufficent space for search fail reply?\n", + __FILE__, __LINE__ ); + return; + } + cas_commit_msg ( client, 0 ); + SEND_UNLOCK ( client ); +} + +/* + * udp_version_action() + */ +static int udp_version_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ + if ( mp->m_count != 0 ) { + client->minor_version_number = mp->m_count; + if ( CA_V411 ( mp->m_count ) ) { + client->seqNoOfReq = mp->m_cid; + } + else { + client->seqNoOfReq = 0; + } + } + return RSRV_OK; +} + +/* + * rsrv_version_reply() + */ +int rsrv_version_reply ( struct client *client ) +{ + int status; + SEND_LOCK ( client ); + /* + * sequence number is specified zero when we copy in the + * header because we dont know it until we receive a datagram + * from the client + */ + status = cas_copy_in_header ( client, CA_PROTO_VERSION, + 0, 0, CA_MINOR_PROTOCOL_REVISION, + 0, 0, 0 ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK ( client ); + return RSRV_ERROR; + } + cas_commit_msg ( client, 0 ); + SEND_UNLOCK ( client ); + return RSRV_OK; +} + +/* + * search_reply_udp () + */ +static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ + struct dbAddr tmp_addr; + ca_uint16_t *pMinorVersion; + char *pName = (char *) pPayload; + int status; + unsigned sid; + ca_uint16_t count; + ca_uint16_t type; + int spaceAvailOnFreeList; + size_t spaceNeeded; + size_t reasonableMonitorSpace = 10; + + /* + * check the sanity of the message + */ + if (mp->m_postsize<=1) { + log_header ("empty PV name in UDP search request?", + client, mp, pPayload, 0); + return RSRV_OK; + } + pName[mp->m_postsize-1] = '\0'; + + /* Exit quickly if channel not on this node */ + status = db_name_to_addr (pName, &tmp_addr); + if (status) { + DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); + if (mp->m_dataType == DOREPLY) + search_fail_reply ( mp, pPayload, client ); + return RSRV_OK; + } + + /* + * stop further use of server if memory becomes scarse + */ + spaceAvailOnFreeList = freeListItemsAvail ( rsrvChanFreeList ) > 0 + && freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace; + spaceNeeded = sizeof (struct channel_in_use) + + reasonableMonitorSpace * sizeof (struct event_ext); + if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { + SEND_LOCK(client); + send_err ( mp, ECA_ALLOCMEM, client, "Server memory exhausted" ); + SEND_UNLOCK(client); + return RSRV_OK; + } + + /* + * starting with V4.4 the count field is used (abused) + * to store the minor version number of the client. + * + * New versions dont alloc the channel in response + * to a search request. + * + * m_count, m_cid are already in host format... + */ + if (CA_V44(mp->m_count)) { + sid = ~0U; + count = 0; + type = ca_server_port; + } + else { + struct channel_in_use *pchannel; + + pchannel = casCreateChannel ( client, &tmp_addr, mp->m_cid ); + if (!pchannel) { + SEND_LOCK(client); + send_err ( mp, ECA_ALLOCMEM, client, + RECORD_NAME ( &tmp_addr ) ); + SEND_UNLOCK ( client ); + return RSRV_OK; + } + sid = pchannel->sid; + if ( tmp_addr.no_elements < 0 ) { + count = 0; + } + else if ( tmp_addr.no_elements > 0xffff ) { + count = 0xfffe; + } + else { + count = (ca_uint16_t) tmp_addr.no_elements; + } + type = (ca_uint16_t) tmp_addr.dbr_field_type; + } + + SEND_LOCK ( client ); + status = cas_copy_in_header ( client, CA_PROTO_SEARCH, + sizeof(*pMinorVersion), type, count, + sid, mp->m_available, + ( void * ) &pMinorVersion ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK ( client ); + return RSRV_ERROR; + } + + /* + * Starting with CA V4.1 the minor version number + * is appended to the end of each search reply. + * This value is ignored by earlier clients. + */ + *pMinorVersion = htons ( CA_MINOR_PROTOCOL_REVISION ); + + cas_commit_msg ( client, sizeof ( *pMinorVersion ) ); + SEND_UNLOCK ( client ); + + return RSRV_OK; +} + +/* + * search_reply_tcp () + */ +static int search_reply_tcp ( + caHdrLargeArray *mp, void *pPayload, struct client *client ) +{ + struct dbAddr tmp_addr; + char *pName = (char *) pPayload; + int status; + int spaceAvailOnFreeList; + size_t spaceNeeded; + size_t reasonableMonitorSpace = 10; + + /* + * check the sanity of the message + */ + if (mp->m_postsize<=1) { + log_header ("empty PV name in UDP search request?", + client, mp, pPayload, 0); + return RSRV_OK; + } + pName[mp->m_postsize-1] = '\0'; + + /* Exit quickly if channel not on this node */ + status = db_name_to_addr (pName, &tmp_addr); + if (status) { + DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); + if (mp->m_dataType == DOREPLY) + search_fail_reply ( mp, pPayload, client ); + return RSRV_OK; + } + + /* + * stop further use of server if memory becomes scarse + */ + spaceAvailOnFreeList = freeListItemsAvail ( rsrvChanFreeList ) > 0 + && freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace; + spaceNeeded = sizeof (struct channel_in_use) + + reasonableMonitorSpace * sizeof (struct event_ext); + if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { + SEND_LOCK(client); + send_err ( mp, ECA_ALLOCMEM, client, "Server memory exhausted" ); + SEND_UNLOCK(client); + return RSRV_OK; + } + + SEND_LOCK ( client ); + status = cas_copy_in_header ( client, CA_PROTO_SEARCH, + 0, ca_server_port, 0, ~0U, mp->m_available, 0 ); + if ( status != ECA_NORMAL ) { + SEND_UNLOCK ( client ); + return RSRV_ERROR; + } + + cas_commit_msg ( client, 0 ); + SEND_UNLOCK ( client ); + + return RSRV_OK; +} + +typedef int (*pProtoStubTCP) (caHdrLargeArray *mp, void *pPayload, struct client *client); + +/* + * TCP protocol jump table + */ +static const pProtoStubTCP tcpJumpTable[] = +{ + tcp_version_action, + event_add_action, + event_cancel_reply, + read_action, + write_action, + bad_tcp_cmd_action, + search_reply_tcp, + bad_tcp_cmd_action, + events_off_action, + events_on_action, + read_sync_reply, + bad_tcp_cmd_action, + clear_channel_reply, + bad_tcp_cmd_action, + bad_tcp_cmd_action, + read_notify_action, + bad_tcp_cmd_action, + bad_tcp_cmd_action, + claim_ciu_action, + write_notify_action, + client_name_action, + host_name_action, + bad_tcp_cmd_action, + tcp_echo_action, + bad_tcp_cmd_action, + bad_tcp_cmd_action, + bad_tcp_cmd_action, + bad_tcp_cmd_action +}; + +/* + * UDP protocol jump table + */ +typedef int (*pProtoStubUDP) (caHdrLargeArray *mp, void *pPayload, struct client *client); +static const pProtoStubUDP udpJumpTable[] = +{ + udp_version_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + search_reply_udp, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + udp_echo_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action, + bad_udp_cmd_action +}; + +/* + * CAMESSAGE() + */ +int camessage ( struct client *client ) +{ + unsigned nmsg = 0; + unsigned msgsize; + unsigned bytes_left; + int status = RSRV_ERROR; + + if ( ! pCaBucket ) { + pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE); + if(!pCaBucket){ + return RSRV_ERROR; + } + } + + /* drain remnents of large messages that will not fit */ + if ( client->recvBytesToDrain ) { + if ( client->recvBytesToDrain >= client->recv.cnt ) { + client->recvBytesToDrain -= client->recv.cnt; + client->recv.stk = client->recv.cnt; + return RSRV_OK; + } + else { + client->recv.stk += client->recvBytesToDrain; + client->recvBytesToDrain = 0u; + } + } + + DLOG ( 2, ( "CAS: Parsing %d(decimal) bytes\n", recv->cnt ) ); + + while ( 1 ) + { + caHdrLargeArray msg; + caHdr *mp; + void *pBody; + + /* wait for at least a complete caHdr */ + bytes_left = client->recv.cnt - client->recv.stk; + if ( bytes_left < sizeof(*mp) ) { + status = RSRV_OK; + break; + } + + mp = (caHdr *) &client->recv.buf[client->recv.stk]; + msg.m_cmmd = ntohs ( mp->m_cmmd ); + msg.m_postsize = ntohs ( mp->m_postsize ); + msg.m_dataType = ntohs ( mp->m_dataType ); + msg.m_count = ntohs ( mp->m_count ); + msg.m_cid = ntohl ( mp->m_cid ); + msg.m_available = ntohl ( mp->m_available ); + + if ( CA_V49(client->minor_version_number) && msg.m_postsize == 0xffff ) { + ca_uint32_t *pLW = ( ca_uint32_t * ) ( mp + 1 ); + if ( bytes_left < sizeof(*mp) + 2 * sizeof(*pLW) ) { + status = RSRV_OK; + break; + } + msg.m_postsize = ntohl ( pLW[0] ); + msg.m_count = ntohl ( pLW[1] ); + msgsize = msg.m_postsize + sizeof(*mp) + 2 * sizeof ( *pLW ); + pBody = ( void * ) ( pLW + 2 ); + } + else { + msgsize = msg.m_postsize + sizeof(*mp); + pBody = ( void * ) ( mp + 1 ); + } + + /* + * disconnect clients that dont send 8 byte + * aligned payloads + */ + if ( msgsize & 0x7 ) { + send_err ( &msg, ECA_INTERNAL, client, + "CAS: Missaligned protocol rejected" ); + log_header ( "CAS: Missaligned protocol rejected", + client, &msg, 0, nmsg ); + status = RSRV_ERROR; + break; + } + + /* problem: we have a complete header, + * but before we check msgsize we don't know + * if we have a complete message body + * -> we may be called again with the same header + * after receiving the full message + */ + if ( msgsize > client->recv.maxstk ) { + casExpandRecvBuffer ( client, msgsize ); + if ( msgsize > client->recv.maxstk ) { + send_err ( &msg, ECA_TOLARGE, client, + "CAS: Server unable to load large request message. Max bytes=%lu", + rsrvSizeofLargeBufTCP ); + log_header ( "CAS: server unable to load large request message", + client, &msg, 0, nmsg ); + assert ( client->recv.cnt <= client->recv.maxstk ); + assert ( msgsize >= bytes_left ); + client->recvBytesToDrain = msgsize - bytes_left; + client->recv.stk = client->recv.cnt; + status = RSRV_OK; + break; + } + } + + /* + * wait for complete message body + */ + if ( msgsize > bytes_left ) { + status = RSRV_OK; + break; + } + + nmsg++; + + if ( CASDEBUG > 2 ) + log_header (NULL, client, &msg, pBody, nmsg); + + if ( client == prsrv_cast_client ) { + if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) { + status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client ); + if (status!=RSRV_OK) { + status = RSRV_ERROR; + break; + } + } + else { + status = bad_udp_cmd_action ( &msg, pBody, client ); + break; + } + } + else { + if ( msg.m_cmmd < NELEMENTS(tcpJumpTable) ) { + status = ( *tcpJumpTable[msg.m_cmmd] ) ( &msg, pBody, client ); + if ( status != RSRV_OK ) { + status = RSRV_ERROR; + break; + } + } + else { + return bad_tcp_cmd_action ( &msg, pBody, client ); + } + } + + client->recv.stk += msgsize; + } + + return status; +} + +/* + * rsrvCheckPut () + */ +int rsrvCheckPut (const struct channel_in_use *pciu) +{ + /* + * SPC_NOMOD fields are always unwritable + */ + if (pciu->addr.special==SPC_NOMOD) { + return 0; + } + else { + return asCheckPut (pciu->asClientPVT); + } +} diff --git a/src/rsrv/camsgtask.c b/src/rsrv/camsgtask.c new file mode 100644 index 000000000..a5133caf6 --- /dev/null +++ b/src/rsrv/camsgtask.c @@ -0,0 +1,199 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 6-88 + */ + + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "osiSock.h" +#include "epicsTime.h" +#include "errlog.h" +#include "taskwd.h" +#include "db_access.h" +#include "caerr.h" + +#define epicsExportSharedSymbols +#include "rsrv.h" +#include "server.h" + +/* + * camsgtask() + * + * CA server TCP client task (one spawned for each client) + */ +void camsgtask ( void *pParm ) +{ + struct client *client = (struct client *) pParm; + int nchars; + int status; + + casAttachThreadToClient ( client ); + + /* + * send the server's minor version number to the client + */ + status = cas_copy_in_header ( client, CA_PROTO_VERSION, 0, + 0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 ); + if ( status != ECA_NORMAL ) { + LOCK_CLIENTQ; + ellDelete ( &clientQ, &client->node ); + UNLOCK_CLIENTQ; + destroy_tcp_client ( client ); + return; + } + + while (castcp_ctl == ctlRun && !client->disconnect) { + + /* + * allow message to batch up if more are comming + */ + status = socket_ioctl (client->sock, FIONREAD, &nchars); + if (status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf("CAS: io ctl err - %s\n", + sockErrBuf); + cas_send_bs_msg(client, TRUE); + } + else if (nchars == 0){ + cas_send_bs_msg(client, TRUE); + } + + client->recv.stk = 0; + assert ( client->recv.maxstk >= client->recv.cnt ); + nchars = recv ( client->sock, &client->recv.buf[client->recv.cnt], + (int) ( client->recv.maxstk - client->recv.cnt ), 0 ); + if ( nchars == 0 ){ + if ( CASDEBUG > 0 ) { + /* convert to u long so that %lu works on both 32 and 64 bit archs */ + unsigned long cnt = sizeof ( client->recv.buf ) - client->recv.cnt; + errlogPrintf ( "CAS: nill message disconnect ( %lu bytes request )\n", + cnt ); + } + break; + } + else if ( nchars < 0 ) { + int anerrno = SOCKERRNO; + + if ( anerrno == SOCK_EINTR ) { + continue; + } + + if ( anerrno == SOCK_ENOBUFS ) { + errlogPrintf ( + "rsrv: system low on network buffers " + "- receive retry in 15 seconds\n" ); + epicsThreadSleep ( 15.0 ); + continue; + } + + /* + * normal conn lost conditions + */ + if ( ( anerrno != SOCK_ECONNABORTED && + anerrno != SOCK_ECONNRESET && + anerrno != SOCK_ETIMEDOUT ) || + CASDEBUG > 2 ) { + errlogPrintf ( "CAS: client disconnect(errno=%d)\n", anerrno ); + } + break; + } + + epicsTimeGetCurrent ( &client->time_at_last_recv ); + client->recv.cnt += ( unsigned ) nchars; + + status = camessage ( client ); + if (status == 0) { + /* + * if there is a partial message + * align it with the start of the buffer + */ + if (client->recv.cnt > client->recv.stk) { + unsigned bytes_left; + + bytes_left = client->recv.cnt - client->recv.stk; + + /* + * overlapping regions handled + * properly by memmove + */ + memmove (client->recv.buf, + &client->recv.buf[client->recv.stk], bytes_left); + client->recv.cnt = bytes_left; + } + else { + client->recv.cnt = 0ul; + } + } + else { + char buf[64]; + + client->recv.cnt = 0ul; + + /* + * disconnect when there are severe message errors + */ + ipAddrToDottedIP (&client->addr, buf, sizeof(buf)); + epicsPrintf ("CAS: forcing disconnect from %s\n", buf); + break; + } + } + + LOCK_CLIENTQ; + ellDelete ( &clientQ, &client->node ); + UNLOCK_CLIENTQ; + + destroy_tcp_client ( client ); +} + + +void epicsShareAPI casHostNameInitiatingCurrentThread ( char * pBuf, unsigned bufSize ) +{ + if ( bufSize ) { + const char * pHostName = ""; + { + struct client * pClient = ( struct client * ) + epicsThreadPrivateGet ( rsrvCurrentClient ); + if ( pClient ) { + pHostName = pClient->pHostName; + } + } + strncpy ( pBuf, pHostName, bufSize ); + pBuf [ bufSize - 1u ] = '\0'; + } +} + +void epicsShareAPI casUserNameInitiatingCurrentThread ( char * pBuf, unsigned bufSize ) +{ + if ( bufSize ) { + const char * pUserName = ""; + { + struct client * pClient = ( struct client * ) + epicsThreadPrivateGet ( rsrvCurrentClient ); + if ( pClient ) { + pUserName = pClient->pUserName; + } + } + strncpy ( pBuf, pUserName, bufSize ); + pBuf [ bufSize - 1u ] = '\0'; + } +} + diff --git a/src/rsrv/caserverio.c b/src/rsrv/caserverio.c new file mode 100644 index 000000000..119ad5e3a --- /dev/null +++ b/src/rsrv/caserverio.c @@ -0,0 +1,418 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 060791 + */ + +#include +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "osiSock.h" +#include "epicsTime.h" +#include "epicsSignal.h" +#include "errlog.h" +#include "caerr.h" + +#include "net_convert.h" + +#define epicsExportSharedSymbols +#include "server.h" + +/* As an optimisation, any message allocated with a large header is resized to + * use a small header if the payload size is below this threshold. */ +#define SMALL_MESSAGE_THRESHOLD 65 + +/* + * cas_send_bs_msg() + * + * (channel access server send message) + */ +void cas_send_bs_msg ( struct client *pclient, int lock_needed ) +{ + int status; + + if ( CASDEBUG > 2 && pclient->send.stk ) { + errlogPrintf ( "CAS: Sending a message of %d bytes\n", pclient->send.stk ); + } + + if ( pclient->disconnect ) { + if ( CASDEBUG > 2 ) { + errlogPrintf ( "CAS: msg Discard for sock %d addr %x\n", + pclient->sock, (unsigned) pclient->addr.sin_addr.s_addr ); + } + pclient->send.stk = 0u; + return; + } + + if ( lock_needed ) { + SEND_LOCK ( pclient ); + } + + while ( pclient->send.stk && ! pclient->disconnect ) { + status = send ( pclient->sock, pclient->send.buf, pclient->send.stk, 0 ); + if ( status >= 0 ) { + unsigned transferSize = (unsigned) status; + if ( transferSize >= pclient->send.stk ) { + pclient->send.stk = 0; + epicsTimeGetCurrent ( &pclient->time_at_last_send ); + break; + } + else { + unsigned bytesLeft = pclient->send.stk - transferSize; + memmove ( pclient->send.buf, &pclient->send.buf[transferSize], + bytesLeft ); + pclient->send.stk = bytesLeft; + } + } + else { + int causeWasSocketHangup = 0; + int anerrno = SOCKERRNO; + char buf[64]; + + if ( pclient->disconnect ) { + pclient->send.stk = 0u; + break; + } + + if ( anerrno == SOCK_EINTR ) { + continue; + } + + if ( anerrno == SOCK_ENOBUFS ) { + errlogPrintf ( "rsrv: system low on network buffers - send retry in 15 seconds\n" ); + epicsThreadSleep ( 15.0 ); + continue; + } + + ipAddrToDottedIP ( &pclient->addr, buf, sizeof(buf) ); + + if ( + anerrno == SOCK_ECONNABORTED || + anerrno == SOCK_ECONNRESET || + anerrno == SOCK_EPIPE || + anerrno == SOCK_ETIMEDOUT ) { + causeWasSocketHangup = 1; + } + else { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( + "CAS: TCP send to \"%s\" failed because \"%s\"\n", + buf, sockErrBuf); + } + pclient->disconnect = TRUE; + pclient->send.stk = 0u; + + /* + * wakeup the receive thread + */ + if ( ! causeWasSocketHangup ) { + enum epicsSocketSystemCallInterruptMechanismQueryInfo info = + epicsSocketSystemCallInterruptMechanismQuery (); + switch ( info ) { + case esscimqi_socketCloseRequired: + if ( pclient->sock != INVALID_SOCKET ) { + epicsSocketDestroy ( pclient->sock ); + pclient->sock = INVALID_SOCKET; + } + break; + case esscimqi_socketBothShutdownRequired: + { + int status = shutdown ( pclient->sock, SHUT_RDWR ); + if ( status ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ("rsrv: socket shutdown error was %s\n", + sockErrBuf ); + } + } + break; + case esscimqi_socketSigAlarmRequired: + epicsSignalRaiseSigAlarm ( pclient->tid ); + break; + default: + break; + }; + break; + } + } + } + + if ( lock_needed ) { + SEND_UNLOCK(pclient); + } + + DLOG ( 3, ( "------------------------------\n\n" ) ); + + return; +} + +/* + * cas_send_dg_msg() + * + * (channel access server send udp message) + */ +void cas_send_dg_msg ( struct client * pclient ) +{ + int status; + int sizeDG; + char * pDG; + caHdr * pMsg; + + if ( CASDEBUG > 2 && pclient->send.stk ) { + errlogPrintf ( "CAS: Sending a udp message of %d bytes\n", pclient->send.stk ); + } + + SEND_LOCK ( pclient ); + + if ( pclient->send.stk <= sizeof (caHdr) ) { + SEND_UNLOCK(pclient); + return; + } + + pDG = pclient->send.buf; + pMsg = ( caHdr * ) pDG; + sizeDG = pclient->send.stk; + assert ( ntohs ( pMsg->m_cmmd ) == CA_PROTO_VERSION ); + if ( CA_V411 ( pclient->minor_version_number ) ) { + pMsg->m_cid = htonl ( pclient->seqNoOfReq ); + pMsg->m_dataType = htons ( sequenceNoIsValid ); + } + else { + pDG += sizeof (caHdr); + sizeDG -= sizeof (caHdr); + } + + status = sendto ( pclient->sock, pDG, sizeDG, 0, + (struct sockaddr *)&pclient->addr, sizeof(pclient->addr) ); + if ( status >= 0 ) { + if ( status >= sizeDG ) { + epicsTimeGetCurrent ( &pclient->time_at_last_send ); + } + else { + errlogPrintf ( + "cas: system failed to send entire udp frame?\n" ); + } + } + else { + char sockErrBuf[64]; + char buf[128]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + ipAddrToDottedIP ( &pclient->addr, buf, sizeof(buf) ); + errlogPrintf( + "CAS: UDP send to \"%s\" " + "failed because \"%s\"\n", + buf, + sockErrBuf); + } + + pclient->send.stk = 0u; + + /* + * add placeholder for the first version message should it be needed + */ + rsrv_version_reply ( prsrv_cast_client ); + + SEND_UNLOCK(pclient); + + DLOG ( 3, ( "------------------------------\n\n" ) ); + + return; +} + +/* + * + * cas_copy_in_header() + * + * Allocate space in the outgoing message buffer and + * copy in message header. Return pointer to message body. + * + * send lock must be on while in this routine + * + * Returns a valid ptr to message body or NULL if the msg + * will not fit. + */ +int cas_copy_in_header ( + struct client *pclient, ca_uint16_t response, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t responseSpecific, void **ppPayload ) +{ + unsigned msgSize; + ca_uint32_t alignedPayloadSize; + caHdr *pMsg; + + if ( payloadSize > UINT_MAX - sizeof ( caHdr ) - 8u ) { + return ECA_TOLARGE; + } + + alignedPayloadSize = CA_MESSAGE_ALIGN ( payloadSize ); + + msgSize = alignedPayloadSize + sizeof ( caHdr ); + if ( alignedPayloadSize >= 0xffff || nElem >= 0xffff ) { + if ( ! CA_V49 ( pclient->minor_version_number ) ) { + return ECA_16KARRAYCLIENT; + } + msgSize += 2 * sizeof ( ca_uint32_t ); + } + + if ( msgSize > pclient->send.maxstk ) { + casExpandSendBuffer ( pclient, msgSize ); + if ( msgSize > pclient->send.maxstk ) { + return ECA_TOLARGE; + } + } + + if ( pclient->send.stk > pclient->send.maxstk - msgSize ) { + if ( pclient->disconnect ) { + pclient->send.stk = 0; + } + else{ + if ( pclient->proto == IPPROTO_TCP) { + cas_send_bs_msg ( pclient, FALSE ); + } + else if ( pclient->proto == IPPROTO_UDP ) { + cas_send_dg_msg ( pclient ); + } + else { + return ECA_INTERNAL; + } + } + } + + pMsg = (caHdr *) &pclient->send.buf[pclient->send.stk]; + pMsg->m_cmmd = htons(response); + pMsg->m_dataType = htons(dataType); + pMsg->m_cid = htonl(cid); + pMsg->m_available = htonl(responseSpecific); + if (alignedPayloadSize < 0xffff && nElem < 0xffff) { + pMsg->m_postsize = htons(((ca_uint16_t) alignedPayloadSize)); + pMsg->m_count = htons(((ca_uint16_t) nElem)); + if (ppPayload) + *ppPayload = (void *) (pMsg + 1); + } + else { + ca_uint32_t *pW32 = (ca_uint32_t *) (pMsg + 1); + pMsg->m_postsize = htons(0xffff); + pMsg->m_count = htons(0u); + pW32[0] = htonl(alignedPayloadSize); + pW32[1] = htonl(nElem); + if (ppPayload) + *ppPayload = (void *) (pW32 + 2); + } + + /* zero out pad bytes */ + if ( alignedPayloadSize > payloadSize ) { + char *p = ( char * ) *ppPayload; + memset ( p + payloadSize, '\0', + alignedPayloadSize - payloadSize ); + } + + return ECA_NORMAL; +} + +void cas_set_header_cid ( struct client *pClient, ca_uint32_t cid ) +{ + caHdr *pMsg = ( caHdr * ) &pClient->send.buf[pClient->send.stk]; + pMsg->m_cid = htonl ( cid ); +} + +void cas_set_header_count (struct client *pClient, ca_uint32_t count) +{ + caHdr *pMsg = (caHdr *) &pClient->send.buf[pClient->send.stk]; + if (pMsg->m_postsize == htons(0xffff)) { + ca_uint32_t *pLW; + + assert(pMsg->m_count == 0); + pLW = (ca_uint32_t *) (pMsg + 1); + pLW[1] = htonl(count); + } + else { + assert(count < 65536); + pMsg->m_count = htons((ca_uint16_t) count); + } +} + +void cas_commit_msg ( struct client *pClient, ca_uint32_t size ) +{ + caHdr * pMsg = ( caHdr * ) &pClient->send.buf[pClient->send.stk]; + size = CA_MESSAGE_ALIGN ( size ); + if ( pMsg->m_postsize == htons ( 0xffff ) ) { + ca_uint32_t * pLW = ( ca_uint32_t * ) ( pMsg + 1 ); + assert ( size <= ntohl ( *pLW ) ); + if (size < SMALL_MESSAGE_THRESHOLD) { + /* If the message is sufficiently small it can be worth converting a + * large message header into a small header. This saves us all of 8 + * bytes over the wire, so it's not such a big deal. */ + pMsg->m_postsize = htons((ca_uint16_t) size); + pMsg->m_count = htons((ca_uint16_t) ntohl(pLW[1])); + memmove(pLW, pLW + 2, size); + size += sizeof(caHdr); + } + else { + pLW[0] = htonl ( size ); + size += sizeof ( caHdr ) + 2 * sizeof ( *pLW ); + } + } + else { + assert ( size <= ntohs ( pMsg->m_postsize ) ); + pMsg->m_postsize = htons ( (ca_uint16_t) size ); + size += sizeof ( caHdr ); + } + pClient->send.stk += size; +} + +/* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ +ca_uint16_t rsrvGetUInt16 ( struct message_buffer *recv ) +{ + ca_uint8_t *pBuf = ( ca_uint8_t * ) recv->buf; + ca_uint16_t result; + /* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ + assert ( recv->cnt - recv->stk >= 2u ); + result = pBuf[recv->stk++] << 8u; + result |= pBuf[recv->stk++] << 0u; + return result; +} + +/* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ +ca_uint32_t rsrvGetUInt32 ( struct message_buffer *recv ) +{ + ca_uint8_t *pBuf = ( ca_uint8_t * ) recv->buf; + ca_uint32_t result; + /* + * this assumes that we have already checked to see + * if sufficent bytes are available + */ + assert ( recv->cnt - recv->stk >= 4u ); + result = pBuf[recv->stk++] << 24u; + result |= pBuf[recv->stk++] << 16u; + result |= pBuf[recv->stk++] << 8u; + result |= pBuf[recv->stk++] << 0u; + return result; +} diff --git a/src/rsrv/caservertask.c b/src/rsrv/caservertask.c new file mode 100644 index 000000000..053e33d91 --- /dev/null +++ b/src/rsrv/caservertask.c @@ -0,0 +1,967 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Revision-Id: johill@lanl.gov-20101101211551-n0j6oacp2jbcihjj + * + * Author: Jeffrey O. Hill + * + */ + +#include +#include +#include +#include +#include +#include + +#include "osiSock.h" +#include "osiPoolStatus.h" +#include "epicsSignal.h" +#include "epicsEvent.h" +#include "epicsMutex.h" +#include "epicsTime.h" +#include "errlog.h" +#include "taskwd.h" +#include "addrList.h" +#include "freeList.h" +#include "errlog.h" +#include "db_field_log.h" +#include "dbAddr.h" +#include "dbEvent.h" +#include "dbCommon.h" +#include "epicsStdioRedirect.h" + +#define epicsExportSharedSymbols +#include "rsrv.h" +#define GLBLSOURCE +#include "server.h" + +#define DELETE_TASK(NAME)\ +if(threadNameToId(NAME)!=0)threadDestroy(threadNameToId(NAME)); + +epicsThreadPrivateId rsrvCurrentClient; + +/* + * + * req_server() + * + * CA server task + * + * Waits for connections at the CA port and spawns a task to + * handle each of them + * + */ +static void req_server (void *pParm) +{ + unsigned priorityOfSelf = epicsThreadGetPrioritySelf (); + unsigned priorityOfBeacons; + epicsThreadBooleanStatus tbs; + struct sockaddr_in serverAddr; /* server's address */ + osiSocklen_t addrSize; + int status; + SOCKET clientSock; + epicsThreadId tid; + int portChange; + + epicsSignalInstallSigPipeIgnore (); + + taskwdInsert ( epicsThreadGetIdSelf (), NULL, NULL ); + + rsrvCurrentClient = epicsThreadPrivateCreate (); + + if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { + ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, + (unsigned short) CA_SERVER_PORT ); + } + else { + ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, + (unsigned short) CA_SERVER_PORT ); + } + + if (IOC_sock != 0 && IOC_sock != INVALID_SOCKET) { + epicsSocketDestroy ( IOC_sock ); + } + + /* + * Open the socket. Use ARPA Internet address format and stream + * sockets. Format described in . + */ + if ( ( IOC_sock = epicsSocketCreate (AF_INET, SOCK_STREAM, 0) ) == INVALID_SOCKET ) { + errlogPrintf ("CAS: Socket creation error\n"); + epicsThreadSuspendSelf (); + } + + epicsSocketEnableAddressReuseDuringTimeWaitState ( IOC_sock ); + + /* Zero the sock_addr structure */ + memset ( (void *) &serverAddr, 0, sizeof ( serverAddr ) ); + serverAddr.sin_family = AF_INET; + serverAddr.sin_addr.s_addr = htonl (INADDR_ANY); + serverAddr.sin_port = htons ( ca_server_port ); + + /* get server's Internet address */ + status = bind ( IOC_sock, (struct sockaddr *) &serverAddr, sizeof ( serverAddr ) ); + if ( status < 0 ) { + if ( SOCKERRNO == SOCK_EADDRINUSE ) { + /* + * enable assignment of a default port + * (so the getsockname() call below will + * work correctly) + */ + serverAddr.sin_port = ntohs (0); + status = bind ( IOC_sock, + (struct sockaddr *) &serverAddr, sizeof ( serverAddr ) ); + } + if ( status < 0 ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAS: Socket bind error was \"%s\"\n", + sockErrBuf ); + epicsThreadSuspendSelf (); + } + portChange = 1; + } + else { + portChange = 0; + } + + addrSize = ( osiSocklen_t ) sizeof ( serverAddr ); + status = getsockname ( IOC_sock, + (struct sockaddr *)&serverAddr, &addrSize); + if ( status ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "CAS: getsockname() error %s\n", + sockErrBuf ); + epicsThreadSuspendSelf (); + } + + ca_server_port = ntohs (serverAddr.sin_port); + + if ( portChange ) { + errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n"); + errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", + ca_server_port ); + errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n"); + errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" ); + errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" ); + } + + /* listen and accept new connections */ + if ( listen ( IOC_sock, 20 ) < 0 ) { + errlogPrintf ("CAS: Listen error\n"); + epicsSocketDestroy (IOC_sock); + epicsThreadSuspendSelf (); + } + + tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfBeacons ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + priorityOfBeacons = priorityOfSelf; + } + + beacon_startStopEvent = epicsEventMustCreate(epicsEventEmpty); + beacon_ctl = ctlPause; + + tid = epicsThreadCreate ( "CAS-beacon", priorityOfBeacons, + epicsThreadGetStackSize (epicsThreadStackSmall), + rsrv_online_notify_task, 0 ); + if ( tid == 0 ) { + epicsPrintf ( "CAS: unable to start beacon thread\n" ); + } + + epicsEventMustWait(beacon_startStopEvent); + epicsEventSignal(castcp_startStopEvent); + + while (TRUE) { + struct sockaddr sockAddr; + osiSocklen_t addLen = sizeof(sockAddr); + + while (castcp_ctl == ctlPause) { + epicsThreadSleep(0.1); + } + + clientSock = epicsSocketAccept ( IOC_sock, &sockAddr, &addLen ); + if ( clientSock == INVALID_SOCKET ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf("CAS: Client accept error was \"%s\"\n", + sockErrBuf ); + epicsThreadSleep(15.0); + continue; + } + else { + epicsThreadId id; + struct client *pClient; + + /* socket passed in is closed if unsuccessful here */ + pClient = create_tcp_client ( clientSock ); + if ( ! pClient ) { + epicsThreadSleep ( 15.0 ); + continue; + } + + LOCK_CLIENTQ; + ellAdd ( &clientQ, &pClient->node ); + UNLOCK_CLIENTQ; + + id = epicsThreadCreate ( "CAS-client", epicsThreadPriorityCAServerLow, + epicsThreadGetStackSize ( epicsThreadStackBig ), + camsgtask, pClient ); + if ( id == 0 ) { + LOCK_CLIENTQ; + ellDelete ( &clientQ, &pClient->node ); + UNLOCK_CLIENTQ; + destroy_tcp_client ( pClient ); + errlogPrintf ( "CAS: task creation for new client failed\n" ); + epicsThreadSleep ( 15.0 ); + continue; + } + } + } +} + +/* + * rsrv_init () + */ +int rsrv_init (void) +{ + epicsThreadBooleanStatus tbs; + unsigned priorityOfConnectDaemon; + epicsThreadId tid; + long maxBytesAsALong; + long status; + + clientQlock = epicsMutexMustCreate(); + + ellInit ( &clientQ ); + freeListInitPvt ( &rsrvClientFreeList, sizeof(struct client), 8 ); + freeListInitPvt ( &rsrvChanFreeList, sizeof(struct channel_in_use), 512 ); + freeListInitPvt ( &rsrvEventFreeList, sizeof(struct event_ext), 512 ); + freeListInitPvt ( &rsrvSmallBufFreeListTCP, MAX_TCP, 16 ); + initializePutNotifyFreeList (); + + status = envGetLongConfigParam ( &EPICS_CA_MAX_ARRAY_BYTES, &maxBytesAsALong ); + if ( status || maxBytesAsALong < 0 ) { + errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was not a positive integer\n" ); + rsrvSizeofLargeBufTCP = MAX_TCP; + } + else { + /* allow room for the protocol header so that they get the array size they requested */ + static const unsigned headerSize = sizeof ( caHdr ) + 2 * sizeof ( ca_uint32_t ); + ca_uint32_t maxBytes = ( unsigned ) maxBytesAsALong; + if ( maxBytes < 0xffffffff - headerSize ) { + maxBytes += headerSize; + } + else { + maxBytes = 0xffffffff; + } + if ( maxBytes < MAX_TCP ) { + errlogPrintf ( "cas: EPICS_CA_MAX_ARRAY_BYTES was rounded up to %u\n", MAX_TCP ); + rsrvSizeofLargeBufTCP = MAX_TCP; + } + else { + rsrvSizeofLargeBufTCP = maxBytes; + } + } + freeListInitPvt ( &rsrvLargeBufFreeListTCP, rsrvSizeofLargeBufTCP, 1 ); + ellInit ( &beaconAddrList ); + prsrv_cast_client = NULL; + pCaBucket = NULL; + + castcp_startStopEvent = epicsEventMustCreate(epicsEventEmpty); + castcp_ctl = ctlPause; + + /* + * go down two levels so that we are below + * the TCP and event threads started on behalf + * of individual clients + */ + tbs = epicsThreadHighestPriorityLevelBelow ( + epicsThreadPriorityCAServerLow, &priorityOfConnectDaemon ); + if ( tbs == epicsThreadBooleanStatusSuccess ) { + tbs = epicsThreadHighestPriorityLevelBelow ( + priorityOfConnectDaemon, &priorityOfConnectDaemon ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + priorityOfConnectDaemon = epicsThreadPriorityCAServerLow; + } + } + else { + priorityOfConnectDaemon = epicsThreadPriorityCAServerLow; + } + + tid = epicsThreadCreate ( "CAS-TCP", + priorityOfConnectDaemon, + epicsThreadGetStackSize(epicsThreadStackMedium), + req_server, 0); + if ( tid == 0 ) { + epicsPrintf ( "CAS: unable to start connection request thread\n" ); + } + + epicsEventMustWait(castcp_startStopEvent); + + return RSRV_OK; +} + +int rsrv_run (void) +{ + castcp_ctl = ctlRun; + casudp_ctl = ctlRun; + beacon_ctl = ctlRun; + + return RSRV_OK; +} + +int rsrv_pause (void) +{ + beacon_ctl = ctlPause; + casudp_ctl = ctlPause; + castcp_ctl = ctlPause; + + return RSRV_OK; +} + +static unsigned countChanListBytes ( + struct client *client, ELLLIST * pList ) +{ + struct channel_in_use * pciu; + unsigned bytes_reserved = 0; + + epicsMutexMustLock ( client->chanListLock ); + pciu = ( struct channel_in_use * ) pList->node.next; + while ( pciu ) { + bytes_reserved += sizeof(struct channel_in_use); + bytes_reserved += sizeof(struct event_ext)*ellCount( &pciu->eventq ); + bytes_reserved += rsrvSizeOfPutNotify ( pciu->pPutNotify ); + pciu = ( struct channel_in_use * ) ellNext( &pciu->node ); + } + epicsMutexUnlock ( client->chanListLock ); + + return bytes_reserved; +} + +static void showChanList ( + struct client * client, ELLLIST * pList ) +{ + unsigned i = 0u; + struct channel_in_use * pciu; + epicsMutexMustLock ( client->chanListLock ); + pciu = (struct channel_in_use *) pList->node.next; + while ( pciu ){ + printf( "\t%s(%d%c%c)", + pciu->addr.precord->name, + ellCount ( &pciu->eventq ), + asCheckGet ( pciu->asClientPVT ) ? 'r': '-', + rsrvCheckPut ( pciu ) ? 'w': '-' ); + pciu = ( struct channel_in_use * ) ellNext ( &pciu->node ); + if( ++i % 3u == 0u ) { + printf ( "\n" ); + } + } + epicsMutexUnlock ( client->chanListLock ); +} + +/* + * log_one_client () + */ +static void log_one_client (struct client *client, unsigned level) +{ + char *pproto; + double send_delay; + double recv_delay; + char *state[] = {"up", "down"}; + epicsTimeStamp current; + char clientHostName[256]; + + ipAddrToDottedIP (&client->addr, clientHostName, sizeof(clientHostName)); + + if(client->proto == IPPROTO_UDP){ + pproto = "UDP"; + } + else if(client->proto == IPPROTO_TCP){ + pproto = "TCP"; + } + else{ + pproto = "UKN"; + } + + epicsTimeGetCurrent(¤t); + send_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_send); + recv_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_recv); + + printf ( "%s %s(%s): User=\"%s\", V%u.%u, %d Channels, Priority=%u\n", + pproto, + clientHostName, + client->pHostName ? client->pHostName : "", + client->pUserName ? client->pUserName : "", + CA_MAJOR_PROTOCOL_REVISION, + client->minor_version_number, + ellCount(&client->chanList) + + ellCount(&client->chanPendingUpdateARList), + client->priority ); + if ( level >= 1 ) { + printf ("\tTask Id=%p, Socket FD=%d\n", + (void *) client->tid, client->sock); + printf( + "\tSecs since last send %6.2f, Secs since last receive %6.2f\n", + send_delay, recv_delay); + printf( + "\tUnprocessed request bytes=%u, Undelivered response bytes=%u\n", + client->recv.cnt - client->recv.stk, + client->send.stk ); + printf( + "\tState=%s%s%s\n", + state[client->disconnect?1:0], + client->send.type == mbtLargeTCP ? " jumbo-send-buf" : "", + client->recv.type == mbtLargeTCP ? " jumbo-recv-buf" : ""); + } + + if ( level >= 2u ) { + unsigned bytes_reserved = 0; + bytes_reserved += sizeof(struct client); + bytes_reserved += countChanListBytes ( + client, & client->chanList ); + bytes_reserved += countChanListBytes ( + client, & client->chanPendingUpdateARList ); + printf( "\t%d bytes allocated\n", bytes_reserved); + showChanList ( client, & client->chanList ); + showChanList ( client, & client->chanPendingUpdateARList ); + printf("\n"); + } + + if ( level >= 3u ) { + printf( "\tSend Lock\n"); + epicsMutexShow(client->lock,1); + printf( "\tPut Notify Lock\n"); + epicsMutexShow (client->putNotifyLock,1); + printf( "\tAddress Queue Lock\n"); + epicsMutexShow (client->chanListLock,1); + printf( "\tEvent Queue Lock\n"); + epicsMutexShow (client->eventqLock,1); + printf( "\tBlock Semaphore\n"); + epicsEventShow (client->blockSem,1); + } +} + +/* + * casr() + */ +void epicsShareAPI casr (unsigned level) +{ + size_t bytes_reserved; + struct client *client; + + if ( ! clientQlock ) { + return; + } + + printf ("Channel Access Server V%s\n", + CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ) ); + + LOCK_CLIENTQ + client = (struct client *) ellNext ( &clientQ.node ); + if (client) { + printf("Connected circuits:\n"); + } + else { + printf("No clients connected.\n"); + } + while (client) { + log_one_client(client, level); + client = (struct client *) ellNext(&client->node); + } + UNLOCK_CLIENTQ + + if (level>=2 && prsrv_cast_client) { + printf( "UDP Server:\n" ); + log_one_client(prsrv_cast_client, level); + } + + if (level>=2u) { + bytes_reserved = 0u; + bytes_reserved += sizeof (struct client) * + freeListItemsAvail (rsrvClientFreeList); + bytes_reserved += sizeof (struct channel_in_use) * + freeListItemsAvail (rsrvChanFreeList); + bytes_reserved += sizeof(struct event_ext) * + freeListItemsAvail (rsrvEventFreeList); + bytes_reserved += MAX_TCP * + freeListItemsAvail ( rsrvSmallBufFreeListTCP ); + bytes_reserved += rsrvSizeofLargeBufTCP * + freeListItemsAvail ( rsrvLargeBufFreeListTCP ); + bytes_reserved += rsrvSizeOfPutNotify ( 0 ) * + freeListItemsAvail ( rsrvPutNotifyFreeList ); + printf( "There are currently %u bytes on the server's free list\n", + (unsigned int) bytes_reserved); + printf( "%u client(s), %u channel(s), %u event(s) (monitors) %u putNotify(s)\n", + (unsigned int) freeListItemsAvail ( rsrvClientFreeList ), + (unsigned int) freeListItemsAvail ( rsrvChanFreeList ), + (unsigned int) freeListItemsAvail ( rsrvEventFreeList ), + (unsigned int) freeListItemsAvail ( rsrvPutNotifyFreeList )); + printf( "%u small buffers (%u bytes ea), and %u jumbo buffers (%u bytes ea)\n", + (unsigned int) freeListItemsAvail ( rsrvSmallBufFreeListTCP ), + MAX_TCP, + (unsigned int) freeListItemsAvail ( rsrvLargeBufFreeListTCP ), + rsrvSizeofLargeBufTCP ); + if(pCaBucket){ + printf( "The server's resource id conversion table:\n"); + LOCK_CLIENTQ; + bucketShow (pCaBucket); + UNLOCK_CLIENTQ; + } + printf ( "The server's array size limit is %u bytes max\n", + rsrvSizeofLargeBufTCP ); + + printChannelAccessAddressList (&beaconAddrList); + } +} + +/* + * destroy_client () + */ +void destroy_client ( struct client *client ) +{ + if ( ! client ) { + return; + } + + if ( client->tid != 0 ) { + taskwdRemove ( client->tid ); + } + + if ( client->sock != INVALID_SOCKET ) { + epicsSocketDestroy ( client->sock ); + } + + if ( client->proto == IPPROTO_TCP ) { + if ( client->send.buf ) { + if ( client->send.type == mbtSmallTCP ) { + freeListFree ( rsrvSmallBufFreeListTCP, client->send.buf ); + } + else if ( client->send.type == mbtLargeTCP ) { + freeListFree ( rsrvLargeBufFreeListTCP, client->send.buf ); + } + else { + errlogPrintf ( "cas: Corrupt send buffer free list type code=%u during client cleanup?\n", + client->send.type ); + } + } + if ( client->recv.buf ) { + if ( client->recv.type == mbtSmallTCP ) { + freeListFree ( rsrvSmallBufFreeListTCP, client->recv.buf ); + } + else if ( client->recv.type == mbtLargeTCP ) { + freeListFree ( rsrvLargeBufFreeListTCP, client->recv.buf ); + } + else { + errlogPrintf ( "cas: Corrupt recv buffer free list type code=%u during client cleanup?\n", + client->send.type ); + } + } + } + else if ( client->proto == IPPROTO_UDP ) { + if ( client->send.buf ) { + free ( client->send.buf ); + } + if ( client->recv.buf ) { + free ( client->recv.buf ); + } + } + + if ( client->eventqLock ) { + epicsMutexDestroy ( client->eventqLock ); + } + + if ( client->chanListLock ) { + epicsMutexDestroy ( client->chanListLock ); + } + + if ( client->putNotifyLock ) { + epicsMutexDestroy ( client->putNotifyLock ); + } + + if ( client->lock ) { + epicsMutexDestroy ( client->lock ); + } + + if ( client->blockSem ) { + epicsEventDestroy ( client->blockSem ); + } + + if ( client->pUserName ) { + free ( client->pUserName ); + } + + if ( client->pHostName ) { + free ( client->pHostName ); + } + + freeListFree ( rsrvClientFreeList, client ); +} + +static void destroyAllChannels ( + struct client * client, ELLLIST * pList ) +{ + if ( !client->chanListLock || !client->eventqLock ) { + return; + } + + while ( TRUE ) { + struct event_ext *pevext; + int status; + struct channel_in_use *pciu; + + epicsMutexMustLock ( client->chanListLock ); + pciu = (struct channel_in_use *) ellGet ( pList ); + epicsMutexUnlock ( client->chanListLock ); + + if ( ! pciu ) { + break; + } + + while ( TRUE ) { + /* + * AS state change could be using this list + */ + epicsMutexMustLock ( client->eventqLock ); + pevext = (struct event_ext *) ellGet ( &pciu->eventq ); + epicsMutexUnlock ( client->eventqLock ); + + if ( ! pevext ) { + break; + } + + if ( pevext->pdbev ) { + db_cancel_event (pevext->pdbev); + } + freeListFree (rsrvEventFreeList, pevext); + } + rsrvFreePutNotify ( client, pciu->pPutNotify ); + LOCK_CLIENTQ; + status = bucketRemoveItemUnsignedId ( pCaBucket, &pciu->sid); + rsrvChannelCount--; + UNLOCK_CLIENTQ; + if ( status != S_bucket_success ) { + errPrintf ( status, __FILE__, __LINE__, + "Bad id=%d at close", pciu->sid); + } + status = asRemoveClient(&pciu->asClientPVT); + if ( status && status != S_asLib_asNotActive ) { + printf ( "bad asRemoveClient() status was %x \n", status ); + errPrintf ( status, __FILE__, __LINE__, "asRemoveClient" ); + } + + freeListFree ( rsrvChanFreeList, pciu ); + } +} + +void destroy_tcp_client ( struct client *client ) +{ + int status; + + if ( CASDEBUG > 0 ) { + errlogPrintf ( "CAS: Connection %d Terminated\n", client->sock ); + } + + if ( client->evuser ) { + /* + * turn off extra labor callbacks from the event thread + */ + status = db_add_extra_labor_event ( client->evuser, NULL, NULL ); + assert ( ! status ); + + /* + * wait for extra labor in progress to comple + */ + db_flush_extra_labor_event ( client->evuser ); + } + + destroyAllChannels ( client, & client->chanList ); + destroyAllChannels ( client, & client->chanPendingUpdateARList ); + + if ( client->evuser ) { + db_close_events (client->evuser); + } + + destroy_client ( client ); +} + +/* + * create_client () + */ +struct client * create_client ( SOCKET sock, int proto ) +{ + struct client *client; + int spaceAvailOnFreeList; + size_t spaceNeeded; + + /* + * stop further use of server if memory becomes scarse + */ + spaceAvailOnFreeList = freeListItemsAvail ( rsrvClientFreeList ) > 0 + && freeListItemsAvail ( rsrvSmallBufFreeListTCP ) > 0; + spaceNeeded = sizeof (struct client) + MAX_TCP; + if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { + epicsSocketDestroy ( sock ); + epicsPrintf ("CAS: no space in pool for a new client (below max block thresh)\n"); + return NULL; + } + + client = freeListCalloc ( rsrvClientFreeList ); + if ( ! client ) { + epicsSocketDestroy ( sock ); + epicsPrintf ("CAS: no space in pool for a new client (alloc failed)\n"); + return NULL; + } + + client->sock = sock; + client->proto = proto; + + client->blockSem = epicsEventCreate ( epicsEventEmpty ); + client->lock = epicsMutexCreate(); + client->putNotifyLock = epicsMutexCreate(); + client->chanListLock = epicsMutexCreate(); + client->eventqLock = epicsMutexCreate(); + if ( ! client->blockSem || ! client->lock || ! client->putNotifyLock || + ! client->chanListLock || ! client->eventqLock ) { + destroy_client ( client ); + return NULL; + } + + client->pUserName = NULL; + client->pHostName = NULL; + ellInit ( & client->chanList ); + ellInit ( & client->chanPendingUpdateARList ); + ellInit ( & client->putNotifyQue ); + memset ( (char *)&client->addr, 0, sizeof (client->addr) ); + client->tid = 0; + + if ( proto == IPPROTO_TCP ) { + client->send.buf = (char *) freeListCalloc ( rsrvSmallBufFreeListTCP ); + client->send.maxstk = MAX_TCP; + client->send.type = mbtSmallTCP; + client->recv.buf = (char *) freeListCalloc ( rsrvSmallBufFreeListTCP ); + client->recv.maxstk = MAX_TCP; + client->recv.type = mbtSmallTCP; + } + else if ( proto == IPPROTO_UDP ) { + client->send.buf = malloc ( MAX_UDP_SEND ); + client->send.maxstk = MAX_UDP_SEND; + client->send.type = mbtUDP; + client->recv.buf = malloc ( MAX_UDP_RECV ); + client->recv.maxstk = MAX_UDP_RECV; + client->recv.type = mbtUDP; + } + if ( ! client->send.buf || ! client->recv.buf ) { + destroy_client ( client ); + return NULL; + } + client->send.stk = 0u; + client->send.cnt = 0u; + client->recv.stk = 0u; + client->recv.cnt = 0u; + client->evuser = NULL; + client->priority = CA_PROTO_PRIORITY_MIN; + client->disconnect = FALSE; + epicsTimeGetCurrent ( &client->time_at_last_send ); + epicsTimeGetCurrent ( &client->time_at_last_recv ); + client->minor_version_number = CA_UKN_MINOR_VERSION; + client->recvBytesToDrain = 0u; + + return client; +} + +void casAttachThreadToClient ( struct client *pClient ) +{ + epicsSignalInstallSigAlarmIgnore (); + epicsSignalInstallSigPipeIgnore (); + pClient->tid = epicsThreadGetIdSelf (); + epicsThreadPrivateSet ( rsrvCurrentClient, pClient ); + taskwdInsert ( pClient->tid, NULL, NULL ); +} + +void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ) +{ + if ( pClient->send.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP + && size <= rsrvSizeofLargeBufTCP ) { + int spaceAvailOnFreeList = freeListItemsAvail ( rsrvLargeBufFreeListTCP ) > 0; + if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { + char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); + if ( pNewBuf ) { + memcpy ( pNewBuf, pClient->send.buf, pClient->send.stk ); + freeListFree ( rsrvSmallBufFreeListTCP, pClient->send.buf ); + pClient->send.buf = pNewBuf; + pClient->send.maxstk = rsrvSizeofLargeBufTCP; + pClient->send.type = mbtLargeTCP; + } + } + } +} + +void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ) +{ + if ( pClient->recv.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP + && size <= rsrvSizeofLargeBufTCP) { + int spaceAvailOnFreeList = freeListItemsAvail ( rsrvLargeBufFreeListTCP ) > 0; + if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { + char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); + if ( pNewBuf ) { + assert ( pClient->recv.cnt >= pClient->recv.stk ); + memcpy ( pNewBuf, &pClient->recv.buf[pClient->recv.stk], pClient->recv.cnt - pClient->recv.stk ); + freeListFree ( rsrvSmallBufFreeListTCP, pClient->recv.buf ); + pClient->recv.buf = pNewBuf; + pClient->recv.cnt = pClient->recv.cnt - pClient->recv.stk; + pClient->recv.stk = 0u; + pClient->recv.maxstk = rsrvSizeofLargeBufTCP; + pClient->recv.type = mbtLargeTCP; + } + } + } +} + +/* + * create_tcp_client () + */ +struct client *create_tcp_client ( SOCKET sock ) +{ + int status; + struct client *client; + int intTrue = TRUE; + osiSocklen_t addrSize; + unsigned priorityOfEvents; + + /* socket passed in is destroyed here if unsuccessful */ + client = create_client ( sock, IPPROTO_TCP ); + if ( ! client ) { + return NULL; + } + + /* + * see TCP(4P) this seems to make unsolicited single events much + * faster. I take care of queue up as load increases. + */ + status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, + (char *) &intTrue, sizeof (intTrue) ); + if (status < 0) { + errlogPrintf ( "CAS: TCP_NODELAY option set failed\n" ); + destroy_client ( client ); + return NULL; + } + + /* + * turn on KEEPALIVE so if the client crashes + * this task will find out and exit + */ + status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, + (char *) &intTrue, sizeof (intTrue) ); + if ( status < 0 ) { + errlogPrintf ( "CAS: SO_KEEPALIVE option set failed\n" ); + destroy_client ( client ); + return NULL; + } + + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made + * + * joh 11-10-98 + */ +#if 0 + /* + * set TCP buffer sizes to be synergistic + * with CA internal buffering + */ + i = MAX_MSG_SIZE; + status = setsockopt ( sock, SOL_SOCKET, SO_SNDBUF, (char *) &i, sizeof (i) ); + if (status < 0) { + errlogPrintf ( "CAS: SO_SNDBUF set failed\n" ); + destroy_client ( client ); + return NULL; + } + i = MAX_MSG_SIZE; + status = setsockopt ( sock, SOL_SOCKET, SO_RCVBUF, (char *) &i, sizeof (i) ); + if (status < 0) { + errlogPrintf ( "CAS: SO_RCVBUF set failed\n" ); + destroy_client ( client ); + return NULL; + } +#endif + + addrSize = sizeof ( client->addr ); + status = getpeername ( sock, (struct sockaddr *)&client->addr, + &addrSize ); + if ( status < 0 ) { + epicsPrintf ("CAS: peer address fetch failed\n"); + destroy_tcp_client (client); + return NULL; + } + + client->evuser = (struct event_user *) db_init_events (); + if ( ! client->evuser ) { + errlogPrintf ("CAS: unable to init the event facility\n"); + destroy_tcp_client (client); + return NULL; + } + + status = db_add_extra_labor_event ( client->evuser, rsrv_extra_labor, client ); + if (status != DB_EVENT_OK) { + errlogPrintf("CAS: unable to setup the event facility\n"); + destroy_tcp_client (client); + return NULL; + } + + { + epicsThreadBooleanStatus tbs; + + tbs = epicsThreadHighestPriorityLevelBelow ( epicsThreadPriorityCAServerLow, &priorityOfEvents ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + priorityOfEvents = epicsThreadPriorityCAServerLow; + } + } + + status = db_start_events ( client->evuser, "CAS-event", + NULL, NULL, priorityOfEvents ); + if ( status != DB_EVENT_OK ) { + errlogPrintf ( "CAS: unable to start the event facility\n" ); + destroy_tcp_client ( client ); + return NULL; + } + + /* + * add first version message should it be needed + */ + rsrv_version_reply ( client ); + + if ( CASDEBUG > 0 ) { + char buf[64]; + ipAddrToDottedIP ( &client->addr, buf, sizeof(buf) ); + errlogPrintf ( "CAS: conn req from %s\n", buf ); + } + + return client; +} + +void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount ) +{ + LOCK_CLIENTQ; + { + int circuitCount = ellCount ( &clientQ ); + if ( circuitCount < 0 ) { + *pCircuitCount = 0; + } + else { + *pCircuitCount = (unsigned) circuitCount; + } + *pChanCount = rsrvChannelCount; + } + UNLOCK_CLIENTQ; +} diff --git a/src/rsrv/cast_server.c b/src/rsrv/cast_server.c new file mode 100644 index 000000000..27cbea355 --- /dev/null +++ b/src/rsrv/cast_server.c @@ -0,0 +1,316 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 5-88 + * + * Improvements + * ------------ + * .01 + * Dont send channel found message unless there is memory, a task slot, + * and a TCP socket available. Send a diagnostic instead. + * Or ... make the timeout shorter? This is only a problem if + * they persist in trying to make a connection after getting no + * response. + * + * Notes: + * ------ + * .01 + * Replies to broadcasts are not returned over + * an existing TCP connection to avoid a TCP + * pend which could lock up the cast server. + */ + + +#include +#include +#include +#include +#include + +#include "osiSock.h" +#include "epicsMutex.h" +#include "dbDefs.h" +#include "errlog.h" +#include "taskwd.h" +#include "epicsTime.h" +#include "envDefs.h" +#include "freeList.h" + +#define epicsExportSharedSymbols +#include "server.h" +#include "rsrv.h" + +#define TIMEOUT 60.0 /* sec */ + +/* + * clean_addrq + */ +static void clean_addrq(void) +{ + struct channel_in_use * pciu; + struct channel_in_use * pnextciu; + epicsTimeStamp current; + double delay; + double maxdelay = 0; + unsigned ndelete=0; + double timeout = TIMEOUT; + int s; + + epicsTimeGetCurrent ( ¤t ); + + epicsMutexMustLock ( prsrv_cast_client->chanListLock ); + pnextciu = (struct channel_in_use *) + prsrv_cast_client->chanList.node.next; + + while( (pciu = pnextciu) ) { + pnextciu = (struct channel_in_use *)pciu->node.next; + + delay = epicsTimeDiffInSeconds(¤t,&pciu->time_at_creation); + if (delay > timeout) { + + ellDelete(&prsrv_cast_client->chanList, &pciu->node); + LOCK_CLIENTQ; + s = bucketRemoveItemUnsignedId ( + pCaBucket, + &pciu->sid); + if(s){ + errMessage (s, "Bad id at close"); + } + else { + rsrvChannelCount--; + } + UNLOCK_CLIENTQ; + if ( ! s ) { + freeListFree(rsrvChanFreeList, pciu); + ndelete++; + } + if(delay>maxdelay) maxdelay = delay; + } + } + epicsMutexUnlock ( prsrv_cast_client->chanListLock ); + +# ifdef DEBUG + if(ndelete){ + epicsPrintf ("CAS: %d CA channels have expired after %f sec\n", + ndelete, maxdelay); + } +# endif + +} + +/* + * CAST_SERVER + * + * service UDP messages + * + */ +void cast_server(void *pParm) +{ + struct sockaddr_in sin; + int status; + int count=0; + struct sockaddr_in new_recv_addr; + osiSocklen_t recv_addr_size; + unsigned short port; + osiSockIoctl_t nchars; + + if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { + port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, + (unsigned short) CA_SERVER_PORT ); + } + else { + port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, + (unsigned short) CA_SERVER_PORT ); + } + + recv_addr_size = sizeof(new_recv_addr); + + if( IOC_cast_sock!=0 && IOC_cast_sock!=INVALID_SOCKET ) { + epicsSocketDestroy ( IOC_cast_sock ); + } + + /* + * Open the socket. + * Use ARPA Internet address format and datagram socket. + */ + + if ( ( IOC_cast_sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0) ) == INVALID_SOCKET ) { + epicsPrintf ("CAS: cast socket creation error\n"); + epicsThreadSuspendSelf (); + } + + /* + * some concern that vxWorks will run out of mBuf's + * if this change is made + * + * joh 11-10-98 + */ +#if 0 + { + /* + * + * this allows for faster connects by queuing + * additional incomming UDP search frames + * + * this allocates a 32k buffer + * (uses a power of two) + */ + int size = 1u<<15u; + status = setsockopt (IOC_cast_sock, SOL_SOCKET, + SO_RCVBUF, (char *)&size, sizeof(size)); + if (status<0) { + epicsPrintf ("CAS: unable to set cast socket size\n"); + } + } +#endif + + epicsSocketEnableAddressUseForDatagramFanout ( IOC_cast_sock ); + + /* Zero the sock_addr structure */ + memset((char *)&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(port); + + /* get server's Internet address */ + if( bind(IOC_cast_sock, (struct sockaddr *)&sin, sizeof (sin)) < 0){ + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsPrintf ("CAS: UDP server port bind error was \"%s\"\n", sockErrBuf ); + epicsSocketDestroy ( IOC_cast_sock ); + epicsThreadSuspendSelf (); + } + + /* + * setup new client structure but reuse old structure if + * possible + * + */ + while ( TRUE ) { + prsrv_cast_client = create_client ( IOC_cast_sock, IPPROTO_UDP ); + if ( prsrv_cast_client ) { + break; + } + epicsThreadSleep(300.0); + } + + casAttachThreadToClient ( prsrv_cast_client ); + + /* + * add placeholder for the first version message should it be needed + */ + rsrv_version_reply ( prsrv_cast_client ); + + epicsEventSignal(casudp_startStopEvent); + + while (TRUE) { + status = recvfrom ( + IOC_cast_sock, + prsrv_cast_client->recv.buf, + prsrv_cast_client->recv.maxstk, + 0, + (struct sockaddr *)&new_recv_addr, + &recv_addr_size); + if (status < 0) { + if (SOCKERRNO != SOCK_EINTR) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + epicsPrintf ("CAS: UDP recv error (errno=%s)\n", + sockErrBuf); + epicsThreadSleep(1.0); + } + } + else if (casudp_ctl == ctlRun) { + prsrv_cast_client->recv.cnt = (unsigned) status; + prsrv_cast_client->recv.stk = 0ul; + epicsTimeGetCurrent(&prsrv_cast_client->time_at_last_recv); + + prsrv_cast_client->minor_version_number = 0; + prsrv_cast_client->seqNoOfReq = 0; + + /* + * If we are talking to a new client flush to the old one + * in case we are holding UDP messages waiting to + * see if the next message is for this same client. + */ + if (prsrv_cast_client->send.stk>sizeof(caHdr)) { + status = memcmp( (void *)&prsrv_cast_client->addr, (void *)&new_recv_addr, recv_addr_size); + if(status){ + /* + * if the address is different + */ + cas_send_dg_msg(prsrv_cast_client); + prsrv_cast_client->addr = new_recv_addr; + } + } + else { + prsrv_cast_client->addr = new_recv_addr; + } + + if (CASDEBUG>1) { + char buf[40]; + + ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf)); + errlogPrintf ("CAS: cast server msg of %d bytes from addr %s\n", + prsrv_cast_client->recv.cnt, buf); + } + + if (CASDEBUG>2) + count = ellCount (&prsrv_cast_client->chanList); + + status = camessage ( prsrv_cast_client ); + if(status == RSRV_OK){ + if(prsrv_cast_client->recv.cnt != + prsrv_cast_client->recv.stk){ + char buf[40]; + + ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf)); + + epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n", + prsrv_cast_client->recv.cnt-prsrv_cast_client->recv.stk, buf); + } + } + else { + char buf[40]; + + ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf)); + + epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf); + } + + if (CASDEBUG>2) { + if ( ellCount (&prsrv_cast_client->chanList) ) { + errlogPrintf ("CAS: Fnd %d name matches (%d tot)\n", + ellCount(&prsrv_cast_client->chanList)-count, + ellCount(&prsrv_cast_client->chanList)); + } + } + } + + /* + * allow messages to batch up if more are comming + */ + nchars = 0; /* supress purify warning */ + status = socket_ioctl(IOC_cast_sock, FIONREAD, &nchars); + if (status<0) { + errlogPrintf ("CA cast server: Unable to fetch N characters pending\n"); + cas_send_dg_msg (prsrv_cast_client); + clean_addrq (); + } + else if (nchars == 0) { + cas_send_dg_msg (prsrv_cast_client); + clean_addrq (); + } + } +} diff --git a/src/rsrv/online_notify.c b/src/rsrv/online_notify.c new file mode 100644 index 000000000..76631c519 --- /dev/null +++ b/src/rsrv/online_notify.c @@ -0,0 +1,302 @@ +/*************************************************************************\ +* 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 + * + * tell CA clients this a server has joined the network + * + * Author: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 103090 + * + */ + +#include +#include +#include +#include +#include + +/* + * EPICS includes + */ +#include "dbDefs.h" +#include "osiSock.h" +#include "errlog.h" +#include "envDefs.h" +#include "addrList.h" +#include "taskwd.h" + +#define epicsExportSharedSymbols +#include "server.h" + +/* + * forcePort () + */ +static void forcePort (ELLLIST *pList, unsigned short port) +{ + osiSockAddrNode *pNode; + + pNode = (osiSockAddrNode *) ellFirst ( pList ); + while ( pNode ) { + if ( pNode->addr.sa.sa_family == AF_INET ) { + pNode->addr.ia.sin_port = htons ( port ); + } + pNode = (osiSockAddrNode *) ellNext ( &pNode->node ); + } +} + +/* + * RSRV_ONLINE_NOTIFY_TASK + */ +void rsrv_online_notify_task(void *pParm) +{ + unsigned priorityOfSelf = epicsThreadGetPrioritySelf (); + osiSockAddrNode *pNode; + double delay; + double maxdelay; + long longStatus; + double maxPeriod; + caHdr msg; + int status; + SOCKET sock; + int intTrue = TRUE; + unsigned short port; + ca_uint32_t beaconCounter = 0; + char * pStr; + int autoBeaconAddr; + ELLLIST autoAddrList; + char buf[16]; + unsigned priorityOfUDP; + epicsThreadBooleanStatus tbs; + epicsThreadId tid; + + taskwdInsert (epicsThreadGetIdSelf(),NULL,NULL); + + if ( envGetConfigParamPtr ( & EPICS_CAS_BEACON_PERIOD ) ) { + longStatus = envGetDoubleConfigParam ( & EPICS_CAS_BEACON_PERIOD, & maxPeriod ); + } + else { + longStatus = envGetDoubleConfigParam ( & EPICS_CA_BEACON_PERIOD, & maxPeriod ); + } + if (longStatus || maxPeriod<=0.0) { + maxPeriod = 15.0; + epicsPrintf ("EPICS \"%s\" float fetch failed\n", + EPICS_CAS_BEACON_PERIOD.name); + epicsPrintf ("Setting \"%s\" = %f\n", + EPICS_CAS_BEACON_PERIOD.name, maxPeriod); + } + + delay = 0.02; /* initial beacon period in sec */ + maxdelay = maxPeriod; + + /* + * Open the socket. + * Use ARPA Internet address format and datagram socket. + * Format described in . + */ + if ( (sock = epicsSocketCreate (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { + errlogPrintf ("CAS: online socket creation error\n"); + epicsThreadSuspendSelf (); + } + + status = setsockopt (sock, SOL_SOCKET, SO_BROADCAST, + (char *)&intTrue, sizeof(intTrue)); + if (status<0) { + errlogPrintf ("CAS: online socket set up error\n"); + epicsThreadSuspendSelf (); + } + + { + /* + * this connect is to supress a warning message on Linux + * when we shutdown the read side of the socket. If it + * fails (and it will on old ip kernels) we just ignore + * the failure. + */ + osiSockAddr sockAddr; + sockAddr.ia.sin_family = AF_UNSPEC; + sockAddr.ia.sin_port = htons ( 0 ); + sockAddr.ia.sin_addr.s_addr = htonl (0); + connect ( sock, & sockAddr.sa, sizeof ( sockAddr.sa ) ); + shutdown ( sock, SHUT_RD ); + } + + memset((char *)&msg, 0, sizeof msg); + msg.m_cmmd = htons (CA_PROTO_RSRV_IS_UP); + msg.m_count = htons (ca_server_port); + msg.m_dataType = htons (CA_MINOR_PROTOCOL_REVISION); + + ellInit ( & beaconAddrList ); + ellInit ( & autoAddrList ); + + pStr = envGetConfigParam(&EPICS_CAS_AUTO_BEACON_ADDR_LIST, sizeof(buf), buf); + if ( ! pStr ) { + pStr = envGetConfigParam(&EPICS_CA_AUTO_ADDR_LIST, sizeof(buf), buf); + } + if (pStr) { + if (strstr(pStr,"no")||strstr(pStr,"NO")) { + autoBeaconAddr = FALSE; + } + else if (strstr(pStr,"yes")||strstr(pStr,"YES")) { + autoBeaconAddr = TRUE; + } + else { + fprintf(stderr, + "CAS: EPICS_CA(S)_AUTO_ADDR_LIST = \"%s\"? Assuming \"YES\"\n", pStr); + autoBeaconAddr = TRUE; + } + } + else { + autoBeaconAddr = TRUE; + } + + /* + * load user and auto configured + * broadcast address list + */ + if (envGetConfigParamPtr(&EPICS_CAS_BEACON_PORT)) { + port = envGetInetPortConfigParam (&EPICS_CAS_BEACON_PORT, + (unsigned short) CA_REPEATER_PORT ); + } + else { + port = envGetInetPortConfigParam (&EPICS_CA_REPEATER_PORT, + (unsigned short) CA_REPEATER_PORT ); + } + + /* + * discover beacon addresses associated with this interface + */ + if ( autoBeaconAddr ) { + osiSockAddr addr; + ELLLIST tmpList; + + ellInit ( &tmpList ); + addr.ia.sin_family = AF_UNSPEC; + osiSockDiscoverBroadcastAddresses (&tmpList, sock, &addr); + forcePort ( &tmpList, port ); + removeDuplicateAddresses ( &autoAddrList, &tmpList, 1 ); + } + + /* + * by default use EPICS_CA_ADDR_LIST for the + * beacon address list + */ + { + const ENV_PARAM *pParam; + + if (envGetConfigParamPtr(&EPICS_CAS_INTF_ADDR_LIST) || + envGetConfigParamPtr(&EPICS_CAS_BEACON_ADDR_LIST)) { + pParam = &EPICS_CAS_BEACON_ADDR_LIST; + } + else { + pParam = &EPICS_CA_ADDR_LIST; + } + + /* + * add in the configured addresses + */ + addAddrToChannelAccessAddressList ( + &autoAddrList, pParam, port, pParam == &EPICS_CA_ADDR_LIST ); + } + + removeDuplicateAddresses ( &beaconAddrList, &autoAddrList, 0 ); + + if ( ellCount ( &beaconAddrList ) == 0 ) { + errlogPrintf ("The CA server's beacon address list was empty after initialization?\n"); + } + +# ifdef DEBUG + printChannelAccessAddressList (&beaconAddrList); +# endif + + tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfSelf, &priorityOfUDP ); + if ( tbs != epicsThreadBooleanStatusSuccess ) { + priorityOfUDP = priorityOfSelf; + } + + casudp_startStopEvent = epicsEventMustCreate(epicsEventEmpty); + casudp_ctl = ctlPause; + + tid = epicsThreadCreate ( "CAS-UDP", priorityOfUDP, + epicsThreadGetStackSize (epicsThreadStackMedium), + cast_server, 0 ); + if ( tid == 0 ) { + epicsPrintf ( "CAS: unable to start UDP daemon thread\n" ); + } + + epicsEventMustWait(casudp_startStopEvent); + epicsEventSignal(beacon_startStopEvent); + + while (TRUE) { + pNode = (osiSockAddrNode *) ellFirst (&beaconAddrList); + while (pNode) { + char buf[64]; + + status = connect (sock, &pNode->addr.sa, + sizeof(pNode->addr.sa)); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf)); + errlogPrintf ( "%s: CA beacon routing (connect to \"%s\") error was \"%s\"\n", + __FILE__, buf, sockErrBuf); + } + else { + struct sockaddr_in if_addr; + + osiSocklen_t size = sizeof (if_addr); + status = getsockname (sock, (struct sockaddr *) &if_addr, &size); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + errlogPrintf ( "%s: CA beacon routing (getsockname) error was \"%s\"\n", + __FILE__, sockErrBuf); + } + else if (if_addr.sin_family==AF_INET) { + msg.m_available = if_addr.sin_addr.s_addr; + msg.m_cid = htonl ( beaconCounter ); + + status = send (sock, (char *)&msg, sizeof(msg), 0); + if (status < 0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + ipAddrToDottedIP (&pNode->addr.ia, buf, sizeof(buf)); + errlogPrintf ( "%s: CA beacon (send to \"%s\") error was \"%s\"\n", + __FILE__, buf, sockErrBuf); + } + else { + assert (status == sizeof(msg)); + } + } + } + pNode = (osiSockAddrNode *) pNode->node.next; + } + + epicsThreadSleep(delay); + if (delaymaxdelay) { + delay = maxdelay; + } + } + + beaconCounter++; /* expected to overflow */ + + while (beacon_ctl == ctlPause) { + epicsThreadSleep(0.1); + delay = 0.02; /* Restart beacon timing if paused */ + } + } +} + + diff --git a/src/rsrv/rsrv.h b/src/rsrv/rsrv.h new file mode 100644 index 000000000..2d939af2d --- /dev/null +++ b/src/rsrv/rsrv.h @@ -0,0 +1,39 @@ +/*************************************************************************\ +* 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: Jeffrey O. Hill + * hill@luke.lanl.gov + * (505) 665 1831 + * Date: 5-88 + */ + +#ifndef rsrvh +#define rsrvh + +#include "shareLib.h" + +epicsShareFunc int rsrv_init(void); +epicsShareFunc int rsrv_run(void); +epicsShareFunc int rsrv_pause(void); + +epicsShareFunc void epicsShareAPI casr (unsigned level); +epicsShareFunc void epicsShareAPI casHostNameInitiatingCurrentThread ( + char * pBuf, unsigned bufSize ); +epicsShareFunc void epicsShareAPI casUserNameInitiatingCurrentThread ( + char * pBuf, unsigned bufSize ); +void casStatsFetch ( unsigned *pChanCount, unsigned *pConnCount ); + +#define RSRV_OK 0 +#define RSRV_ERROR (-1) + +#endif /*rsrvh */ diff --git a/src/rsrv/rsrvIoc.rc b/src/rsrv/rsrvIoc.rc new file mode 100755 index 000000000..758096e16 --- /dev/null +++ b/src/rsrv/rsrvIoc.rc @@ -0,0 +1,36 @@ +#include +#include "epicsVersion.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + PRODUCTVERSION EPICS_VERSION,EPICS_REVISION,EPICS_MODIFICATION,EPICS_PATCH_LEVEL + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_UNKNOWN + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments","Channel Access Ioc Resource Server Library for EPICS\0" + VALUE "CompanyName", "The EPICS collaboration\0" + VALUE "FileDescription", "Channel Access Ioc Resource Server Library\0" + VALUE "FileVersion", EPICS_VERSION_STRING "\0" + VALUE "InternalName", "rsrv\0" + VALUE "LegalCopyright", "Copyright (C) Univ. of California, Univ. of Chicago\0" + VALUE "OriginalFilename", "rsrv.dll\0" + VALUE "ProductName", "Experimental Physics and Industrial Control System (EPICS)\0" + VALUE "ProductVersion", EPICS_VERSION_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/rsrv/rsrvIocRegister.c b/src/rsrv/rsrvIocRegister.c new file mode 100644 index 000000000..a713a0bd7 --- /dev/null +++ b/src/rsrv/rsrvIocRegister.c @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2007 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "iocsh.h" +#define epicsExportSharedSymbols +#include "rsrv.h" +#include "rsrvIocRegister.h" + +/* casr */ +static const iocshArg casrArg0 = { "level",iocshArgInt}; +static const iocshArg * const casrArgs[1] = {&casrArg0}; +static const iocshFuncDef casrFuncDef = {"casr",1,casrArgs}; +static void casrCallFunc(const iocshArgBuf *args) +{ + casr(args[0].ival); +} + + +void epicsShareAPI rsrvIocRegister(void) +{ + iocshRegister(&casrFuncDef,casrCallFunc); +} diff --git a/src/rsrv/rsrvIocRegister.h b/src/rsrv/rsrvIocRegister.h new file mode 100644 index 000000000..5f83a03d3 --- /dev/null +++ b/src/rsrv/rsrvIocRegister.h @@ -0,0 +1,25 @@ +/*************************************************************************\ +* Copyright (c) 2007 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_rsrvIocRegister_H +#define INC_rsrvIocRegister_H + +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void epicsShareAPI rsrvIocRegister(void); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_rsrvIocRegister_H */ diff --git a/src/rsrv/server.h b/src/rsrv/server.h new file mode 100644 index 000000000..6d1dfc38f --- /dev/null +++ b/src/rsrv/server.h @@ -0,0 +1,232 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Jeffrey O. Hill + * + */ + +#ifndef INCLserverh +#define INCLserverh + +#ifdef epicsExportSharedSymbols +# define rsrvRestore_epicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif /* ifdef epicsExportSharedSymbols */ + +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsEvent.h" +#include "bucketLib.h" +#include "asLib.h" +#include "dbAddr.h" +#include "dbNotify.h" +#define CA_MINOR_PROTOCOL_REVISION 13 +#include "caProto.h" +#include "ellLib.h" +#include "epicsTime.h" +#include "epicsAssert.h" + +#ifdef rsrvRestore_epicsExportSharedSymbols +#define epicsExportSharedSymbols +#endif + +/* a modified ca header with capacity for large arrays */ +typedef struct caHdrLargeArray { + ca_uint32_t m_postsize; /* size of message extension */ + ca_uint32_t m_count; /* operation data count */ + ca_uint32_t m_cid; /* channel identifier */ + ca_uint32_t m_available; /* protocol stub dependent */ + ca_uint16_t m_dataType; /* operation data type */ + ca_uint16_t m_cmmd; /* operation to be performed */ +} caHdrLargeArray; + +/* + * !! buf must be the first item in this structure !! + * This guarantees that buf will have 8 byte natural + * alignment + * + * The terminating unsigned pad0 field is there to force the + * length of the message_buffer to be a multiple of 8 bytes. + * This is due to the sequential placing of two message_buffer + * structures (trans, rec) within the client structure. + * Eight-byte alignment is required by the Sparc 5 and other RISC + * processors. + */ +enum messageBufferType { mbtUDP, mbtSmallTCP, mbtLargeTCP }; +struct message_buffer { + char *buf; + unsigned stk; + unsigned maxstk; + unsigned cnt; + enum messageBufferType type; +}; + +extern epicsThreadPrivateId rsrvCurrentClient; + +typedef struct client { + ELLNODE node; + struct message_buffer send; + struct message_buffer recv; + epicsMutexId lock; + epicsMutexId putNotifyLock; + epicsMutexId chanListLock; + epicsMutexId eventqLock; + ELLLIST chanList; + ELLLIST chanPendingUpdateARList; + ELLLIST putNotifyQue; + struct sockaddr_in addr; + epicsTimeStamp time_at_last_send; + epicsTimeStamp time_at_last_recv; + void *evuser; + char *pUserName; + char *pHostName; + epicsEventId blockSem; /* used whenever the client blocks */ + SOCKET sock; + int proto; + epicsThreadId tid; + unsigned minor_version_number; + ca_uint32_t seqNoOfReq; /* for udp */ + unsigned recvBytesToDrain; + unsigned priority; + char disconnect; /* disconnect detected */ +} client; + +enum rsrvChanState { + rsrvCS_invalid, + rsrvCS_pendConnectResp, + rsrvCS_inService, + rsrvCS_pendConnectRespUpdatePendAR, + rsrvCS_inServiceUpdatePendAR +}; + +/* + * per channel structure + * (stored in chanList or chanPendingUpdateARList off of a client block) + */ +struct channel_in_use { + ELLNODE node; + ELLLIST eventq; + struct client *client; + struct rsrv_put_notify *pPutNotify; /* potential active put notify */ + const unsigned cid; /* client id */ + const unsigned sid; /* server id */ + epicsTimeStamp time_at_creation; /* for UDP timeout */ + struct dbAddr addr; + ASCLIENTPVT asClientPVT; + enum rsrvChanState state; +}; + +/* + * Event block extension for channel access + * some things duplicated for speed + */ +struct event_ext { + ELLNODE node; + caHdrLargeArray msg; + struct channel_in_use *pciu; + struct event_block *pdbev; /* ptr to db event block */ + unsigned size; /* for speed */ + unsigned mask; + char modified; /* mod & ev flw ctrl enbl */ +}; + + +enum ctl {ctlRun, ctlPause, ctlExit}; + +/* NOTE: external used so they remember the state across loads */ +#ifdef GLBLSOURCE +# define GLBLTYPE +# define GLBLTYPE_INIT(A) +#else +# define GLBLTYPE extern +# define GLBLTYPE_INIT(A) +#endif + +/* + * for debug-level dependent messages: + */ +#ifdef DEBUG +# define DLOG(LEVEL,ARGSINPAREN) \ + if (CASDEBUG > LEVEL) errlogPrintf ARGSINPAREN +#else +# define DLOG(LEVEL,ARGSINPAREN) +#endif + +GLBLTYPE int CASDEBUG; +GLBLTYPE SOCKET IOC_sock; +GLBLTYPE SOCKET IOC_cast_sock; +GLBLTYPE unsigned short ca_server_port; +GLBLTYPE ELLLIST clientQ; /* locked by clientQlock */ +GLBLTYPE ELLLIST beaconAddrList; +GLBLTYPE epicsMutexId clientQlock; +GLBLTYPE struct client *prsrv_cast_client; +GLBLTYPE BUCKET *pCaBucket; +GLBLTYPE void *rsrvClientFreeList; +GLBLTYPE void *rsrvChanFreeList; +GLBLTYPE void *rsrvEventFreeList; +GLBLTYPE void *rsrvSmallBufFreeListTCP; +GLBLTYPE void *rsrvLargeBufFreeListTCP; +GLBLTYPE unsigned rsrvSizeofLargeBufTCP; +GLBLTYPE void *rsrvPutNotifyFreeList; +GLBLTYPE unsigned rsrvChannelCount; + +GLBLTYPE epicsEventId casudp_startStopEvent; +GLBLTYPE epicsEventId beacon_startStopEvent; +GLBLTYPE epicsEventId castcp_startStopEvent; +GLBLTYPE volatile enum ctl casudp_ctl; +GLBLTYPE volatile enum ctl beacon_ctl; +GLBLTYPE volatile enum ctl castcp_ctl; + + +#define CAS_HASH_TABLE_SIZE 4096 + +#define SEND_LOCK(CLIENT) epicsMutexMustLock((CLIENT)->lock) +#define SEND_UNLOCK(CLIENT) epicsMutexUnlock((CLIENT)->lock) + +#define LOCK_CLIENTQ epicsMutexMustLock (clientQlock); +#define UNLOCK_CLIENTQ epicsMutexUnlock (clientQlock); + +void camsgtask (void *client); +void cas_send_bs_msg ( struct client *pclient, int lock_needed ); +void cas_send_dg_msg ( struct client *pclient ); +void rsrv_online_notify_task (void *); +void cast_server (void *); +struct client *create_client ( SOCKET sock, int proto ); +void destroy_client ( struct client * ); +struct client *create_tcp_client ( SOCKET sock ); +void destroy_tcp_client ( struct client * ); +void casAttachThreadToClient ( struct client * ); +int camessage ( struct client *client ); +void rsrv_extra_labor ( void * pArg ); +int rsrvCheckPut ( const struct channel_in_use *pciu ); +int rsrv_version_reply ( struct client *client ); +void rsrvFreePutNotify ( struct client *pClient, + struct rsrv_put_notify *pNotify ); +void initializePutNotifyFreeList (void); +unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ); + +/* + * inclming protocol maintetnance + */ +void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ); + +/* + * outgoing protocol maintenance + */ +void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ); +int cas_copy_in_header ( + struct client *pClient, ca_uint16_t response, ca_uint32_t payloadSize, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint32_t responseSpecific, void **pPayload ); +void cas_set_header_cid ( struct client *pClient, ca_uint32_t ); +void cas_set_header_count (struct client *pClient, ca_uint32_t count); +void cas_commit_msg ( struct client *pClient, ca_uint32_t size ); + +#endif /*INCLserverh*/ diff --git a/src/softIoc/Makefile b/src/softIoc/Makefile new file mode 100644 index 000000000..80989a450 --- /dev/null +++ b/src/softIoc/Makefile @@ -0,0 +1,39 @@ +########################################################################## +# Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +########################################################################## + +TOP=../.. + +include $(TOP)/configure/CONFIG + +PROD_IOC = softIoc + +DBD += softIoc.dbd +softIoc_DBD += base.dbd +softIoc_DBD += dlload.dbd +softIoc_DBD += system.dbd + +softIoc_SRCS += softIoc_registerRecordDeviceDriver.cpp +softIoc_SRCS_DEFAULT += softMain.cpp +softIoc_SRCS_vxWorks = -nil- + +softIoc_LIBS = $(EPICS_BASE_IOC_LIBS) + +DB += softIocExit.db + +FINAL_LOCATION ?= $(shell $(PERL) $(TOOLS)/fullPathName.pl $(INSTALL_LOCATION)) + +include $(TOP)/configure/RULES + +softMain$(OBJ) : epicsInstallDir.h + +epicsInstallDir.h: + $(ECHO) "FINAL_LOCATION=$(FINAL_LOCATION)" + $(PERL) ../makeInstallDir.pl '$(FINAL_LOCATION)' > $@ + +clean:: + @$(RM) epicsInstallDir.h + diff --git a/src/softIoc/makeInstallDir.pl b/src/softIoc/makeInstallDir.pl new file mode 100644 index 000000000..c9d6d2a75 --- /dev/null +++ b/src/softIoc/makeInstallDir.pl @@ -0,0 +1,25 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +use strict; + +die "Path to INSTALL_LOCATION missing\n" unless @ARGV == 1; + +my $path = shift; + +$path =~ s/\\/\\\\/gx; + +print "/* THIS IS A GENERATED FILE. DO NOT EDIT! */\n", + "\n", + "#ifndef INC_epicsInstallDir_H\n", + "#define INC_epicsInstallDir_H\n", + "\n", + "#define EPICS_BASE \"$path\"\n", + "\n", + "#endif /* INC_epicsInstallDir_H */\n"; diff --git a/src/softIoc/softIocExit.db b/src/softIoc/softIocExit.db new file mode 100644 index 000000000..e563f3f54 --- /dev/null +++ b/src/softIoc/softIocExit.db @@ -0,0 +1,7 @@ +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +record(sub,"$(IOC):exit") { + field(DESC,"Exit subroutine") + field(SCAN,"Passive") + field(SNAM,"exit") +} diff --git a/src/softIoc/softMain.cpp b/src/softIoc/softMain.cpp new file mode 100644 index 000000000..038891c18 --- /dev/null +++ b/src/softIoc/softMain.cpp @@ -0,0 +1,235 @@ +/*************************************************************************\ +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2003 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to the Software License Agreement +* found in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* Author: Andrew Johnson Date: 2003-04-08 */ + +/* Usage: + * softIoc [-D softIoc.dbd] [-h] [-S] [-s] [-a ascf] + * [-m macro=value,macro2=value2] [-d file.db] + * [-x prefix] [st.cmd] + * + * If used the -D option must come first, and specify the + * path to the softIoc.dbd file. The compile-time install + * location is saved in the binary as a default. + * + * Usage information will be printed if -h is given, then + * the program will exit normally. + * + * The -S option prevents an interactive shell being started + * after all arguments have been processed. + * + * Previous versions accepted a -s option to cause a shell + * to be started; this option is still accepted but ignored + * since a command shell is now started by default. + * + * Access Security can be enabled with the -a option giving + * the name of the configuration file; if any macros were + * set with -m before the -a option was given, they will be + * used as access security substitution macros. + * + * Any number of -m and -d arguments can be interspersed; + * the macros are applied to the following .db files. Each + * later -m option causes earlier macros to be discarded. + * + * The -x option loads the softIocExit.db with the macro + * IOC set to the string provided. This database contains + * a subroutine record named $(IOC):exit which has its field + * SNAM set to "exit". When this record is processed, the + * subroutine that runs will call epicsExit() with the value + * of the field A determining whether the exit status is + * EXIT_SUCCESS if (A == 0.0) or EXIT_FAILURE (A != 0.0). + * + * A st.cmd file is optional. If any databases were loaded + * the st.cmd file will be run *after* iocInit. To perform + * iocsh commands before iocInit, all database loading must + * be performed by the script itself, or by the user from + * the interactive IOC shell. + */ + +#include +#include +#include +#include +#include + +#include "registryFunction.h" +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsStdio.h" +#include "dbStaticLib.h" +#include "subRecord.h" +#include "dbAccess.h" +#include "asDbLib.h" +#include "iocInit.h" +#include "iocsh.h" +#include "epicsInstallDir.h" + +extern "C" int softIoc_registerRecordDeviceDriver(struct dbBase *pdbbase); + +#define DBD_FILE EPICS_BASE "/dbd/softIoc.dbd" +#define EXIT_FILE EPICS_BASE "/db/softIocExit.db" + +const char *arg0; +const char *base_dbd = DBD_FILE; +const char *exit_db = EXIT_FILE; + + +static void exitSubroutine(subRecord *precord) { + epicsExit((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static void usage(int status) { + printf("Usage: %s [-D softIoc.dbd] [-h] [-S] [-a ascf]\n", arg0); + puts("\t[-m macro=value,macro2=value2] [-d file.db]"); + puts("\t[-x prefix] [st.cmd]"); + puts("Compiled-in path to softIoc.dbd is:"); + printf("\t%s\n", base_dbd); + epicsExit(status); +} + + +int main(int argc, char *argv[]) +{ + char *dbd_file = const_cast(base_dbd); + char *macros = NULL; + char xmacro[PVNAME_STRINGSZ + 4]; + int startIocsh = 1; /* default = start shell */ + int loadedDb = 0; + + arg0 = strrchr(*argv, '/'); + if (!arg0) { + arg0 = *argv; + } else { + ++arg0; /* skip the '/' */ + } + + --argc, ++argv; + + /* Do this here in case the dbd file not available */ + if (argc>0 && **argv=='-' && (*argv)[1]=='h') { + usage(EXIT_SUCCESS); + } + + if (argc>1 && **argv=='-' && (*argv)[1]=='D') { + dbd_file = *++argv; + argc -= 2; + ++argv; + } + + if (dbLoadDatabase(dbd_file, NULL, NULL)) { + epicsExit(EXIT_FAILURE); + } + + softIoc_registerRecordDeviceDriver(pdbbase); + registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine); // X aCC 331 + + while (argc>1 && **argv == '-') { + switch ((*argv)[1]) { + case 'a': + if (macros) asSetSubstitutions(macros); + asSetFilename(*++argv); + --argc; + break; + + case 'd': + if (dbLoadRecords(*++argv, macros)) { + epicsExit(EXIT_FAILURE); + } + loadedDb = 1; + --argc; + break; + + case 'h': + usage(EXIT_SUCCESS); + + case 'm': + macros = *++argv; + --argc; + break; + + case 'S': + startIocsh = 0; + break; + + case 's': + break; + + case 'x': + epicsSnprintf(xmacro, sizeof xmacro, "IOC=%s", *++argv); + if (dbLoadRecords(exit_db, xmacro)) { + epicsExit(EXIT_FAILURE); + } + loadedDb = 1; + --argc; + break; + + default: + printf("%s: option '%s' not recognized\n", arg0, *argv); + usage(EXIT_FAILURE); + } + --argc; + ++argv; + } + + if (argc>0 && **argv=='-') { + switch((*argv)[1]) { + case 'a': + case 'd': + case 'm': + case 'x': + printf("%s: missing argument to option '%s'\n", arg0, *argv); + usage(EXIT_FAILURE); + + case 'h': + usage(EXIT_SUCCESS); + + case 'S': + startIocsh = 0; + break; + + case 's': + break; + + default: + printf("%s: option '%s' not recognized\n", arg0, *argv); + usage(EXIT_FAILURE); + } + --argc; + ++argv; + } + + if (loadedDb) { + iocInit(); + epicsThreadSleep(0.2); + } + + /* run user's startup script */ + if (argc>0) { + if (iocsh(*argv)) epicsExit(EXIT_FAILURE); + epicsThreadSleep(0.2); + loadedDb = 1; /* Give it the benefit of the doubt... */ + } + + /* start an interactive shell if it was requested */ + if (startIocsh) { + iocsh(NULL); + } else { + if (loadedDb) { + epicsThreadExitMain(); + } else { + printf("%s: Nothing to do!\n", arg0); + usage(EXIT_FAILURE); + } + } + epicsExit(EXIT_SUCCESS); + /*Note that the following statement will never be executed*/ + return 0; +} diff --git a/src/tools/EPICS/Copy.pm b/src/tools/EPICS/Copy.pm new file mode 100644 index 000000000..d6e55e9a5 --- /dev/null +++ b/src/tools/EPICS/Copy.pm @@ -0,0 +1,75 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is 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 + +# Copy directories and files from a template + +sub copyTree { + my ($src, $dst, $Rnamesubs, $Rtextsubs) = @_; + # $Rnamesubs contains substitutions for file names, + # $Rtextsubs contains substitutions for file content. + + opendir my $FILES, $src + or die "opendir failed while copying $src: $!\n"; + my @entries = readdir $FILES; + closedir $FILES; + + foreach (@entries) { + next if m/^\.\.?$/; # ignore . and .. + next if m/^CVS$/; # Shouldn't exist, but... + + my $srcName = "$src/$_"; + + # Substitute any _VARS_ in the name + s/@(\w+?)@/$Rnamesubs->{$1} || "@$1@"/eg; + my $dstName = "$dst/$_"; + + if (-d $srcName) { + print ":" unless $opt_d; + copyDir($srcName, $dstName, $Rnamesubs, $Rtextsubs); + } elsif (-f $srcName) { + print "." unless $opt_d; + copyFile($srcName, $dstName, $Rtextsubs); + } elsif (-l $srcName) { + warn "\nSoft link in template, ignored:\n\t$srcName\n"; + } else { + warn "\nUnknown file type in template, ignored:\n\t$srcName\n"; + } + } +} + +sub copyDir { + my ($src, $dst, $Rnamesubs, $Rtextsubs) = @_; + if (-e $dst && ! -d $dst) { + warn "\nTarget exists but is not a directory, skipping:\n\t$dst\n"; + return; + } + print "Creating directory '$dst'\n" if $opt_d; + mkdir $dst, 0777 or die "Can't create $dst: $!\n" + unless -d $dst; + copyTree($src, $dst, $Rnamesubs, $Rtextsubs); +} + +sub copyFile { + my ($src, $dst, $Rtextsubs) = @_; + return if (-e $dst); + print "Creating file '$dst'\n" if $opt_d; + open(my $SRC, '<', $src) + and open(my $DST, '>', $dst) + or die "$! copying $src to $dst\n"; + while (<$SRC>) { + # Substitute any @VARS@ in the text + s{@(\w+?)@} + {exists $Rtextsubs->{$1} ? $Rtextsubs->{$1} : "\@$1\@"}eg; + print $DST $_; + } + close $DST; + close $SRC; +} + +1; diff --git a/src/tools/EPICS/Getopts.pm b/src/tools/EPICS/Getopts.pm new file mode 100644 index 000000000..c08db62aa --- /dev/null +++ b/src/tools/EPICS/Getopts.pm @@ -0,0 +1,76 @@ +package EPICS::Getopts; +require 5.000; +require Exporter; + +@ISA = qw(Exporter); +@EXPORT = qw(getopts); + +# EPICS::Getopts.pm - An EPICS-specific version of getopts +# +# This version of getopts is modified from the Perl original in the +# following ways: +# +# 1. The perl routine in GetOpt::Std allows a perl hash to be passed +# in as a third argument and used for storing option values. This +# functionality has been deleted. +# 2. Arguments without a ':' modifier are now counted, rather than +# being binary. This means that multiple copies of the same option +# can be detected by the program. +# 3. A new '@' modifier is provided which collects the option arguments +# in an array @opt_X. Multiple copies of this option are permitted, +# which push the argument values onto the end of the list. + +sub getopts ( $;$ ) { + my ($argumentative) = @_; + my (@args,$first,$rest); + my $errs = 0; + local $_; + local @EXPORT; + + @args = split( / */, $argumentative ); + while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) { + ($first,$rest) = ($1,$2); + if (/^--$/) { # early exit if -- + shift @ARGV; + last; + } + $pos = index($argumentative,$first); + if ($pos >= 0) { + if (defined($args[$pos+1]) and (index(':@', $args[$pos+1]) >= 0)) { + shift(@ARGV); + if ($rest eq '') { + ++$errs unless @ARGV; + $rest = shift(@ARGV); + } + if ($args[$pos+1] eq ':') { + ${"opt_$first"} = $rest; + push @EXPORT, "\$opt_$first"; + } elsif ($args[$pos+1] eq '@') { + push @{"opt_$first"}, $rest; + push @EXPORT, "\@opt_$first"; + } + } else { + ${"opt_$first"} += 1; + push @EXPORT, "\$opt_$first"; + if ($rest eq '') { + shift(@ARGV); + } else { + $ARGV[0] = "-$rest"; + } + } + } else { + warn "Unknown option: $first\n"; + ++$errs; + if ($rest ne '') { + $ARGV[0] = "-$rest"; + } else { + shift(@ARGV); + } + } + } + local $Exporter::ExportLevel = 1; + import EPICS::Getopts; + $errs == 0; +} + +1; diff --git a/src/tools/EPICS/Path.pm b/src/tools/EPICS/Path.pm new file mode 100644 index 000000000..e7d027a6e --- /dev/null +++ b/src/tools/EPICS/Path.pm @@ -0,0 +1,145 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is 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 + +use Carp; +use Cwd qw(getcwd abs_path); +use File::Spec; + +=head1 EPICS::Path + +EPICS::Path - Path-handling utilities for EPICS tools + +=head1 SYNOPSIS + + use lib '@EPICS_BASE@/lib/perl'; + use EPICS::Path; + + my $dir = UnixPath('C:\Program Files\EPICS'); + print LocalPath($dir), "\n"; + print AbsPath('../lib', $dir); + +=head1 DESCRIPTION + +C provides functions for processing pathnames that are commonly +needed by EPICS tools. Windows is not the only culprit, some older automount +daemons insert strange prefixes into absolute directory paths that we have to +remove before storing the result for use later. + + +=head1 FUNCTIONS + +=over 4 + +=item UnixPath( I ) + +C should be used on any pathnames provided by external tools to +convert them into a form that Perl understands. + +On cygwin we convert Windows drive specs to the equivalent cygdrive path, and on +Windows we switch directory separators from back-slash to forward slashes. + +=cut + +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; + } + return $newpath; +} + +=item LocalPath( I ) + +C should be used when generating pathnames for external tools or to +put into a file. It converts paths from the Unix form that Perl understands to +any necessary external representation, and also removes automounter prefixes to +put the path into its canonical form. + +On cygwin we convert cygdrive paths to their equivalent Windows drive specs. +Before Leopard, the Mac OS X automounter inserted a verbose prefix, and in case +anyone is still using SunOS it adds its own prefix as well. + +=cut + +sub LocalPath { + my ($newpath) = @_; + if ($^O eq 'cygwin') { + $newpath =~ s{^/cygdrive/([a-zA-Z])/}{$1:/}; + } elsif ($^O eq 'darwin') { + # Darwin automounter + $newpath =~ s{^/private/var/auto\.}{/}; + } elsif ($^O eq 'sunos') { + # SunOS automounter + $newpath =~ s{^/tmp_mnt/}{/}; + } + return $newpath; +} + +=item AbsPath( I ) + +=item AbsPath( I, I ) + +The C function in Perl's C module doesn't like non-existent +path components other than in the final position, but EPICS tools needs to be +able to handle them in paths like F<$(TOP)/lib/$(T_A)> before the F<$(TOP)/lib> +directory has been created. + +C takes a path I and optionally an absolute path to a directory +that first is relative to; if the second argument is not provided the current +working directory is used. The result returned has been filtered through +C to remove any automounter prefixes. + +=cut + +sub AbsPath { + my ($path, $cwd) = @_; + + $path = '.' unless defined $path; + + if (defined $cwd) { + croak("'$cwd' is not an absolute path") + unless $cwd =~ m[^ / ]x; + } else { + $cwd = getcwd(); + } + + # Move leading ./ and ../ components from $path to $cwd + if (my ($dots, $not) = ($path =~ m[^ ( (?: \. \.? /+ )+ ) ( .* ) $]x)) { + $cwd .= "/$dots"; + $path = $not; + } + + # Handle any trailing .. part + if ($path eq '..') { + $cwd .= '/..'; + $path = '.' + } + + # Now calculate the absolute path + my $abs = File::Spec->rel2abs($path, abs_path($cwd)); + + return LocalPath($abs); +} + +=back + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2008 UChicago Argonne LLC, as Operator of Argonne National +Laboratory. + +This software is distributed under the terms of the EPICS Open License. + +=cut + + +1; diff --git a/src/tools/EPICS/Release.pm b/src/tools/EPICS/Release.pm new file mode 100644 index 000000000..0ebee63b8 --- /dev/null +++ b/src/tools/EPICS/Release.pm @@ -0,0 +1,113 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is 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 + +# +# Parse all relevent configure/RELEASE* files and includes +# +sub readReleaseFiles { + my ($relfile, $Rmacros, $Rapps, $arch) = @_; + + my $hostarch = $ENV{'EPICS_HOST_ARCH'}; + $Rmacros->{'EPICS_HOST_ARCH'} = $hostarch if $hostarch; + + return unless (-e $relfile); + &readRelease($relfile, $Rmacros, $Rapps); + + if ($hostarch) { + my $hrelfile = "$relfile.$hostarch"; + &readRelease($hrelfile, $Rmacros, $Rapps) if (-e $hrelfile); + $hrelfile .= '.Common'; + &readRelease($hrelfile, $Rmacros, $Rapps) if (-e $hrelfile); + } + + if ($arch) { + my $crelfile = "$relfile.Common.$arch"; + &readRelease($crelfile, $Rmacros, $Rapps) if (-e $crelfile); + + if ($hostarch) { + my $arelfile = "$relfile.$hostarch.$arch"; + &readRelease($arelfile, $Rmacros, $Rapps) if (-e $arelfile); + } + } +} + +# +# Parse a configure/RELEASE* file and anything it includes +# +sub readRelease { + my ($file, $Rmacros, $Rapps) = @_; + # $Rmacros is a reference to a hash, $Rapps a ref to an array + + open(my $IN, '<', $file) or die "Can't open $file: $!\n"; + while (<$IN>) { + chomp; + s/ \r $//x; # Shouldn't need this, but sometimes... + s/ # .* $//x; # Remove trailing comments + s/ \s+ $//x; # Remove trailing whitespace + next if m/^ \s* $/x; # Skip blank lines + + # Handle " = " plus the := and ?= variants + my ($macro, $op, $val) = m/^ \s* (\w+) \s* ([?:]?=) \s* (.*) /x; + if ($macro ne '') { + $macro = 'TOP' if $macro =~ m/^ INSTALL_LOCATION /x; + if (exists $Rmacros->{$macro}) { + next if $op eq '?='; + } else { + push @$Rapps, $macro; + } + $val = expandMacros($val, $Rmacros) if $op eq ':='; + $Rmacros->{$macro} = $val; + next; + } + # Handle "include " and "-include " syntax + my ($op, $path) = m/^ \s* (-? include) \s+ (.*)/x; + $path = expandMacros($path, $Rmacros); + if (-e $path) { + &readRelease($path, $Rmacros, $Rapps); + } elsif ($op eq "include") { + warn "EPICS/Release.pm: Include file '$path' not found\n"; + } + } + close $IN; +} + +# +# Expand all (possibly nested) macros in a string +# +sub expandMacros { + my ($str, $Rmacros) = @_; + # $Rmacros is a reference to a hash + + while (my ($pre, $var, $post) = $str =~ m/ (.*) \$\( (\w+) \) (.*) /x) { + last unless exists $Rmacros->{$var}; + $str = $pre . $Rmacros->{$var} . $post; + } + return $str; +} + +# +# Expand all (possibly nested) macros in dictionary +# +sub expandRelease { + my ($Rmacros) = @_; + # $Rmacros is a reference to a hash + + while (my ($macro, $val) = each %$Rmacros) { + while (my ($pre,$var,$post) = $val =~ m/ (.*) \$\( (\w+) \) (.*) /x) { + warn "EPICS/Release.pm: Undefined macro \$($var) used\n" + unless exists $Rmacros->{$var}; + die "EPICS/Release.pm: Circular definition of macro $macro\n" + if $macro eq $var; + $val = $pre . $Rmacros->{$var} . $post; + $Rmacros->{$macro} = $val; + } + } +} + +1; diff --git a/src/tools/Makefile b/src/tools/Makefile new file mode 100644 index 000000000..4dfc7aa1d --- /dev/null +++ b/src/tools/Makefile @@ -0,0 +1,36 @@ +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../.. + +include $(TOP)/configure/CONFIG + +# Bootstrap resolution: tools not installed yet +TOOLS = $(TOP)/src/tools + +PERL_MODULES += EPICS/Copy.pm +PERL_MODULES += EPICS/Path.pm +PERL_MODULES += EPICS/Release.pm +PERL_MODULES += EPICS/Getopts.pm + +PERL_SCRIPTS += convertRelease.pl +PERL_SCRIPTS += cvsclean.pl +PERL_SCRIPTS += dos2unix.pl +PERL_SCRIPTS += expandVars.pl +PERL_SCRIPTS += filterWarnings.pl +PERL_SCRIPTS += fullPathName.pl +PERL_SCRIPTS += installEpics.pl +PERL_SCRIPTS += makeDbDepends.pl +PERL_SCRIPTS += makeIncludeDbd.pl +PERL_SCRIPTS += makeMakefile.pl +PERL_SCRIPTS += makeTestfile.pl +PERL_SCRIPTS += mkmf.pl +PERL_SCRIPTS += munch.pl +PERL_SCRIPTS += replaceVAR.pl +PERL_SCRIPTS += useManifestTool.pl + +include $(TOP)/configure/RULES + diff --git a/src/tools/convertRelease.pl b/src/tools/convertRelease.pl new file mode 100644 index 000000000..68fa087c1 --- /dev/null +++ b/src/tools/convertRelease.pl @@ -0,0 +1,229 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # convertRelease.pl +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +# +# Revision-Id: anj@aps.anl.gov-20101026142747-yfjkhakzmp4rnj0g +# +# Convert configure/RELEASE file(s) into something else. +# + +use strict; + +use FindBin qw($Bin); +use lib ("$Bin/../../lib/perl", $Bin); + +use Cwd qw(cwd abs_path); +use Getopt::Std; +use EPICS::Path; +use EPICS::Release; + +use vars qw($arch $top $iocroot $root); + +our ($opt_a, $opt_t, $opt_T); + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; +getopts('a:t:T:') or &HELP_MESSAGE; + +my $cwd = UnixPath(cwd()); + +if ($opt_a) { + $arch = $opt_a; +} else { # Look for O. in current path + $cwd =~ m{ / O. ([\w-]+) $}x; + $arch = $1; +} + +if ($opt_T) { + $top = $opt_T; +} else { # Find $top from current path + # This approach only works inside iocBoot/* and configure/* + $top = $cwd; + $top =~ s{ / iocBoot .* $}{}x; + $top =~ s{ / configure .* $}{}x; +} + +# The IOC may need a different path to get to $top +if ($opt_t) { + $iocroot = $opt_t; + $root = $top; + while (substr($iocroot, -1, 1) eq substr($root, -1, 1)) { + chop $iocroot; + chop $root; + } +} + +&HELP_MESSAGE unless @ARGV == 1; + +my $outfile = $ARGV[0]; + +# TOP refers to this application +my %macros = (TOP => LocalPath($top)); +my @apps = ('TOP'); # Records the order of definitions in RELEASE file + +# Read the RELEASE file(s) +my $relfile = "$top/configure/RELEASE"; +die "Can't find $relfile" unless (-f $relfile); +readReleaseFiles($relfile, \%macros, \@apps, $arch); +expandRelease(\%macros, \@apps); + + +# This is a perl switch statement: +for ($outfile) { + m/releaseTops/ and do { &releaseTops; last; }; + m/dllPath\.bat/ and do { &dllPath; last; }; + m/cdCommands/ and do { &cdCommands; last; }; + m/envPaths/ and do { &envPaths; last; }; + m/checkRelease/ and do { &checkRelease; last; }; + die "Output file type \'$outfile\' not supported"; +} + + +############### Subroutines only below here ############### + +sub HELP_MESSAGE { + print STDERR <$outfile") or die "$! creating $outfile"; + print OUT "\@ECHO OFF\n"; + print OUT "PATH %PATH%;", join(';', binDirs()), "\n"; + close OUT; +} + +sub binDirs { + my @includes = grep !m/^ (RULES | TEMPLATE_TOP) $/x, @apps; + my @path; + foreach my $app (@includes) { + my $path = $macros{$app} . "/bin/$arch"; + next unless -d $path; + $path =~ s/^$root/$iocroot/o if ($opt_t); + push @path, LocalPath($path); + } + return @path; +} + +# +# Generate cdCommands file with cd path strings for vxWorks IOCs and +# RTEMS IOCs using CEXP (need parentheses around command arguments). +# +sub cdCommands { + die "Architecture not set (use -a option)" unless ($arch); + my @includes = grep !m/^(RULES | TEMPLATE_TOP)$/x, @apps; + + unlink($outfile); + open(OUT,">$outfile") or die "$! creating $outfile"; + + my $startup = $cwd; + $startup =~ s/^$root/$iocroot/o if ($opt_t); + + print OUT "startup = \"$startup\"\n"; + + my $ioc = $cwd; + $ioc =~ s/^.*\///; # iocname is last component of directory name + + print OUT "putenv(\"ARCH=$arch\")\n"; + print OUT "putenv(\"IOC=$ioc\")\n"; + + foreach my $app (@includes) { + my $iocpath = my $path = $macros{$app}; + $iocpath =~ s/^$root/$iocroot/o if ($opt_t); + my $app_lc = lc($app); + print OUT "$app_lc = \"$iocpath\"\n" + if (-d $path); + print OUT "putenv(\"$app=$iocpath\")\n" + if (-d $path); + print OUT "${app_lc}bin = \"$iocpath/bin/$arch\"\n" + if (-d "$path/bin/$arch"); + } + close OUT; +} + +# +# Generate envPaths file with epicsEnvSet commands for iocsh IOCs. +# Include parentheses anyway in case CEXP users want to use this. +# +sub envPaths { + die "Architecture not set (use -a option)" unless ($arch); + my @includes = grep !m/^ (RULES | TEMPLATE_TOP) $/x, @apps; + + unlink($outfile); + open(OUT,">$outfile") or die "$! creating $outfile"; + + my $ioc = $cwd; + $ioc =~ s/^.*\///; # iocname is last component of directory name + + print OUT "epicsEnvSet(\"ARCH\",\"$arch\")\n"; + print OUT "epicsEnvSet(\"IOC\",\"$ioc\")\n"; + + foreach my $app (@includes) { + my $iocpath = my $path = $macros{$app}; + $iocpath =~ s/^$root/$iocroot/o if ($opt_t); + print OUT "epicsEnvSet(\"$app\",\"$iocpath\")\n" if (-d $path); + } + close OUT; +} + +# +# Check RELEASE file consistency with support modules +# +sub checkRelease { + my $status = 0; + delete $macros{RULES}; + delete $macros{TOP}; + delete $macros{TEMPLATE_TOP}; + + while (my ($app, $path) = each %macros) { + my %check = (TOP => $path); + my @order = (); + my $relfile = "$path/configure/RELEASE"; + readReleaseFiles($relfile, \%check, \@order, $arch); + expandRelease(\%check, \@order); + delete $check{TOP}; + delete $check{EPICS_HOST_ARCH}; + + while (my ($parent, $ppath) = each %check) { + if (exists $macros{$parent} && + abs_path($macros{$parent}) ne abs_path($ppath)) { + print "\n" unless ($status); + print "Definition of $parent conflicts with $app support.\n"; + print "In this application a RELEASE file defines\n"; + print "\t$parent = $macros{$parent}\n"; + print "but $app at $path defines\n"; + print "\t$parent = $ppath\n"; + $status = 1; + } + } + } + print "\n" if ($status); + exit $status; +} + diff --git a/src/tools/cvsclean.pl b/src/tools/cvsclean.pl new file mode 100644 index 000000000..d3485e7ec --- /dev/null +++ b/src/tools/cvsclean.pl @@ -0,0 +1,21 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # cvsclean.pl +#************************************************************************* +# 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. +# This file is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* + +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Find and delete cvs .#* and editor backup *~ +# files from all dirs of the directory tree. + +use File::Find; + +@ARGV = ('.') unless @ARGV; + +find sub { unlink if -f && m/(^\.\#)|(~$)/ }, @ARGV; diff --git a/src/tools/dos2unix.pl b/src/tools/dos2unix.pl new file mode 100644 index 000000000..64117fe78 --- /dev/null +++ b/src/tools/dos2unix.pl @@ -0,0 +1,35 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # makeConfigAppInclude.pl +#************************************************************************* +# 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. +#************************************************************************* + +# Converts text file in DOS CR/LF format to unix ISO format +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +@files=@ARGV; + +$| = 1; +foreach( @files ) { + open(INPUT, "<$_"); + $backup = "$_.bak"; + rename( $_, $backup) || die "Unable to rename $_\n$!\n"; + # Make the output be binary so it won't convert /n back to /r/n + binmode OUTPUT, ":raw"; + open(OUTPUT, ">$_"); + binmode OUTPUT, ":raw"; + while() { + # Remove CR-LF sequences + s/\r\n/\n/; + print OUTPUT; + } + close INPUT; + close OUTPUT; + unlink ($backup) or die "Cannot remove $backup"; +} diff --git a/src/tools/expandVars.pl b/src/tools/expandVars.pl new file mode 100644 index 000000000..5c2ad4cda --- /dev/null +++ b/src/tools/expandVars.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl +# +# Tool to expand @VAR@ variables while copying a file. +# The file will *not* be copied if it already exists. +# +# Author: Andrew Johnson +# Date: 10 February 2005 +# +# Revision-Id: anj@aps.anl.gov-20101026142747-yfjkhakzmp4rnj0g +# + +use strict; + +use FindBin qw($Bin); +use lib ("$Bin/../../lib/perl", $Bin); + +use EPICS::Getopts; +use EPICS::Path; +use EPICS::Release; +use EPICS::Copy; + +# Process command line options +our ($opt_a, $opt_d, @opt_D, $opt_h, $opt_t); +getopts('a:dD@ht:') + or &HELP_MESSAGE; + +# Handle the -h command +&HELP_MESSAGE if $opt_h; + +die "Path to TOP not set, use -t option\n" + unless $opt_t; + +# Check filename arguments +my $infile = shift + or die "No input filename argument\n"; +my $outfile = shift + or die "No output filename argument\n"; + +# Where are we? +my $top = AbsPath($opt_t); +print "TOP = $top\n" if $opt_d; + +# Read RELEASE file into vars +my %vars = (TOP => $top); +my @apps = ('TOP'); +readReleaseFiles("$top/configure/RELEASE", \%vars, \@apps, $opt_a); +expandRelease(\%vars); + +$vars{'ARCH'} = $opt_a if $opt_a; + +while ($_ = shift @opt_D) { + my ($var, $val) = split /=/; + $vars{$var} = $val; + print "$var = $val\n" if $opt_d; +} + +# Do it! +copyFile($infile, $outfile, \%vars); + +##### File contains subroutines only below here + +sub HELP_MESSAGE { + print STDERR < 2>&1 | $0\n"; + + exit 2; +} + +# Invalid whit space filter on solaris not implemented +#if ( $ENV{"EPICS_HOST_ARCH"} =~ m/solaris-sparc/ ) { +# while ( $errline = <> ) { +# if ( $errline !~ m/invalid white space character in directive/ ) { +# print $errline; +# } +# } +#} else { + while ( $errline = <> ) { + if ( $errline =~ m/^(Warning|Error)/ ) { + ($errno) = ($errline =~ m/.* ([0-9]+):/); + $codeline = <>; + $pointline = <>; + next if $codeline =~ m|/[/*]\s*X.*aCC[^a-zA-Z]*$errno|; + + print wrap ("", " ", $errline); + print $codeline; + print $pointline; + } + } +#} diff --git a/src/tools/fullPathName.pl b/src/tools/fullPathName.pl new file mode 100644 index 000000000..20c70ca45 --- /dev/null +++ b/src/tools/fullPathName.pl @@ -0,0 +1,37 @@ +eval 'exec perl -S -w $0 ${1+"$@"}' # -*- Mode: perl -*- + if 0; +#************************************************************************* +# Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# Revision-Id: anj@aps.anl.gov-20101026142747-yfjkhakzmp4rnj0g + +# Determines an absolute pathname for its argument, +# which may be either a relative or absolute path and +# might have trailing directory names that don't exist yet. + +use strict; + +use FindBin qw($Bin); +use lib ("$Bin/../../lib/perl", $Bin); + +use Getopt::Std; +use EPICS::Path; + +our ($opt_h); + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; +&HELP_MESSAGE if !getopts('h') || $opt_h || @ARGV != 1; + +my $path = AbsPath(shift); + +print "$path\n"; + + +sub HELP_MESSAGE { + print STDERR "Usage: fullPathName.pl [-h] pathname\n"; + exit 2; +} diff --git a/src/tools/installEpics.pl b/src/tools/installEpics.pl new file mode 100644 index 000000000..248e662f7 --- /dev/null +++ b/src/tools/installEpics.pl @@ -0,0 +1,118 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# InstallEpics.pl +# +# InstallEpics is used within makefiles to copy new versions of +# files into a destination directory. +# Based on installEpics shell script. +# +# 2-4-97 -kuk- +# +########################################################## + +use Getopt::Std; +use File::Path; +use File::Copy; + +$tool=$0; +$tool=~ s'.*[/\\].+''; # basename +$mode=0755; + +# get command line options +getopt "m"; +$mode = oct ($opt_m) if ($opt_m); + +# Complain about obsolete options: +Usage("unknown option given") if ($opt_g or $opt_o or $opt_c or $opt_s); + +$num_files = $#ARGV; +# at least two args required +Usage ("Nothing to install") if ($num_files < 1); + +# split args in file1 ... fileN target_dir: +@files=@ARGV[0..$num_files-1]; +$install_dir=$ARGV[$num_files]; +$install_dir =~ s[\\][/]g; # maybe fix DOS-style path +$install_dir =~ s[/$][]; # remove trailing '/' +$install_dir =~ s[//][/]g; # replace '//' by '/' + +# Do we have to create the directory? +unless ( (-d $install_dir) || (-l $install_dir) ) +{ + # Create dir only if -d option given + Usage ("$install_dir does not exist") unless ($opt_d); + + # Create all the subdirs that lead to $install_dir + mkpath ($install_dir, 1, 0777); +} + +foreach $source ( @files ) +{ + Usage ("Can't find file '$source'") unless -f $source; + + $basename=$source; + $basename=~s'.*[/\\]''; + $target = "$install_dir/$basename"; + + # The Win32 filesystem seems to be 'slow', + # i.e. $target may look like 'up to date' + # unless you wait an hour. + # -> skip this test on WIN32 ? + #if (-f $target and $^O ne "MSWin32") + if (-f $target) + { + if (-M $target < -M $source and + -C $target < -C $source) + { + print "$target is up to date\n"; + next; + } + else + { + # remove old target, make sure it is deletable: + chmod 0777, $target; + unlink $target; + } + } + + # print "Installing $source into $install_dir\n"; + copy ($source, $target) or die "Copy failed"; + + # chmod 0555 DOES work on WIN32, but: + # Another chmod 0777 to make it write- and deletable + # will then fail. + # -> you have to use Win32::SetFileAttributes + # to get rid of those files from within Perl. + # Because the chmod is not really needed on WIN32, + # just skip it! + chmod $mode, $target unless ($^O eq "MSWin32"); +} + +sub Usage +{ + my ($txt) = @_; + + print "Usage:\n"; + print "\t$tool [ -m mode ] file ... directory\n"; + print "\n"; + print "\t-d Create non-existing directories\n"; + print "\t-m mode Set the mode for the installed file"; + print " (0755 by default)\n"; + print "\tfile Name of file\n"; + print "\tdirectory Destination directory\n"; + + print "$txt\n" if $txt; + + exit 2; +} + +# EOF installEpics.pl diff --git a/src/tools/makeDbDepends.pl b/src/tools/makeDbDepends.pl new file mode 100644 index 000000000..729814326 --- /dev/null +++ b/src/tools/makeDbDepends.pl @@ -0,0 +1,33 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # makeDbDepends.pl + +# Called from within RULES.Db in the Db directories. +# Searches .substitutions and .template files (from the command line) for +# "file xxx {" entries to create a DEPENDS file +# and +# 'include "xxx"' entries to create a DEPENDS file + + +$target = $ARGV[0]; +shift @ARGV; + + +foreach $file (@ARGV) { + open(IN, "<$file") or die "Cannot open $file: $!"; + @infile = ; + close IN or die "Cannot close $file: $!"; + + @depends = grep { s/^\s*file\s*(.*)\s*\{.*$/\1/ } @infile; + chomp @depends; + + if (@depends) { + print "$target: @depends\n"; + } + + @depends2 = grep { s/^\s*include\s*\"\s*(.*)\s*\".*$/\1/ } @infile; + chomp @depends2; + + if (@depends2) { + print "$target: @depends2\n"; + } +} diff --git a/src/tools/makeIncludeDbd.pl b/src/tools/makeIncludeDbd.pl new file mode 100644 index 000000000..6faf1ddbd --- /dev/null +++ b/src/tools/makeIncludeDbd.pl @@ -0,0 +1,43 @@ +#!/usr/bin/perl +#************************************************************************* +# 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 + +use File::Basename; + +sub Usage +{ + my ($txt) = @_; + + print "Usage:\n"; + print "\tmakeIncludeDbd.pl infile1 [ infile2 infile3 ...] outfile\n"; + print "\nError: $txt\n" if $txt; + + exit 2; +} + +# need at least two args: ARGV[0] and ARGV[1] +Usage("\"makeIncludeDbd.pl @ARGV\": No input files specified") if $#ARGV < 1; + +$target=$ARGV[$#ARGV]; +@sources=@ARGV[0..$#ARGV-1]; + +open(OUT, "> $target") or die "Cannot create $target\n";; +foreach $file ( @sources ) +{ + $base=basename($file); + print OUT "include \"$base\"\n"; +} + +close OUT; + +# EOF makeIncludeDbd.pl + diff --git a/src/tools/makeMakefile.pl b/src/tools/makeMakefile.pl new file mode 100644 index 000000000..80383ab3f --- /dev/null +++ b/src/tools/makeMakefile.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl +#************************************************************************* +# 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. +#************************************************************************* +# +# makeMakefile.pl +# +# called from RULES_ARCHS +# +# +# Usage: perl makeMakefile.pl O.*-dir top Makefile-Type + +$dir = $ARGV[0]; +$top= $ARGV[1]; +$type = $ARGV[2]; +$makefile="$dir/Makefile"; +$b_t=""; + +if ($type ne "") +{ + $b_t = "B_T=$type"; +} + +if ($dir =~ m'O.(.+)') +{ + $t_a = $1; +} +else +{ + die "Cannot extract T_A from $dir"; +} + +mkdir ($dir, 0777) unless -d $dir; + +open OUT, "> $makefile" or die "Cannot create $makefile"; + +print OUT "#This Makefile created by makeMakefile.pl\n\n\n"; +print OUT "all :\n"; +print OUT " \$(MAKE) -f ../Makefile$type TOP=$top T_A=$t_a $b_t \$@\n\n"; +print OUT ".DEFAULT: force\n"; +print OUT " \$(MAKE) -f ../Makefile$type TOP=$top T_A=$t_a $b_t \$@\n\n"; +print OUT "force: ;\n"; + +close OUT; + +# EOF makeMakefile.pl + diff --git a/src/tools/makeTestfile.pl b/src/tools/makeTestfile.pl new file mode 100644 index 000000000..7b4f010bb --- /dev/null +++ b/src/tools/makeTestfile.pl @@ -0,0 +1,31 @@ +#!/usr/bin/perl +#************************************************************************* +# Copyright (c) 2008 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# The makeTestfile.pl script generates a file $target.t which is needed +# because some versions of the Perl test harness can only run test scripts +# that are actually written in Perl. The script we generate execs the +# real test program which must be in the same directory as the .t file. + +# Usage: makeTestfile.pl target.t executable +# target.t is the name of the Perl script to generate +# executable is the name of the file the script runs + +use strict; + +my ($target, $exe) = @ARGV; + +open(my $OUT, '>', $target) or die "Can't create $target: $!\n"; + +print $OUT <$depFile" or die "\aERROR opening file $depFile for writing: $!\n"; + + my $old_handle = select(DEPENDS); + + print "# DO NOT EDIT: This file created by $version\n\n"; + + foreach $file (@includes) { + print "$objFile : $file\n"; + } + print "\n\n"; + + select($old_handle) ; # in this case, STDOUT +} + +#------------------------------------------- +# scan file for #includes +sub scanFile { + my $file = shift; + my $incfile; + my $line; + print "Scanning file $file\n" if $debug; + open FILE, $file or return; + foreach $line ( ) { + $incfile = findNextIncName($line,$file=~/\.substitutions$/); + next if !$incfile; + next if $output{$incfile}; + push @includes,$incfile; + $output{$incfile} = 1; + } + close FILE; +} + +#------------------------------------------ +# scan files in includes list +sub scanIncludesList { + my $file; + foreach $file (@includes) { + scanFile($file); + } +} + +#----------------------------------------- +# find filename on #include line +sub findNextIncName { + my $line = shift; + my $is_subst = shift; + my $incname = ""; + my $incfile = 0; + my $dir; + + local $/ = $endline; + if ($is_subst) { + return 0 if not $line =~ /^\s*file\s*([^\s{]*)/; + $incname = $1; + } else { + return 0 if not $line =~ /^#?\s*include\s*('.*?'|<.*?>|".*?")/; + $incname = substr $1, 1, length($1)-2; + } + print "DEBUG: $incname\n" if $debug; + + return $incname if -f $incname; + return 0 if ( $incname =~ /^\// || $incname =~ /^\\/ ); + + foreach $dir ( @dirs ) { + chomp($dir); + $incfile = "$dir/$incname"; + print "DEBUG: checking for $incname in $dir\n" if $debug; + return $incfile if -f $incfile; + } + return 0; +} diff --git a/src/tools/munch.pl b/src/tools/munch.pl new file mode 100644 index 000000000..0faf6cd30 --- /dev/null +++ b/src/tools/munch.pl @@ -0,0 +1,137 @@ +#!/usr/bin/perl +#************************************************************************* +# Copyright (c) 2010 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +# Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +# +# Creates a ctdt.c file of C++ static constructors and destructors, +# as required for all vxWorks binaries containing C++ code. + +use strict; +use warnings; +use Getopt::Std; + +our ($opt_o); + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; +&HELP_MESSAGE if !getopts('o:') || @ARGV != 1; + +# Is exception handler frame info required? +my $need_eh_frame = 0; + +# Constructor and destructor names: +# Array contains names from input file. +# Hash is used to skip duplicate names. +my (@ctors, %ctors); +my (@dtors, %dtors); + +while (<>) +{ + chomp; + $need_eh_frame++ if m/__? gxx_personality_v [0-9]/x; + next if m/__? GLOBAL_. (F | I._GLOBAL_.D) .+/x; + if (m/__? GLOBAL_ . D .+/x) { + my ($addr, $type, $name) = split ' ', $_, 3; + push @dtors, $name unless exists $dtors{$name}; + $dtors{$name} = 1; + } + if (m/__? GLOBAL_ . I .+/x) { + my ($addr, $type, $name) = split ' ', $_, 3; + push @ctors, $name unless exists $ctors{$name}; + $ctors{$name} = 1; + } +} + +push my @out, + '/* C++ static constructor and destructor lists */', + '/* This is generated by munch.pl, do not edit! */', + '', + '#include ', + '', + '/* Declarations */', + (map {cDecl($_)} @ctors, @dtors), + ''; + +exceptionHandlerFrame() if $need_eh_frame; + +push @out, + '/* List of Constructors */', + 'void (*_ctors[])(void) = {', + (join ",\n", (map {' ' . cName($_)} @ctors), ' NULL'), + '};', + '', + '/* List of Destructors */', + 'void (*_dtors[])(void) = {', + (join ",\n", (map {' ' . cName($_)} @dtors), ' NULL'), + '};', + ''; + +if ($opt_o) { + open(my $OUT, '>', $opt_o) + or die "Can't create $opt_o: $!\n"; + print $OUT join "\n", @out; + close $OUT + or die "Can't close $opt_o: $!\n"; +} else { + print join "\n", @out; +} + + +# Outputs the C code for registering exception handler frame info +sub exceptionHandlerFrame { + my $eh_ctor = 'eh_ctor'; + my $eh_dtor = 'eh_dtor'; + + # Add EH ctor/dtor to _start_ of arrays + unshift @ctors, $eh_ctor; + unshift @dtors, $eh_dtor; + + push @out, + '/* Exception handler frame */', + 'extern const unsigned __EH_FRAME_BEGIN__[];', + '', + "static void $eh_ctor(void) {", + ' extern void __register_frame_info (const void *, void *);', + ' static struct { unsigned pad[8]; } object;', + '', + ' __register_frame_info(__EH_FRAME_BEGIN__, &object);', + '}', + '', + "static void $eh_dtor(void) {", + ' extern void *__deregister_frame_info (const void *);', + '', + ' __deregister_frame_info(__EH_FRAME_BEGIN__);', + '}', + ''; + return; +} + +sub cName { + my ($name) = @_; + $name =~ s/^__/_/; + $name =~ s/\./\$/g; + return $name; +} + +sub cDecl { + my ($name) = @_; + my $decl = 'extern void ' . cName($name) . '(void)'; + # 68k and MIPS targets allow periods in symbol names, which + # can only be reached using an assembler string. + if (m/\./) { + $decl .= "\n __asm__ (\"" . $name . "\");"; + } else { + $decl .= ';'; + } + return $decl; +} + +sub HELP_MESSAGE { + print STDERR "Usage: munch.pl [-o file_ctdt.c] file.nm\n"; + exit 2; +} diff --git a/src/tools/replaceVAR.pl b/src/tools/replaceVAR.pl new file mode 100644 index 000000000..4c50f1a19 --- /dev/null +++ b/src/tools/replaceVAR.pl @@ -0,0 +1,21 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # replaceVAR.pl +#************************************************************************* +# 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. +#************************************************************************* + +# Called from within the object directory +# Replaces VAR(xxx) with $(xxx) +# and VAR_xxx_ with $(xxx) + +while () { + s/VAR\(/\$\(/g; + s/VAR_([^_]*)_/\$\($1\)/g; + print; +} diff --git a/src/tools/useManifestTool.pl b/src/tools/useManifestTool.pl new file mode 100644 index 000000000..45986b662 --- /dev/null +++ b/src/tools/useManifestTool.pl @@ -0,0 +1,35 @@ +eval 'exec perl -S -w $0 ${1+"$@"}' # -*- Mode: perl -*- + if 0; + +# +# Use MS Visual C++ compiler version number to determine if +# we want to use the Manifest Tool (status=1) or not (status=0) +# +# VC compiler versions >= 14.00 will have status=1 +# VC compiler versions 10.00 - 13.10 will have status=0 +# EPICS builds with older VC compilers is not supported +# + +my $versionString=`cl 2>&1`; + +if ($versionString =~ m/Version 16./) { + $status=0; +} elsif ($versionString =~ m/Version 15./){ + $status=1; +} elsif ($versionString =~ m/Version 14./){ + $status=1; +} elsif ($versionString =~ m/Version 13.10/){ + $status=0; +} elsif ($versionString =~ m/Version 13.0/){ + $status=0; +} elsif ($versionString =~ m/Version 12./){ + $status=0; +} elsif ($versionString =~ m/Version 11./){ + $status=0; +} elsif ($versionString =~ m/Version 10./){ + $status=0; +} else { + $status=0; +} +print "$status\n"; +exit; diff --git a/src/toolsComm/Makefile b/src/toolsComm/Makefile new file mode 100644 index 000000000..ef3e0c0ce --- /dev/null +++ b/src/toolsComm/Makefile @@ -0,0 +1,20 @@ +#************************************************************************* +# 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 + +DIRS = antelope flex + +flex_DEPEND_DIRS += antelope + +include $(TOP)/configure/RULES_DIRS + diff --git a/src/toolsComm/antelope/ACKNOWLEDGEMENTS b/src/toolsComm/antelope/ACKNOWLEDGEMENTS new file mode 100644 index 000000000..b66bb2506 --- /dev/null +++ b/src/toolsComm/antelope/ACKNOWLEDGEMENTS @@ -0,0 +1,25 @@ + Berkeley Yacc owes much to the unflagging efforts of Keith Bostic. +His badgering kept me working on it long after I was ready to quit. + + Berkeley Yacc is based on the excellent algorithm for computing LALR(1) +lookaheads developed by Tom Pennello and Frank DeRemer. The algorithm is +described in their almost impenetrable article in TOPLAS 4,4. + + Finally, much of the credit for the latest version must go to those +who pointed out deficiencies of my earlier releases. Among the most +prolific contributors were + + Benson I. Margulies + Dave Gentzel + Antoine Verheijen + Peter S. Housel + Dale Smith + Ozan Yigit + John Campbell + Bill Sommerfeld + Paul Hilfinger + Gary Bridgewater + Dave Bakken + Dan Lanciani + Richard Sargent + Parag Patel diff --git a/src/toolsComm/antelope/EPICS_READ_THIS b/src/toolsComm/antelope/EPICS_READ_THIS new file mode 100644 index 000000000..c6869244a --- /dev/null +++ b/src/toolsComm/antelope/EPICS_READ_THIS @@ -0,0 +1,54 @@ + +This is the source of the BSD version of yacc that is distributed in the NET2 +release of BSD Unix. We chose this over Bison due to the bison license +agreement. And if you are scratching you head over that comment, read the +addendum in the xxx-info-1 file that comes with bison. + +Anyway, this BSD-yacc has been hacked on to make it generate an all-static +parser C program so that it may be used on an IOC w/o getting things confused. +They way we intend to use it is with the epics-hacked version of flex that +also generates an all-static source program. The general idea is that +your yacc source (xxx.y) file could be formatted like this: + +========================================================= + +%% +YOUR YACC PROGRAM +%% + +int MyYaccParser(char *f1) +{ + static int FirstFlag = 1; + + printf("processing file >%s<\n", f1); + + yyin = fopen(f1, "r"); + if (!FirstFlag) yyrestart(yyin); + FirstFlag = 0; + yyparse(); + fclose(yyin); + + return(0); +} + +static int yyerror(char *str) +{ + printf("Error: %s\n", str); + return(0); +} +========================================================= + +The FirstFlag and yyrestart jazz is the flex-flavored version of how you +restart the scanner if you want to parse another file. Without it, the +scanner can only be used one time. Even if you think your code will only +be used one time, it is desireable to put in the restart stuff anyway, +because some day you (or your boss) will change your mind and without it +the IOC will crash when you make your second call to your parser. + +Note also that there is a yyerror function present. This is mandatory for the +way we are using flex. Without, results are indeterminate... because you will +end up calling some other random yyerror() in the ioc.. probably the one for +the console command interpreter. + + +--John diff --git a/src/toolsComm/antelope/Makefile b/src/toolsComm/antelope/Makefile new file mode 100644 index 000000000..c7115d56d --- /dev/null +++ b/src/toolsComm/antelope/Makefile @@ -0,0 +1,29 @@ +#************************************************************************* +# 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../.. +include $(TOP)/configure/CONFIG + +SRCS += closure.c +SRCS += error.c +SRCS += lalr.c +SRCS += lr0.c +SRCS += main.c +SRCS += mkpar.c +SRCS += output.c +SRCS += reader.c +SRCS += skeleton.c +SRCS += symtab.c +SRCS += verbose.c +SRCS += warshall.c + +PROD_HOST = antelope +PROD_LIBS = Com + +include $(TOP)/configure/RULES + diff --git a/src/toolsComm/antelope/NEW_FEATURES b/src/toolsComm/antelope/NEW_FEATURES new file mode 100644 index 000000000..b030c625b --- /dev/null +++ b/src/toolsComm/antelope/NEW_FEATURES @@ -0,0 +1,46 @@ + The -r option has been implemented. The -r option tells Yacc to +put the read-only tables in y.tab.c and the code and variables in +y.code.c. Keith Bostic asked for this option so that :yyfix could be +eliminated. + + The -l and -t options have been implemented. The -l option tells +Yacc not to include #line directives in the code it produces. The -t +option causes debugging code to be included in the compiled parser. + + The code for error recovery has been changed to implement the same +algorithm as AT&T Yacc. There will still be differences in the way +error recovery works because AT&T Yacc uses more default reductions +than Berkeley Yacc. + + The environment variable TMPDIR determines the directory where +temporary files will be created. If TMPDIR is defined, temporary files +will be created in the directory whose pathname is the value of TMPDIR. +By default, temporary files are created in /tmp. + + The keywords are now case-insensitive. For example, %nonassoc, +%NONASSOC, %NonAssoc, and %nOnAsSoC are all equivalent. + + Commas and semicolons that are not part of C code are treated as +commentary. + + Line-end comments, as in BCPL, are permitted. Line-end comments +begin with // and end at the next end-of-line. Line-end comments are +permitted in C code; they are converted to C comments on output. + + The form of y.output files has been changed to look more like +those produced by AT&T Yacc. + + A new kind of declaration has been added. The form of the declaration +is + + %ident string + +where string is a sequence of characters begining with a double quote +and ending with either a double quote or the next end-of-line, whichever +comes first. The declaration will cause a #ident directive to be written +near the start of the output file. + + If a parser has been compiled with debugging code, that code can be +enabled by setting an environment variable. If the environment variable +YYDEBUG is set to 0, debugging output is suppressed. If it is set to 1, +debugging output is written to standard output. diff --git a/src/toolsComm/antelope/NOTES b/src/toolsComm/antelope/NOTES new file mode 100644 index 000000000..9db3c96ce --- /dev/null +++ b/src/toolsComm/antelope/NOTES @@ -0,0 +1,9 @@ +Berkeley Yacc reflects its origins. The reason so many routines +use exactly six register variables is that Berkeley Yacc was +developed on a VAX using PCC. PCC placed at most six variables +in registers. I went to considerable effort to find which six +variables most belonged in registers. Changes in machines and +compilers make that effort worthless, perhaps even harmful. + +The code contains many instances where address calculations are +performed in particular ways to optimize the code for the VAX. diff --git a/src/toolsComm/antelope/NO_WARRANTY b/src/toolsComm/antelope/NO_WARRANTY new file mode 100644 index 000000000..06e8d93a2 --- /dev/null +++ b/src/toolsComm/antelope/NO_WARRANTY @@ -0,0 +1,3 @@ + Berkeley Yacc is distributed with no warranty whatever. The author +and any other contributors take no responsibility for the consequences of +its use. diff --git a/src/toolsComm/antelope/README b/src/toolsComm/antelope/README new file mode 100644 index 000000000..091f23343 --- /dev/null +++ b/src/toolsComm/antelope/README @@ -0,0 +1,23 @@ + Berkeley Yacc is an LALR(1) parser generator. Berkeley Yacc has been made +as compatible as possible with AT&T Yacc. Berkeley Yacc can accept any input +specification that conforms to the AT&T Yacc documentation. Specifications +that take advantage of undocumented features of AT&T Yacc will probably be +rejected. + + Berkeley Yacc is distributed with no warranty whatever. The code is certain +to contain errors. Neither the author nor any contributor takes responsibility +for any consequences of its use. + + Berkeley Yacc is in the public domain. The data structures and algorithms +used in Berkeley Yacc are all either taken from documents available to the +general public or are inventions of the author. Anyone may freely distribute +source or binary forms of Berkeley Yacc whether unchanged or modified. +Distributers may charge whatever fees they can obtain for Berkeley Yacc. +Programs generated by Berkeley Yacc may be distributed freely. + + Please report bugs to + + robert.corbett@eng.Sun.COM + +Include a small example if possible. Please include the banner string from +skeleton.c with the bug report. Do not expect rapid responses. diff --git a/src/toolsComm/antelope/closure.c b/src/toolsComm/antelope/closure.c new file mode 100644 index 000000000..f08c8cd92 --- /dev/null +++ b/src/toolsComm/antelope/closure.c @@ -0,0 +1,274 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +short *itemset; +short *itemsetend; +unsigned *ruleset; + +static unsigned *first_derives; +static unsigned *EFF; + +#ifdef DEBUG +static void print_closure(int n); +static void print_EFF(void); +static void print_first_derives(void); +#endif + + +static void +set_EFF(void) +{ + unsigned *row; + int symbol; + short *sp; + int rowsize; + int i; + int rule; + + rowsize = WORDSIZE(nvars); + EFF = NEW2(nvars * rowsize, unsigned); + + row = EFF; + for (i = start_symbol; i < nsyms; i++) + { + sp = derives[i]; + for (rule = *sp; rule > 0; rule = *++sp) + { + symbol = ritem[rrhs[rule]]; + if (ISVAR(symbol)) + { + symbol -= start_symbol; + SETBIT(row, symbol); + } + } + row += rowsize; + } + + reflexive_transitive_closure(EFF, nvars); + +#ifdef DEBUG + print_EFF(); +#endif +} + + +void +set_first_derives(void) +{ + unsigned *rrow; + unsigned *vrow; + int j; + unsigned k; + unsigned cword = 0; + short *rp; + + int rule; + int i; + int rulesetsize; + int varsetsize; + + rulesetsize = WORDSIZE(nrules); + varsetsize = WORDSIZE(nvars); + first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens * rulesetsize; + + set_EFF(); + + rrow = first_derives + ntokens * rulesetsize; + for (i = start_symbol; i < nsyms; i++) + { + vrow = EFF + ((i - ntokens) * varsetsize); + k = BITS_PER_WORD; + for (j = start_symbol; j < nsyms; k++, j++) + { + if (k >= BITS_PER_WORD) + { + cword = *vrow++; + k = 0; + } + + if (cword & (1 << k)) + { + rp = derives[j]; + while ((rule = *rp++) >= 0) + { + SETBIT(rrow, rule); + } + } + } + + vrow += varsetsize; + rrow += rulesetsize; + } + +#ifdef DEBUG + print_first_derives(); +#endif + + FREE(EFF); +} + + +void +closure(short int *nucleus, int n) +{ + int ruleno; + unsigned word; + unsigned i; + short *csp; + unsigned *dsp; + unsigned *rsp; + int rulesetsize; + + short *csend; + unsigned *rsend; + int symbol; + int itemno; + + rulesetsize = WORDSIZE(nrules); + rsp = ruleset; + rsend = ruleset + rulesetsize; + for (rsp = ruleset; rsp < rsend; rsp++) + *rsp = 0; + + csend = nucleus + n; + for (csp = nucleus; csp < csend; ++csp) + { + symbol = ritem[*csp]; + if (ISVAR(symbol)) + { + dsp = first_derives + symbol * rulesetsize; + rsp = ruleset; + while (rsp < rsend) + *rsp++ |= *dsp++; + } + } + + ruleno = 0; + itemsetend = itemset; + csp = nucleus; + for (rsp = ruleset; rsp < rsend; ++rsp) + { + word = *rsp; + if (word) + { + for (i = 0; i < BITS_PER_WORD; ++i) + { + if (word & (1 << i)) + { + itemno = rrhs[ruleno+i]; + while (csp < csend && *csp < itemno) + *itemsetend++ = *csp++; + *itemsetend++ = itemno; + while (csp < csend && *csp == itemno) + ++csp; + } + } + } + ruleno += BITS_PER_WORD; + } + + while (csp < csend) + *itemsetend++ = *csp++; + +#ifdef DEBUG + print_closure(n); +#endif +} + + + +void +finalize_closure(void) +{ + FREE(itemset); + FREE(ruleset); + FREE(first_derives + ntokens * WORDSIZE(nrules)); +} + + +#ifdef DEBUG + +static void +print_closure(int n) +{ + short *isp; + + printf("\n\nn = %d\n\n", n); + for (isp = itemset; isp < itemsetend; isp++) + printf(" %d\n", *isp); +} + + +static void +print_EFF(void) +{ + int i, j; + unsigned *rowp; + unsigned word; + unsigned k; + + printf("\n\nEpsilon Free Firsts\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("\n%s", symbol_name[i]); + rowp = EFF + ((i - start_symbol) * WORDSIZE(nvars)); + word = *rowp++; + + k = BITS_PER_WORD; + for (j = 0; j < nvars; k++, j++) + { + if (k >= BITS_PER_WORD) + { + word = *rowp++; + k = 0; + } + + if (word & (1 << k)) + printf(" %s", symbol_name[start_symbol + j]); + } + } +} + + +static void +print_first_derives(void) +{ + int i; + int j; + unsigned *rp; + unsigned cword; + unsigned k; + + printf("\n\n\nFirst Derives\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("\n%s derives\n", symbol_name[i]); + rp = first_derives + i * WORDSIZE(nrules); + k = BITS_PER_WORD; + for (j = 0; j <= nrules; k++, j++) + { + if (k >= BITS_PER_WORD) + { + cword = *rp++; + k = 0; + } + + if (cword & (1 << k)) + printf(" %d\n", j); + } + } + + fflush(stdout); +} + +#endif diff --git a/src/toolsComm/antelope/defs.h b/src/toolsComm/antelope/defs.h new file mode 100644 index 000000000..36b295fa8 --- /dev/null +++ b/src/toolsComm/antelope/defs.h @@ -0,0 +1,367 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include +#include +#include + + +/* machine-dependent definitions */ +/* the following definitions are for the Tahoe */ +/* they might have to be changed for other machines */ + +/* MAXCHAR is the largest unsigned character value */ +/* MAXSHORT is the largest value of a C short */ +/* MINSHORT is the most negative value of a C short */ +/* MAXTABLE is the maximum table size */ +/* BITS_PER_WORD is the number of bits in a C unsigned */ +/* WORDSIZE computes the number of words needed to */ +/* store n bits */ +/* BIT returns the value of the n-th bit starting */ +/* from r (0-indexed) */ +/* SETBIT sets the n-th bit starting from r */ + +#define MAXCHAR 255 +#define MAXSHORT 32767 +#define MINSHORT -32768 +#define MAXTABLE 32500 +#define BITS_PER_WORD 32 +#define WORDSIZE(n) (((n)+(BITS_PER_WORD-1))/BITS_PER_WORD) +#define BIT(r, n) ((((r)[(n)>>5])>>((n)&31))&1) +#define SETBIT(r, n) ((r)[(n)>>5]|=((unsigned)1<<((n)&31))) + + +/* character names */ + +#define NUL '\0' /* the null character */ +#define NEWLINE '\n' /* line feed */ +#define SP ' ' /* space */ +#define BS '\b' /* backspace */ +#define HT '\t' /* horizontal tab */ +#define VT '\013' /* vertical tab */ +#define CR '\r' /* carriage return */ +#define FF '\f' /* form feed */ +#define QUOTE '\'' /* single quote */ +#define DOUBLE_QUOTE '\"' /* double quote */ +#define BACKSLASH '\\' /* backslash */ + + +/* defines for constructing filenames */ + +#define CODE_SUFFIX ".code.c" +#define DEFINES_SUFFIX ".tab.h" +#define OUTPUT_SUFFIX ".tab.c" +#define VERBOSE_SUFFIX ".output" + + +/* keyword codes */ + +#define TOKEN 0 +#define LEFT 1 +#define RIGHT 2 +#define NONASSOC 3 +#define MARK 4 +#define TEXT 5 +#define TYPE 6 +#define START 7 +#define UNION 8 +#define IDENT 9 + + +/* symbol classes */ + +#define UNKNOWN 0 +#define TERM 1 +#define NONTERM 2 + + +/* the undefined value */ + +#define UNDEFINED (-1) + + +/* action codes */ + +#define SHIFT 1 +#define REDUCE 2 + + +/* character macros */ + +#define IS_IDENT(c) (isalnum(c) || (c) == '_' || (c) == '.' || (c) == '$') +#define IS_OCTAL(c) ((c) >= '0' && (c) <= '7') +#define NUMERIC_VALUE(c) ((c) - '0') + + +/* symbol macros */ + +#define ISTOKEN(s) ((s) < start_symbol) +#define ISVAR(s) ((s) >= start_symbol) + + +/* storage allocation macros */ + +#define CALLOC(k,n) (calloc((unsigned)(k),(unsigned)(n))) +#define FREE(x) (free((char*)(x))) +#define MALLOC(n) (malloc((unsigned)(n))) +#define NEW(t) ((t*)allocate(sizeof(t))) +#define NEW2(n,t) ((t*)allocate((unsigned)((n)*sizeof(t)))) +#define REALLOC(p,n) (realloc((char*)(p),(unsigned)(n))) + + +/* the structure of a symbol table entry */ + +typedef struct bucket bucket; +struct bucket +{ + struct bucket *link; + struct bucket *next; + char *name; + char *tag; + short value; + short index; + short prec; + char class; + char assoc; +}; + + +/* the structure of the LR(0) state machine */ + +typedef struct core core; +struct core +{ + struct core *next; + struct core *link; + short number; + short accessing_symbol; + short nitems; + short items[1]; +}; + + +/* the structure used to record shifts */ + +typedef struct shifts shifts; +struct shifts +{ + struct shifts *next; + short number; + short nshifts; + short shift[1]; +}; + + +/* the structure used to store reductions */ + +typedef struct reductions reductions; +struct reductions +{ + struct reductions *next; + short number; + short nreds; + short rules[1]; +}; + + +/* the structure used to represent parser actions */ + +typedef struct action action; +struct action +{ + struct action *next; + short symbol; + short number; + short prec; + char action_code; + char assoc; + char suppressed; +}; + + +/* global variables */ + +extern char dflag; +extern char lflag; +extern char rflag; +extern char tflag; +extern char vflag; +extern char *symbol_prefix; + +extern char *myname; +extern char *cptr; +extern char *line; +extern int lineno; +extern int outline; + +extern char *banner[]; +extern char *tables[]; +extern char *header[]; +extern char *body[]; +extern char *trailer[]; + +extern char *code_file_name; +extern char *defines_file_name; +extern char *input_file_name; +extern char *output_file_name; +extern char *verbose_file_name; + +extern FILE *action_file; +extern FILE *code_file; +extern FILE *defines_file; +extern FILE *input_file; +extern FILE *output_file; +extern FILE *text_file; +extern FILE *union_file; +extern FILE *verbose_file; + +extern int nitems; +extern int nrules; +extern int nsyms; +extern int ntokens; +extern int nvars; +extern int ntags; + +extern char unionized; +extern char line_format[]; + +extern int start_symbol; +extern char **symbol_name; +extern short *symbol_value; +extern short *symbol_prec; +extern char *symbol_assoc; + +extern short *ritem; +extern short *rlhs; +extern short *rrhs; +extern short *rprec; +extern char *rassoc; + +extern short **derives; +extern char *nullable; + +extern bucket *first_symbol; +extern bucket *last_symbol; + +extern int nstates; +extern core *first_state; +extern shifts *first_shift; +extern reductions *first_reduction; +extern short *accessing_symbol; +extern core **state_table; +extern shifts **shift_table; +extern reductions **reduction_table; +extern unsigned *LA; +extern short *LAruleno; +extern short *lookaheads; +extern short *goto_map; +extern short *from_state; +extern short *to_state; + +extern action **parser; +extern int SRtotal; +extern int RRtotal; +extern short *SRconflicts; +extern short *RRconflicts; +extern short *defred; +extern short *rules_used; +extern short nunused; +extern short final_state; + +/* + * global functions + */ + +#ifdef __GNUC__ +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +/* main.c */ +extern void done(int k) NORETURN; +extern char *allocate(unsigned int n); + +/* error.c */ +extern void no_space(void) NORETURN; +extern void fatal(char *msg) NORETURN; +extern void open_error(char *filename) NORETURN; +extern void unexpected_EOF(void) NORETURN; +extern void syntax_error(int st_lineno, char *st_line, char *st_cptr) NORETURN; +extern void unterminated_comment(int c_lineno, char *c_line, char *c_cptr) NORETURN; +extern void unterminated_string(int s_lineno, char *s_line, char *s_cptr) NORETURN; +extern void unterminated_text(int t_lineno, char *t_line, char *t_cptr) NORETURN; +extern void unterminated_union(int u_lineno, char *u_line, char *u_cptr) NORETURN; +extern void over_unionized(char *u_cptr) NORETURN; +extern void illegal_tag(int t_lineno, char *t_line, char *t_cptr) NORETURN; +extern void illegal_character(char *c_cptr) NORETURN; +extern void used_reserved(char *s) NORETURN; +extern void tokenized_start(char *s) NORETURN; +extern void retyped_warning(char *s); +extern void reprec_warning(char *s); +extern void revalued_warning(char *s); +extern void terminal_start(char *s); +extern void restarted_warning(void); +extern void no_grammar(void) NORETURN; +extern void terminal_lhs(int s_lineno) NORETURN; +extern void prec_redeclared(void); +extern void unterminated_action(int a_lineno, char *a_line, char *a_cptr) NORETURN; +extern void dollar_warning(int a_lineno, int i); +extern void dollar_error(int a_lineno, char *a_line, char *a_cptr) NORETURN; +extern void untyped_lhs(void) NORETURN; +extern void untyped_rhs(int i, char *s) NORETURN; +extern void unknown_rhs(int i) NORETURN; +extern void default_action_warning(void); +extern void undefined_goal(char *s) NORETURN; +extern void undefined_symbol_warning(char *s); + +/* symtab.c */ +extern bucket *make_bucket(char *name); +extern bucket *lookup(char *name); +extern void create_symbol_table(void); +extern void free_symbol_table(void); +extern void free_symbols(void); + +/* closure.c */ +extern void set_first_derives(void); +extern void closure(short int *nucleus, int n); +extern void finalize_closure(void); + +/* reader.c */ +extern void reader(void); + +/* lr0.c */ +extern void lr0(void); + +/* lalr.c */ +extern void lalr(void); + +/* mkpar.c */ +extern void make_parser(void); +extern void free_parser(void); + +/* warshall.c */ +extern void reflexive_transitive_closure(unsigned int *R, int n); + +/* output.c */ +extern void output(void); +extern int default_goto(int symbol); +extern int matching_vector(int vector); +extern int pack_vector(int vector); + +/* skeleton.c */ +extern void write_section(char *section[]); + +/* verbose.c */ +extern void verbose(void); + +/* system includes */ + +#include +#include +#include diff --git a/src/toolsComm/antelope/error.c b/src/toolsComm/antelope/error.c new file mode 100644 index 000000000..0c1904561 --- /dev/null +++ b/src/toolsComm/antelope/error.c @@ -0,0 +1,314 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* routines for printing error messages */ + +#include "defs.h" + + +void +fatal(char *msg) +{ + fprintf(stderr, "%s: f - %s\n", myname, msg); + done(2); +} + + +void +no_space(void) +{ + fprintf(stderr, "%s: f - out of space\n", myname); + done(2); +} + + +void +open_error(char *filename) +{ + fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename); + done(2); +} + + +void +unexpected_EOF(void) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-file\n", + myname, lineno, input_file_name); + done(1); +} + + +static int +print_pos(char *st_line, char *st_cptr) +{ + char *s; + + if (st_line == 0) return(0); + for (s = st_line; *s != '\n'; ++s) + { + if (isprint(*s) || *s == '\t') + putc(*s, stderr); + else + putc('?', stderr); + } + putc('\n', stderr); + for (s = st_line; s < st_cptr; ++s) + { + if (*s == '\t') + putc('\t', stderr); + else + putc(' ', stderr); + } + putc('^', stderr); + putc('\n', stderr); + return(0); +} + + +void +syntax_error(int st_lineno, char *st_line, char *st_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", syntax error\n", + myname, st_lineno, input_file_name); + print_pos(st_line, st_cptr); + done(1); +} + + +void +unterminated_comment(int c_lineno, char *c_line, char *c_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unmatched /*\n", + myname, c_lineno, input_file_name); + print_pos(c_line, c_cptr); + done(1); +} + + +void +unterminated_string(int s_lineno, char *s_line, char *s_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unterminated string\n", + myname, s_lineno, input_file_name); + print_pos(s_line, s_cptr); + done(1); +} + + +void +unterminated_text(int t_lineno, char *t_line, char *t_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unmatched %%{\n", + myname, t_lineno, input_file_name); + print_pos(t_line, t_cptr); + done(1); +} + + +void +unterminated_union(int u_lineno, char *u_line, char *u_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unterminated %%union \ +declaration\n", myname, u_lineno, input_file_name); + print_pos(u_line, u_cptr); + done(1); +} + + +void +over_unionized(char *u_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", too many %%union \ +declarations\n", myname, lineno, input_file_name); + print_pos(line, u_cptr); + done(1); +} + + +void +illegal_tag(int t_lineno, char *t_line, char *t_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal tag\n", + myname, t_lineno, input_file_name); + print_pos(t_line, t_cptr); + done(1); +} + + +void +illegal_character(char *c_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal character\n", + myname, lineno, input_file_name); + print_pos(line, c_cptr); + done(1); +} + + +void +used_reserved(char *s) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal use of reserved symbol \ +%s\n", myname, lineno, input_file_name, s); + done(1); +} + + +void +tokenized_start(char *s) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s cannot be \ +declared to be a token\n", myname, lineno, input_file_name, s); + done(1); +} + + +void +retyped_warning(char *s) +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the type of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +void +reprec_warning(char *s) +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the precedence of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +void +revalued_warning(char *s) +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the value of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +void +terminal_start(char *s) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s is a \ +token\n", myname, lineno, input_file_name, s); + done(1); +} + + +void +restarted_warning(void) +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the start symbol has been \ +redeclared\n", myname, lineno, input_file_name); +} + + +void +no_grammar(void) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", no grammar has been \ +specified\n", myname, lineno, input_file_name); + done(1); +} + + +void +terminal_lhs(int s_lineno) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", a token appears on the lhs \ +of a production\n", myname, s_lineno, input_file_name); + done(1); +} + + +void +prec_redeclared(void) +{ + fprintf(stderr, "%s: w - line %d of \"%s\", conflicting %%prec \ +specifiers\n", myname, lineno, input_file_name); +} + + +void +unterminated_action(int a_lineno, char *a_line, char *a_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unterminated action\n", + myname, a_lineno, input_file_name); + print_pos(a_line, a_cptr); + done(1); +} + + +void +dollar_warning(int a_lineno, int i) +{ + fprintf(stderr, "%s: w - line %d of \"%s\", $%d references beyond the \ +end of the current rule\n", myname, a_lineno, input_file_name, i); +} + + +void +dollar_error(int a_lineno, char *a_line, char *a_cptr) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal $-name\n", + myname, a_lineno, input_file_name); + print_pos(a_line, a_cptr); + done(1); +} + + +void +untyped_lhs(void) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", $$ is untyped\n", + myname, lineno, input_file_name); + done(1); +} + + +void +untyped_rhs(int i, char *s) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", $%d (%s) is untyped\n", + myname, lineno, input_file_name, i, s); + done(1); +} + + +void +unknown_rhs(int i) +{ + fprintf(stderr, "%s: e - line %d of \"%s\", $%d is untyped\n", + myname, lineno, input_file_name, i); + done(1); +} + + +void +default_action_warning(void) +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the default action assigns an \ +undefined value to $$\n", myname, lineno, input_file_name); +} + + +void +undefined_goal(char *s) +{ + fprintf(stderr, "%s: e - the start symbol %s is undefined\n", myname, s); + done(1); +} + + +void +undefined_symbol_warning(char *s) +{ + fprintf(stderr, "%s: w - the symbol %s is undefined\n", myname, s); +} diff --git a/src/toolsComm/antelope/lalr.c b/src/toolsComm/antelope/lalr.c new file mode 100644 index 000000000..1c8850f60 --- /dev/null +++ b/src/toolsComm/antelope/lalr.c @@ -0,0 +1,668 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +typedef + struct shorts + { + struct shorts *next; + short value; + } + shorts; + +int tokensetsize; +short *lookaheads; +short *LAruleno; +unsigned *LA; +short *accessing_symbol; +core **state_table; +shifts **shift_table; +reductions **reduction_table; +short *goto_map; +short *from_state; +short *to_state; + +static void set_state_table(void); +static void set_accessing_symbol(void); +static void set_shift_table(void); +static void set_reduction_table(void); +static void set_maxrhs(void); +static void initialize_LA(void); +static void set_goto_map(void); +static void initialize_F(void); +static void build_relations(void); +static void add_lookback_edge(int stateno, int ruleno, int gotono); +static short **transpose(short int **R, int n); +static void compute_FOLLOWS(void); +static void compute_lookaheads(void); +static void digraph(short **relation); +static void traverse(int i); + +static int infinity; +static int maxrhs; +static int ngotos; +static unsigned *F; +static short **includes; +static shorts **lookback; +static short **R; +static short *INDEX; +static short *VERTICES; +static int top; + + +void +lalr(void) +{ + tokensetsize = WORDSIZE(ntokens); + + set_state_table(); + set_accessing_symbol(); + set_shift_table(); + set_reduction_table(); + set_maxrhs(); + initialize_LA(); + set_goto_map(); + initialize_F(); + build_relations(); + compute_FOLLOWS(); + compute_lookaheads(); +} + + + +static void +set_state_table(void) +{ + core *sp; + + state_table = NEW2(nstates, core *); + for (sp = first_state; sp; sp = sp->next) + state_table[sp->number] = sp; +} + + + +static void +set_accessing_symbol(void) +{ + core *sp; + + accessing_symbol = NEW2(nstates, short); + for (sp = first_state; sp; sp = sp->next) + accessing_symbol[sp->number] = sp->accessing_symbol; +} + + + +static void +set_shift_table(void) +{ + shifts *sp; + + shift_table = NEW2(nstates, shifts *); + for (sp = first_shift; sp; sp = sp->next) + shift_table[sp->number] = sp; +} + + + +static void +set_reduction_table(void) +{ + reductions *rp; + + reduction_table = NEW2(nstates, reductions *); + for (rp = first_reduction; rp; rp = rp->next) + reduction_table[rp->number] = rp; +} + + + +static void +set_maxrhs(void) +{ + short *itemp; + short *item_end; + int length; + int max; + + length = 0; + max = 0; + item_end = ritem + nitems; + for (itemp = ritem; itemp < item_end; itemp++) + { + if (*itemp >= 0) + { + length++; + } + else + { + if (length > max) max = length; + length = 0; + } + } + + maxrhs = max; +} + + + +static void +initialize_LA(void) +{ + int i, j, k; + reductions *rp; + + lookaheads = NEW2(nstates + 1, short); + + k = 0; + for (i = 0; i < nstates; i++) + { + lookaheads[i] = k; + rp = reduction_table[i]; + if (rp) + k += rp->nreds; + } + lookaheads[nstates] = k; + + LA = NEW2(k * tokensetsize, unsigned); + LAruleno = NEW2(k, short); + lookback = NEW2(k, shorts *); + + k = 0; + for (i = 0; i < nstates; i++) + { + rp = reduction_table[i]; + if (rp) + { + for (j = 0; j < rp->nreds; j++) + { + LAruleno[k] = rp->rules[j]; + k++; + } + } + } +} + + +static void +set_goto_map(void) +{ + shifts *sp; + int i; + int symbol; + int k; + short *temp_map; + int state2; + int state1; + + goto_map = NEW2(nvars + 1, short) - ntokens; + temp_map = NEW2(nvars + 1, short) - ntokens; + + ngotos = 0; + for (sp = first_shift; sp; sp = sp->next) + { + for (i = sp->nshifts - 1; i >= 0; i--) + { + symbol = accessing_symbol[sp->shift[i]]; + + if (ISTOKEN(symbol)) break; + + if (ngotos == MAXSHORT) + fatal("too many gotos"); + + ngotos++; + goto_map[symbol]++; + } + } + + k = 0; + for (i = ntokens; i < nsyms; i++) + { + temp_map[i] = k; + k += goto_map[i]; + } + + for (i = ntokens; i < nsyms; i++) + goto_map[i] = temp_map[i]; + + goto_map[nsyms] = ngotos; + temp_map[nsyms] = ngotos; + + from_state = NEW2(ngotos, short); + to_state = NEW2(ngotos, short); + + for (sp = first_shift; sp; sp = sp->next) + { + state1 = sp->number; + for (i = sp->nshifts - 1; i >= 0; i--) + { + state2 = sp->shift[i]; + symbol = accessing_symbol[state2]; + + if (ISTOKEN(symbol)) break; + + k = temp_map[symbol]++; + from_state[k] = state1; + to_state[k] = state2; + } + } + + FREE(temp_map + ntokens); +} + + + +/* Map_goto maps a state/symbol pair into its numeric representation. */ + +int +map_goto(int state, int symbol) +{ + int high; + int low; + int middle; + int s; + + low = goto_map[symbol]; + high = goto_map[symbol + 1]; + + for (;;) + { + assert(low <= high); + middle = (low + high) >> 1; + s = from_state[middle]; + if (s == state) + return (middle); + else if (s < state) + low = middle + 1; + else + high = middle - 1; + } +} + + + +static void +initialize_F(void) +{ + int i; + int j; + int k; + shifts *sp; + short *edge; + unsigned *rowp; + short *rp; + short **reads; + int nedges; + int stateno; + int symbol; + int nwords; + + nwords = ngotos * tokensetsize; + F = NEW2(nwords, unsigned); + + reads = NEW2(ngotos, short *); + edge = NEW2(ngotos + 1, short); + nedges = 0; + + rowp = F; + for (i = 0; i < ngotos; i++) + { + stateno = to_state[i]; + sp = shift_table[stateno]; + + if (sp) + { + k = sp->nshifts; + + for (j = 0; j < k; j++) + { + symbol = accessing_symbol[sp->shift[j]]; + if (ISVAR(symbol)) + break; + SETBIT(rowp, symbol); + } + + for (; j < k; j++) + { + symbol = accessing_symbol[sp->shift[j]]; + if (nullable[symbol]) + edge[nedges++] = map_goto(stateno, symbol); + } + + if (nedges) + { + reads[i] = rp = NEW2(nedges + 1, short); + + for (j = 0; j < nedges; j++) + rp[j] = edge[j]; + + rp[nedges] = -1; + nedges = 0; + } + } + + rowp += tokensetsize; + } + + SETBIT(F, 0); + digraph(reads); + + for (i = 0; i < ngotos; i++) + { + if (reads[i]) + FREE(reads[i]); + } + + FREE(reads); + FREE(edge); +} + + + +static void +build_relations(void) +{ + int i; + int j; + int k; + short *rulep; + short *rp; + shifts *sp; + int length; + int nedges; + int done; + int state1; + int stateno; + int symbol1; + int symbol2; + short *shortp; + short *edge; + short *states; + short **new_includes; + + includes = NEW2(ngotos, short *); + edge = NEW2(ngotos + 1, short); + states = NEW2(maxrhs + 1, short); + + for (i = 0; i < ngotos; i++) + { + nedges = 0; + state1 = from_state[i]; + symbol1 = accessing_symbol[to_state[i]]; + + for (rulep = derives[symbol1]; *rulep >= 0; rulep++) + { + length = 1; + states[0] = state1; + stateno = state1; + + for (rp = ritem + rrhs[*rulep]; *rp >= 0; rp++) + { + symbol2 = *rp; + sp = shift_table[stateno]; + k = sp->nshifts; + + for (j = 0; j < k; j++) + { + stateno = sp->shift[j]; + if (accessing_symbol[stateno] == symbol2) break; + } + + states[length++] = stateno; + } + + add_lookback_edge(stateno, *rulep, i); + + length--; + done = 0; + while (!done) + { + done = 1; + rp--; + if (ISVAR(*rp)) + { + stateno = states[--length]; + edge[nedges++] = map_goto(stateno, *rp); + if (nullable[*rp] && length > 0) done = 0; + } + } + } + + if (nedges) + { + includes[i] = shortp = NEW2(nedges + 1, short); + for (j = 0; j < nedges; j++) + shortp[j] = edge[j]; + shortp[nedges] = -1; + } + } + + new_includes = transpose(includes, ngotos); + + for (i = 0; i < ngotos; i++) + if (includes[i]) + FREE(includes[i]); + + FREE(includes); + + includes = new_includes; + + FREE(edge); + FREE(states); +} + + +static void +add_lookback_edge(int stateno, int ruleno, int gotono) +{ + int i, k; + int found; + shorts *sp; + + i = lookaheads[stateno]; + k = lookaheads[stateno + 1]; + found = 0; + while (!found && i < k) + { + if (LAruleno[i] == ruleno) + found = 1; + else + ++i; + } + assert(found); + + sp = NEW(shorts); + sp->next = lookback[i]; + sp->value = gotono; + lookback[i] = sp; +} + + + +short ** +transpose(short int **R, int n) +{ + short **new_R; + short **temp_R; + short *nedges; + short *sp; + int i; + int k; + + nedges = NEW2(n, short); + + for (i = 0; i < n; i++) + { + sp = R[i]; + if (sp) + { + while (*sp >= 0) + nedges[*sp++]++; + } + } + + new_R = NEW2(n, short *); + temp_R = NEW2(n, short *); + + for (i = 0; i < n; i++) + { + k = nedges[i]; + if (k > 0) + { + sp = NEW2(k + 1, short); + new_R[i] = sp; + temp_R[i] = sp; + sp[k] = -1; + } + } + + FREE(nedges); + + for (i = 0; i < n; i++) + { + sp = R[i]; + if (sp) + { + while (*sp >= 0) + *temp_R[*sp++]++ = i; + } + } + + FREE(temp_R); + + return (new_R); +} + + + +static void +compute_FOLLOWS(void) +{ + digraph(includes); +} + + +static void +compute_lookaheads(void) +{ + int i, n; + unsigned *fp1, *fp2, *fp3; + shorts *sp, *next; + unsigned *rowp; + + rowp = LA; + n = lookaheads[nstates]; + for (i = 0; i < n; i++) + { + fp3 = rowp + tokensetsize; + for (sp = lookback[i]; sp; sp = sp->next) + { + fp1 = rowp; + fp2 = F + tokensetsize * sp->value; + while (fp1 < fp3) + *fp1++ |= *fp2++; + } + rowp = fp3; + } + + for (i = 0; i < n; i++) + for (sp = lookback[i]; sp; sp = next) + { + next = sp->next; + FREE(sp); + } + + FREE(lookback); + FREE(F); +} + + +static void +digraph(short **relation) +{ + int i; + + infinity = ngotos + 2; + INDEX = NEW2(ngotos + 1, short); + VERTICES = NEW2(ngotos + 1, short); + top = 0; + + R = relation; + + for (i = 0; i < ngotos; i++) + INDEX[i] = 0; + + for (i = 0; i < ngotos; i++) + { + if (INDEX[i] == 0 && R[i]) + traverse(i); + } + + FREE(INDEX); + FREE(VERTICES); +} + + + +static void +traverse(int i) +{ + unsigned *fp1; + unsigned *fp2; + unsigned *fp3; + int j; + short *rp; + + int height; + unsigned *base; + + VERTICES[++top] = i; + INDEX[i] = height = top; + + base = F + i * tokensetsize; + fp3 = base + tokensetsize; + + rp = R[i]; + if (rp) + { + while ((j = *rp++) >= 0) + { + if (INDEX[j] == 0) + traverse(j); + + if (INDEX[i] > INDEX[j]) + INDEX[i] = INDEX[j]; + + fp1 = base; + fp2 = F + j * tokensetsize; + + while (fp1 < fp3) + *fp1++ |= *fp2++; + } + } + + if (INDEX[i] == height) + { + for (;;) + { + j = VERTICES[top--]; + INDEX[j] = infinity; + + if (i == j) + break; + + fp1 = base; + fp2 = F + j * tokensetsize; + + while (fp1 < fp3) + *fp2++ = *fp1++; + } + } +} diff --git a/src/toolsComm/antelope/lr0.c b/src/toolsComm/antelope/lr0.c new file mode 100644 index 000000000..23b950930 --- /dev/null +++ b/src/toolsComm/antelope/lr0.c @@ -0,0 +1,613 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +extern short *itemset; +extern short *itemsetend; +extern unsigned *ruleset; + +int nstates; +core *first_state; +shifts *first_shift; +reductions *first_reduction; + +static core **state_set; +static core *this_state; +static core *last_state; +static shifts *last_shift; +static reductions *last_reduction; + +static int nshifts; +static short *shift_symbol; + +static short *redset; +static short *shiftset; + +static short **kernel_base; +static short **kernel_end; +static short *kernel_items; + +static int get_state(int symbol); +static void initialize_states(void); +static void new_itemsets(void); +static core *new_state(int symbol); +static void save_shifts(void); +static void save_reductions(void); +#ifdef DEBUG +static void print_derives(void); +#endif + + +static void +allocate_itemsets(void) +{ + short *itemp; + short *item_end; + int symbol; + int i; + int count; + int max; + short *symbol_count; + + count = 0; + symbol_count = NEW2(nsyms, short); + + item_end = ritem + nitems; + for (itemp = ritem; itemp < item_end; itemp++) + { + symbol = *itemp; + if (symbol >= 0) + { + count++; + symbol_count[symbol]++; + } + } + + kernel_base = NEW2(nsyms, short *); + kernel_items = NEW2(count, short); + + count = 0; + max = 0; + for (i = 0; i < nsyms; i++) + { + kernel_base[i] = kernel_items + count; + count += symbol_count[i]; + if (max < symbol_count[i]) + max = symbol_count[i]; + } + + shift_symbol = symbol_count; + kernel_end = NEW2(nsyms, short *); +} + +static void +allocate_storage(void) +{ + allocate_itemsets(); + shiftset = NEW2(nsyms, short); + redset = NEW2(nrules + 1, short); + state_set = NEW2(nitems, core *); +} + +static void +append_states(void) +{ + int i; + int j; + int symbol; + +#ifdef TRACE + fprintf(stderr, "Entering append_states()\n"); +#endif + for (i = 1; i < nshifts; i++) + { + symbol = shift_symbol[i]; + j = i; + while (j > 0 && shift_symbol[j - 1] > symbol) + { + shift_symbol[j] = shift_symbol[j - 1]; + j--; + } + shift_symbol[j] = symbol; + } + + for (i = 0; i < nshifts; i++) + { + symbol = shift_symbol[i]; + shiftset[i] = get_state(symbol); + } +} + +static void +free_storage(void) +{ + FREE(shift_symbol); + FREE(redset); + FREE(shiftset); + FREE(kernel_base); + FREE(kernel_end); + FREE(kernel_items); + FREE(state_set); +} + + +static void +generate_states(void) +{ + allocate_storage(); + itemset = NEW2(nitems, short); + ruleset = NEW2(WORDSIZE(nrules), unsigned); + set_first_derives(); + initialize_states(); + + while (this_state) + { + closure(this_state->items, this_state->nitems); + save_reductions(); + new_itemsets(); + append_states(); + + if (nshifts > 0) + save_shifts(); + + this_state = this_state->next; + } + + finalize_closure(); + free_storage(); +} + + + +static int +get_state(int symbol) +{ + int key; + short *isp1; + short *isp2; + short *iend; + core *sp; + int found; + int n; + +#ifdef TRACE + fprintf(stderr, "Entering get_state(%d)\n", symbol); +#endif + + isp1 = kernel_base[symbol]; + iend = kernel_end[symbol]; + n = iend - isp1; + + key = *isp1; + assert(0 <= key && key < nitems); + sp = state_set[key]; + if (sp) + { + found = 0; + while (!found) + { + if (sp->nitems == n) + { + found = 1; + isp1 = kernel_base[symbol]; + isp2 = sp->items; + + while (found && isp1 < iend) + { + if (*isp1++ != *isp2++) + found = 0; + } + } + + if (!found) + { + if (sp->link) + { + sp = sp->link; + } + else + { + sp = sp->link = new_state(symbol); + found = 1; + } + } + } + } + else + { + state_set[key] = sp = new_state(symbol); + } + + return (sp->number); +} + + +static void +initialize_states(void) +{ + int i; + short *start_derives; + core *p; + + start_derives = derives[start_symbol]; + for (i = 0; start_derives[i] >= 0; ++i) + continue; + + p = (core *) MALLOC(sizeof(core) + i*sizeof(short)); + if (p == 0) no_space(); + + p->next = 0; + p->link = 0; + p->number = 0; + p->accessing_symbol = 0; + p->nitems = i; + + for (i = 0; start_derives[i] >= 0; ++i) + p->items[i] = rrhs[start_derives[i]]; + + first_state = last_state = this_state = p; + nstates = 1; +} + +static void +new_itemsets(void) +{ + int i; + int shiftcount; + short *isp; + short *ksp; + int symbol; + + for (i = 0; i < nsyms; i++) + kernel_end[i] = 0; + + shiftcount = 0; + isp = itemset; + while (isp < itemsetend) + { + i = *isp++; + symbol = ritem[i]; + if (symbol > 0) + { + ksp = kernel_end[symbol]; + if (!ksp) + { + shift_symbol[shiftcount++] = symbol; + ksp = kernel_base[symbol]; + } + + *ksp++ = i + 1; + kernel_end[symbol] = ksp; + } + } + + nshifts = shiftcount; +} + + + +static core * +new_state(int symbol) +{ + int n; + core *p; + short *isp1; + short *isp2; + short *iend; + +#ifdef TRACE + fprintf(stderr, "Entering new_state(%d)\n", symbol); +#endif + + if (nstates >= MAXSHORT) + fatal("too many states"); + + isp1 = kernel_base[symbol]; + iend = kernel_end[symbol]; + n = iend - isp1; + + p = (core *) allocate((unsigned) (sizeof(core) + (n - 1) * sizeof(short))); + p->accessing_symbol = symbol; + p->number = nstates; + p->nitems = n; + + isp2 = p->items; + while (isp1 < iend) + *isp2++ = *isp1++; + + last_state->next = p; + last_state = p; + + nstates++; + + return (p); +} + + +#ifdef DEBUG +static void +show_cores(void) +{ + core *p; + int i, j, k, n; + int itemno; + + k = 0; + for (p = first_state; p; ++k, p = p->next) + { + if (k) printf("\n"); + printf("state %d, number = %d, accessing symbol = %s\n", + k, p->number, symbol_name[p->accessing_symbol]); + n = p->nitems; + for (i = 0; i < n; ++i) + { + itemno = p->items[i]; + printf("%4d ", itemno); + j = itemno; + while (ritem[j] >= 0) ++j; + printf("%s :", symbol_name[rlhs[-ritem[j]]]); + j = rrhs[-ritem[j]]; + while (j < itemno) + printf(" %s", symbol_name[ritem[j++]]); + printf(" ."); + while (ritem[j] >= 0) + printf(" %s", symbol_name[ritem[j++]]); + printf("\n"); + fflush(stdout); + } + } +} + + +static void +show_ritems(void) +{ + int i; + + for (i = 0; i < nitems; ++i) + printf("ritem[%d] = %d\n", i, ritem[i]); +} + + +static void +show_rrhs(void) +{ + int i; + + for (i = 0; i < nrules; ++i) + printf("rrhs[%d] = %d\n", i, rrhs[i]); +} + + +static void +show_shifts(void) +{ + shifts *p; + int i, j, k; + + k = 0; + for (p = first_shift; p; ++k, p = p->next) + { + if (k) printf("\n"); + printf("shift %d, number = %d, nshifts = %d\n", k, p->number, + p->nshifts); + j = p->nshifts; + for (i = 0; i < j; ++i) + printf("\t%d\n", p->shift[i]); + } +} +#endif + +static void +save_shifts(void) +{ + shifts *p; + short *sp1; + short *sp2; + short *send; + + p = (shifts *) allocate((unsigned) (sizeof(shifts) + + (nshifts - 1) * sizeof(short))); + + p->number = this_state->number; + p->nshifts = nshifts; + + sp1 = shiftset; + sp2 = p->shift; + send = shiftset + nshifts; + + while (sp1 < send) + *sp2++ = *sp1++; + + if (last_shift) + { + last_shift->next = p; + last_shift = p; + } + else + { + first_shift = p; + last_shift = p; + } +} + + +static void +save_reductions(void) +{ + short *isp; + short *rp1; + short *rp2; + int item; + int count; + reductions *p; + short *rend; + + count = 0; + for (isp = itemset; isp < itemsetend; isp++) + { + item = ritem[*isp]; + if (item < 0) + { + redset[count++] = -item; + } + } + + if (count) + { + p = (reductions *) allocate((unsigned) (sizeof(reductions) + + (count - 1) * sizeof(short))); + + p->number = this_state->number; + p->nreds = count; + + rp1 = redset; + rp2 = p->rules; + rend = rp1 + count; + + while (rp1 < rend) + *rp2++ = *rp1++; + + if (last_reduction) + { + last_reduction->next = p; + last_reduction = p; + } + else + { + first_reduction = p; + last_reduction = p; + } + } +} + +static void +set_derives(void) +{ + int i, k; + int lhs; + short *rules; + + derives = NEW2(nsyms, short *); + rules = NEW2(nvars + nrules, short); + + k = 0; + for (lhs = start_symbol; lhs < nsyms; lhs++) + { + derives[lhs] = rules + k; + for (i = 0; i < nrules; i++) + { + if (rlhs[i] == lhs) + { + rules[k] = i; + k++; + } + } + rules[k] = -1; + k++; + } + +#ifdef DEBUG + print_derives(); +#endif +} + +void +free_derives(void) +{ + FREE(derives[start_symbol]); + FREE(derives); +} + +#ifdef DEBUG +static void +print_derives(void) +{ + int i; + short *sp; + + printf("\nDERIVES\n\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("%s derives ", symbol_name[i]); + for (sp = derives[i]; *sp >= 0; sp++) + { + printf(" %d", *sp); + } + putchar('\n'); + } + + putchar('\n'); +} +#endif + +static void +set_nullable(void) +{ + int i, j; + int empty; + int done; + + nullable = MALLOC(nsyms); + if (nullable == 0) no_space(); + + for (i = 0; i < nsyms; ++i) + nullable[i] = 0; + + done = 0; + while (!done) + { + done = 1; + for (i = 1; i < nitems; i++) + { + empty = 1; + while ((j = ritem[i]) >= 0) + { + if (!nullable[j]) + empty = 0; + ++i; + } + if (empty) + { + j = rlhs[-j]; + if (!nullable[j]) + { + nullable[j] = 1; + done = 0; + } + } + } + } + +#ifdef DEBUG + for (i = 0; i < nsyms; i++) + { + if (nullable[i]) + printf("%s is nullable\n", symbol_name[i]); + else + printf("%s is not nullable\n", symbol_name[i]); + } +#endif +} + +void +free_nullable(void) +{ + FREE(nullable); +} + +void +lr0(void) +{ + set_derives(); + set_nullable(); + generate_states(); +} diff --git a/src/toolsComm/antelope/main.c b/src/toolsComm/antelope/main.c new file mode 100644 index 000000000..ba39e61e7 --- /dev/null +++ b/src/toolsComm/antelope/main.c @@ -0,0 +1,341 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include +#include "defs.h" +#include "epicsStdio.h" + +char dflag; +char lflag; +char rflag; +char tflag; +char vflag; + +char *symbol_prefix; +char *file_prefix = "y"; +char *myname = "yacc"; +char *temp_form = "yacc.XXXXXXX"; + +int lineno; +int outline; + +char *code_file_name; +char *defines_file_name; +char *input_file_name = ""; +char *output_file_name; +char *verbose_file_name; + +FILE *action_file; /* a temp file, used to save actions associated */ + /* with rules until the parser is written */ +FILE *code_file; /* y.code.c (used when the -r option is specified) */ +FILE *defines_file; /* y.tab.h */ +FILE *input_file; /* the input file */ +FILE *output_file; /* y.tab.c */ +FILE *text_file; /* a temp file, used to save text until all */ + /* symbols have been defined */ +FILE *union_file; /* a temp file, used to save the union */ + /* definition until all symbol have been */ + /* defined */ +FILE *verbose_file; /* y.output */ + +int nitems; +int nrules; +int nsyms; +int ntokens; +int nvars; + +int start_symbol; +char **symbol_name; +short *symbol_value; +short *symbol_prec; +char *symbol_assoc; + +short *ritem; +short *rlhs; +short *rrhs; +short *rprec; +char *rassoc; +short **derives; +char *nullable; + + +void +done(int k) +{ + if (action_file) { fclose(action_file); } + if (text_file) { fclose(text_file); } + if (union_file) { fclose(union_file); } + exit(k); +} + + +static void +onintr(int StupidInconsistantSignalTypes) +{ + done(1); +} + + +static void +set_signals(void) +{ +#ifdef SIGINT + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); +#endif +#ifdef SIGTERM + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, onintr); +#endif +#ifdef SIGHUP + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, onintr); +#endif +} + + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] [-p symbol_prefix] filename\n", myname); + exit(1); +} + + +static int +getargs(int argc, char *argv[]) +{ + int i; + char *s; + + if (argc > 0) myname = argv[0]; + for (i = 1; i < argc; ++i) + { + s = argv[i]; + if (*s != '-') break; + switch (*++s) + { + case '\0': + input_file = stdin; + if (i + 1 < argc) usage(); + return(0); + + case '-': + ++i; + goto no_more_options; + + case 'b': + if (*++s) + file_prefix = s; + else if (++i < argc) + file_prefix = argv[i]; + else + usage(); + continue; + + case 'd': + dflag = 1; + break; + + case 'l': + lflag = 1; + break; + + case 'p': + if (*++s) + symbol_prefix = s; + else if (++i < argc) + symbol_prefix = argv[i]; + else + usage(); + continue; + + case 'r': + rflag = 1; + break; + + case 't': + tflag = 1; + break; + + case 'v': + vflag = 1; + break; + + default: + usage(); + } + + for (;;) + { + switch (*++s) + { + case '\0': + goto end_of_option; + + case 'd': + dflag = 1; + break; + + case 'l': + lflag = 1; + break; + + case 'r': + rflag = 1; + break; + + case 't': + tflag = 1; + break; + + case 'v': + vflag = 1; + break; + + default: + usage(); + } + } +end_of_option:; + } + +no_more_options:; + if (i + 1 != argc) usage(); + input_file_name = argv[i]; + + return(0); +} + + +char * +allocate(unsigned int n) +{ + char *p; + + p = NULL; + if (n) + { + p = CALLOC(1, n); + if (!p) no_space(); + } + return (p); +} + + +static void +create_file_names(void) +{ + int len; + + len = strlen(file_prefix); + + output_file_name = MALLOC(len + 7); + if (output_file_name == 0) + no_space(); + strcpy(output_file_name, file_prefix); + strcpy(output_file_name + len, OUTPUT_SUFFIX); + + if (rflag) + { + code_file_name = MALLOC(len + 8); + if (code_file_name == 0) + no_space(); + strcpy(code_file_name, file_prefix); + strcpy(code_file_name + len, CODE_SUFFIX); + } + else + code_file_name = output_file_name; + + if (dflag) + { + defines_file_name = MALLOC(len + 7); + if (defines_file_name == 0) + no_space(); + strcpy(defines_file_name, file_prefix); + strcpy(defines_file_name + len, DEFINES_SUFFIX); + } + + if (vflag) + { + verbose_file_name = MALLOC(len + 8); + if (verbose_file_name == 0) + no_space(); + strcpy(verbose_file_name, file_prefix); + strcpy(verbose_file_name + len, VERBOSE_SUFFIX); + } +} + + +static void +open_files(void) +{ + create_file_names(); + + if (input_file == 0) + { + input_file = fopen(input_file_name, "r"); + if (input_file == 0) + open_error(input_file_name); + } + + action_file = epicsTempFile(); + if (action_file == 0) + open_error("temp action file"); + + text_file = epicsTempFile(); + if (text_file == 0) + open_error("temp text file"); + + if (vflag) + { + verbose_file = fopen(verbose_file_name, "w"); + if (verbose_file == 0) + open_error(verbose_file_name); + } + + if (dflag) + { + defines_file = fopen(defines_file_name, "w"); + if (defines_file == 0) + open_error(defines_file_name); + union_file = epicsTempFile(); + if (union_file == 0) + open_error("temp union file"); + } + + output_file = fopen(output_file_name, "w"); + if (output_file == 0) + open_error(output_file_name); + + if (rflag) + { + code_file = fopen(code_file_name, "w"); + if (code_file == 0) + open_error(code_file_name); + } + else + code_file = output_file; +} + + +int +main(int argc, char *argv[]) +{ + set_signals(); + getargs(argc, argv); + open_files(); + reader(); + lr0(); + lalr(); + make_parser(); + verbose(); + output(); + done(0); + /*NOTREACHED*/ +} diff --git a/src/toolsComm/antelope/mkpar.c b/src/toolsComm/antelope/mkpar.c new file mode 100644 index 000000000..5385f0e00 --- /dev/null +++ b/src/toolsComm/antelope/mkpar.c @@ -0,0 +1,372 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +action **parser; +int SRtotal; +int RRtotal; +short *SRconflicts; +short *RRconflicts; +short *defred; +short *rules_used; +short nunused; +short final_state; + +static int SRcount; +static int RRcount; + + +static action *parse_actions(int stateno); +static action *get_shifts(int stateno); +static action *add_reductions(int stateno, action *actions); +static action *add_reduce(action *actions, int ruleno, int symbol); +static void find_final_state(void); +static void unused_rules(void); +static void remove_conflicts(void); +static void total_conflicts(void); +static void defreds(void); + +void +make_parser(void) +{ + int i; + + parser = NEW2(nstates, action *); + for (i = 0; i < nstates; i++) + parser[i] = parse_actions(i); + + find_final_state(); + remove_conflicts(); + unused_rules(); + if (SRtotal + RRtotal > 0) total_conflicts(); + defreds(); +} + + +static action * +parse_actions(int stateno) +{ + action *actions; + + actions = get_shifts(stateno); + actions = add_reductions(stateno, actions); + return (actions); +} + + +static action * +get_shifts(int stateno) +{ + action *actions, *temp; + shifts *sp; + short *to_state; + int i, k; + int symbol; + + actions = 0; + sp = shift_table[stateno]; + if (sp) + { + to_state = sp->shift; + for (i = sp->nshifts - 1; i >= 0; i--) + { + k = to_state[i]; + symbol = accessing_symbol[k]; + if (ISTOKEN(symbol)) + { + temp = NEW(action); + temp->next = actions; + temp->symbol = symbol; + temp->number = k; + temp->prec = symbol_prec[symbol]; + temp->action_code = SHIFT; + temp->assoc = symbol_assoc[symbol]; + actions = temp; + } + } + } + return (actions); +} + +static action * +add_reductions(int stateno, action *actions) +{ + int i, j, m, n; + int ruleno, tokensetsize; + unsigned *rowp; + + tokensetsize = WORDSIZE(ntokens); + m = lookaheads[stateno]; + n = lookaheads[stateno + 1]; + for (i = m; i < n; i++) + { + ruleno = LAruleno[i]; + rowp = LA + i * tokensetsize; + for (j = ntokens - 1; j >= 0; j--) + { + if (BIT(rowp, j)) + actions = add_reduce(actions, ruleno, j); + } + } + return (actions); +} + + +static action * +add_reduce(action *actions, int ruleno, int symbol) +{ + action *temp, *prev, *next; + + prev = 0; + for (next = actions; next && next->symbol < symbol; next = next->next) + prev = next; + + while (next && next->symbol == symbol && next->action_code == SHIFT) + { + prev = next; + next = next->next; + } + + while (next && next->symbol == symbol && + next->action_code == REDUCE && next->number < ruleno) + { + prev = next; + next = next->next; + } + + temp = NEW(action); + temp->next = next; + temp->symbol = symbol; + temp->number = ruleno; + temp->prec = rprec[ruleno]; + temp->action_code = REDUCE; + temp->assoc = rassoc[ruleno]; + + if (prev) + prev->next = temp; + else + actions = temp; + + return (actions); +} + + +static void +find_final_state(void) +{ + int goal, i; + short *to_state; + shifts *p; + + p = shift_table[0]; + to_state = p->shift; + goal = ritem[1]; + for (i = p->nshifts - 1; i >= 0; --i) + { + final_state = to_state[i]; + if (accessing_symbol[final_state] == goal) break; + } +} + + +static void +unused_rules(void) +{ + int i; + action *p; + + rules_used = (short *) MALLOC(nrules*sizeof(short)); + if (rules_used == 0) no_space(); + + for (i = 0; i < nrules; ++i) + rules_used[i] = 0; + + for (i = 0; i < nstates; ++i) + { + for (p = parser[i]; p; p = p->next) + { + if (p->action_code == REDUCE && p->suppressed == 0) + rules_used[p->number] = 1; + } + } + + nunused = 0; + for (i = 3; i < nrules; ++i) + if (!rules_used[i]) ++nunused; + + if (nunused) + { + if (nunused == 1) + fprintf(stderr, "%s: 1 rule never reduced\n", myname); + else + fprintf(stderr, "%s: %d rules never reduced\n", myname, nunused); + } +} + + +static void +remove_conflicts(void) +{ + int i; + int symbol; + action *p, *pref = NULL; + + SRtotal = 0; + RRtotal = 0; + SRconflicts = NEW2(nstates, short); + RRconflicts = NEW2(nstates, short); + for (i = 0; i < nstates; i++) + { + SRcount = 0; + RRcount = 0; + symbol = -1; + for (p = parser[i]; p; p = p->next) + { + if (p->symbol != symbol) + { + pref = p; + symbol = p->symbol; + } + else if (i == final_state && symbol == 0) + { + SRcount++; + p->suppressed = 1; + } + else if (pref && pref->action_code == SHIFT) + { + if (pref->prec > 0 && p->prec > 0) + { + if (pref->prec < p->prec) + { + pref->suppressed = 2; + pref = p; + } + else if (pref->prec > p->prec) + { + p->suppressed = 2; + } + else if (pref->assoc == LEFT) + { + pref->suppressed = 2; + pref = p; + } + else if (pref->assoc == RIGHT) + { + p->suppressed = 2; + } + else + { + pref->suppressed = 2; + p->suppressed = 2; + } + } + else + { + SRcount++; + p->suppressed = 1; + } + } + else + { + RRcount++; + p->suppressed = 1; + } + } + SRtotal += SRcount; + RRtotal += RRcount; + SRconflicts[i] = SRcount; + RRconflicts[i] = RRcount; + } +} + + +static void +total_conflicts(void) +{ + fprintf(stderr, "%s: ", myname); + if (SRtotal == 1) + fprintf(stderr, "1 shift/reduce conflict"); + else if (SRtotal > 1) + fprintf(stderr, "%d shift/reduce conflicts", SRtotal); + + if (SRtotal && RRtotal) + fprintf(stderr, ", "); + + if (RRtotal == 1) + fprintf(stderr, "1 reduce/reduce conflict"); + else if (RRtotal > 1) + fprintf(stderr, "%d reduce/reduce conflicts", RRtotal); + + fprintf(stderr, ".\n"); +} + + +static int +sole_reduction(int stateno) +{ + int count, ruleno; + action *p; + + count = 0; + ruleno = 0; + for (p = parser[stateno]; p; p = p->next) + { + if (p->action_code == SHIFT && p->suppressed == 0) + return 0; + else if (p->action_code == REDUCE && p->suppressed == 0) + { + if (ruleno > 0 && p->number != ruleno) + return 0; + if (p->symbol != 1) + ++count; + ruleno = p->number; + } + } + + if (count == 0) + return 0; + return ruleno; +} + + +static void +defreds(void) +{ + int i; + + defred = NEW2(nstates, short); + for (i = 0; i < nstates; i++) + defred[i] = sole_reduction(i); +} + +static void +free_action_row(action *p) +{ + action *q; + + while (p) + { + q = p->next; + FREE(p); + p = q; + } +} + +void +free_parser(void) +{ + int i; + + for (i = 0; i < nstates; i++) + free_action_row(parser[i]); + + FREE(parser); +} + diff --git a/src/toolsComm/antelope/output.c b/src/toolsComm/antelope/output.c new file mode 100644 index 000000000..a1913ed77 --- /dev/null +++ b/src/toolsComm/antelope/output.c @@ -0,0 +1,1247 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +static int nvectors; +static int nentries; +static short **froms; +static short **tos; +static short *tally; +static short *width; +static short *state_count; +static short *order; +static short *base; +static short *pos; +static int maxtable; +static short *table; +static short *check; +static int lowzero; +static int high; + +static void output_prefix(void); +static void output_rule_data(void); +static void output_yydefred(void); +static void output_actions(void); +static void token_actions(void); +static void goto_actions(void); +static void save_column(int symbol, int default_state); +static void sort_actions(void); +static void pack_table(void); +static void output_base(void); +static void output_table(void); +static void output_check(void); +static void output_defines(void); +static void output_stored_text(void); +static void output_debug(void); +static void output_stype(void); +static void output_trailing_text(void); +static void output_semantic_actions(void); +static void free_itemsets(void); +static void free_shifts(void); +static void free_reductions(void); + + +void +output(void) +{ + free_itemsets(); + free_shifts(); + free_reductions(); + output_prefix(); + output_stored_text(); + output_defines(); + output_rule_data(); + output_yydefred(); + output_actions(); + free_parser(); + output_debug(); + output_stype(); + if (rflag) write_section(tables); + write_section(header); + output_trailing_text(); + write_section(body); + output_semantic_actions(); + write_section(trailer); +} + + +static void +output_prefix(void) +{ + if (symbol_prefix == NULL) + symbol_prefix = "yy"; + else + { + ++outline; + fprintf(code_file, "#define yyparse %sparse\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylex %slex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerror %serror\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yychar %schar\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyval %sval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylval %slval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydebug %sdebug\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yynerrs %snerrs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerrflag %serrflag\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyss %sss\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyssp %sssp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvs %svs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvsp %svsp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylhs %slhs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylen %slen\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydefred %sdefred\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydgoto %sdgoto\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yysindex %ssindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrindex %srindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yygindex %sgindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yytable %stable\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yycheck %scheck\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyname %sname\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrule %srule\n", symbol_prefix); + } + ++outline; + fprintf(code_file, "#define YYPREFIX \"%s\"\n", symbol_prefix); +} + + +static void +output_rule_data(void) +{ + int i; + int j; + + fprintf(output_file, "static short %slhs[] = {%42d,", symbol_prefix, + symbol_value[start_symbol]); + + j = 10; + for (i = 3; i < nrules; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", symbol_value[rlhs[i]]); + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + + fprintf(output_file, "static short %slen[] = {%42d,", symbol_prefix, 2); + + j = 10; + for (i = 3; i < nrules; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + j++; + + fprintf(output_file, "%5d,", rrhs[i + 1] - rrhs[i] - 1); + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); +} + + +static void +output_yydefred(void) +{ + int i, j; + + fprintf(output_file, "static short %sdefred[] = {%39d,", symbol_prefix, + (defred[0] ? defred[0] - 2 : 0)); + + j = 10; + for (i = 1; i < nstates; i++) + { + if (j < 10) + ++j; + else + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + + fprintf(output_file, "%5d,", (defred[i] ? defred[i] - 2 : 0)); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); +} + + +static void +output_actions(void) +{ + nvectors = 2*nstates + nvars; + + froms = NEW2(nvectors, short *); + tos = NEW2(nvectors, short *); + tally = NEW2(nvectors, short); + width = NEW2(nvectors, short); + + token_actions(); + FREE(lookaheads); + FREE(LA); + FREE(LAruleno); + FREE(accessing_symbol); + + goto_actions(); + FREE(goto_map + ntokens); + FREE(from_state); + FREE(to_state); + + sort_actions(); + pack_table(); + output_base(); + output_table(); + output_check(); +} + + +static void +token_actions(void) +{ + int i, j; + int shiftcount, reducecount; + int max, min; + short *actionrow, *r, *s; + action *p; + + actionrow = NEW2(2*ntokens, short); + for (i = 0; i < nstates; ++i) + { + if (parser[i]) + { + for (j = 0; j < 2*ntokens; ++j) + actionrow[j] = 0; + + shiftcount = 0; + reducecount = 0; + for (p = parser[i]; p; p = p->next) + { + if (p->suppressed == 0) + { + if (p->action_code == SHIFT) + { + ++shiftcount; + actionrow[p->symbol] = p->number; + } + else if (p->action_code == REDUCE && p->number != defred[i]) + { + ++reducecount; + actionrow[p->symbol + ntokens] = p->number; + } + } + } + + tally[i] = shiftcount; + tally[nstates+i] = reducecount; + width[i] = 0; + width[nstates+i] = 0; + if (shiftcount > 0) + { + froms[i] = r = NEW2(shiftcount, short); + tos[i] = s = NEW2(shiftcount, short); + min = MAXSHORT; + max = 0; + for (j = 0; j < ntokens; ++j) + { + if (actionrow[j]) + { + if (min > symbol_value[j]) + min = symbol_value[j]; + if (max < symbol_value[j]) + max = symbol_value[j]; + *r++ = symbol_value[j]; + *s++ = actionrow[j]; + } + } + width[i] = max - min + 1; + } + if (reducecount > 0) + { + froms[nstates+i] = r = NEW2(reducecount, short); + tos[nstates+i] = s = NEW2(reducecount, short); + min = MAXSHORT; + max = 0; + for (j = 0; j < ntokens; ++j) + { + if (actionrow[ntokens+j]) + { + if (min > symbol_value[j]) + min = symbol_value[j]; + if (max < symbol_value[j]) + max = symbol_value[j]; + *r++ = symbol_value[j]; + *s++ = actionrow[ntokens+j] - 2; + } + } + width[nstates+i] = max - min + 1; + } + } + } + FREE(actionrow); +} + +static void +goto_actions(void) +{ + int i, j, k; + + state_count = NEW2(nstates, short); + + k = default_goto(start_symbol + 1); + fprintf(output_file, "static short %sdgoto[] = {%40d,", symbol_prefix, k); + save_column(start_symbol + 1, k); + + j = 10; + for (i = start_symbol + 2; i < nsyms; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + k = default_goto(i); + fprintf(output_file, "%5d,", k); + save_column(i, k); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(state_count); +} + +int +default_goto(int symbol) +{ + int i; + int m; + int n; + int default_state; + int max; + + m = goto_map[symbol]; + n = goto_map[symbol + 1]; + + if (m == n) return (0); + + for (i = 0; i < nstates; i++) + state_count[i] = 0; + + for (i = m; i < n; i++) + state_count[to_state[i]]++; + + max = 0; + default_state = 0; + for (i = 0; i < nstates; i++) + { + if (state_count[i] > max) + { + max = state_count[i]; + default_state = i; + } + } + + return (default_state); +} + + + + +static void +save_column(int symbol, int default_state) +{ + int i; + int m; + int n; + short *sp; + short *sp1; + short *sp2; + int count; + int symno; + + m = goto_map[symbol]; + n = goto_map[symbol + 1]; + + count = 0; + for (i = m; i < n; i++) + { + if (to_state[i] != default_state) + ++count; + } + if (count == 0) return; + + symno = symbol_value[symbol] + 2*nstates; + + froms[symno] = sp1 = sp = NEW2(count, short); + tos[symno] = sp2 = NEW2(count, short); + + for (i = m; i < n; i++) + { + if (to_state[i] != default_state) + { + *sp1++ = from_state[i]; + *sp2++ = to_state[i]; + } + } + + tally[symno] = count; + width[symno] = sp1[-1] - sp[0] + 1; +} + +static void +sort_actions(void) +{ + int i; + int j; + int k; + int t; + int w; + + order = NEW2(nvectors, short); + nentries = 0; + + for (i = 0; i < nvectors; i++) + { + if (tally[i] > 0) + { + t = tally[i]; + w = width[i]; + j = nentries - 1; + + while (j >= 0 && (width[order[j]] < w)) + j--; + + while (j >= 0 && (width[order[j]] == w) && (tally[order[j]] < t)) + j--; + + for (k = nentries - 1; k > j; k--) + order[k + 1] = order[k]; + + order[j + 1] = i; + nentries++; + } + } +} + + +static void +pack_table(void) +{ + int i; + int place; + int state; + + base = NEW2(nvectors, short); + pos = NEW2(nentries, short); + + maxtable = 1000; + table = NEW2(maxtable, short); + check = NEW2(maxtable, short); + + lowzero = 0; + high = 0; + + for (i = 0; i < maxtable; i++) + check[i] = -1; + + for (i = 0; i < nentries; i++) + { + state = matching_vector(i); + + if (state < 0) + place = pack_vector(i); + else + place = base[state]; + + pos[i] = place; + base[order[i]] = place; + } + + for (i = 0; i < nvectors; i++) + { + if (froms[i]) + FREE(froms[i]); + if (tos[i]) + FREE(tos[i]); + } + + FREE(froms); + FREE(tos); + FREE(pos); +} + + +/* The function matching_vector determines if the vector specified by */ +/* the input parameter matches a previously considered vector. The */ +/* test at the start of the function checks if the vector represents */ +/* a row of shifts over terminal symbols or a row of reductions, or a */ +/* column of shifts over a nonterminal symbol. Berkeley Yacc does not */ +/* check if a column of shifts over a nonterminal symbols matches a */ +/* previously considered vector. Because of the nature of LR parsing */ +/* tables, no two columns can match. Therefore, the only possible */ +/* match would be between a row and a column. Such matches are */ +/* unlikely. Therefore, to save time, no attempt is made to see if a */ +/* column matches a previously considered vector. */ +/* */ +/* Matching_vector is poorly designed. The test could easily be made */ +/* faster. Also, it depends on the vectors being in a specific */ +/* order. */ + +int +matching_vector(int vector) +{ + int i; + int j; + int k; + int t; + int w; + int match; + int prev; + + i = order[vector]; + if (i >= 2*nstates) + return (-1); + + t = tally[i]; + w = width[i]; + + for (prev = vector - 1; prev >= 0; prev--) + { + j = order[prev]; + if (width[j] != w || tally[j] != t) + return (-1); + + match = 1; + for (k = 0; match && k < t; k++) + { + if (tos[j][k] != tos[i][k] || froms[j][k] != froms[i][k]) + match = 0; + } + + if (match) + return (j); + } + + return (-1); +} + + + +int +pack_vector(int vector) +{ + int i, j, k, l; + int t; + int loc; + int ok; + short *from; + short *to; + int newmax; + + i = order[vector]; + t = tally[i]; + assert(t); + + from = froms[i]; + to = tos[i]; + + j = lowzero - from[0]; + for (k = 1; k < t; ++k) + if (lowzero - from[k] > j) + j = lowzero - from[k]; + for (;; ++j) + { + if (j == 0) + continue; + ok = 1; + for (k = 0; ok && k < t; k++) + { + loc = j + from[k]; + if (loc >= maxtable) + { + if (loc >= MAXTABLE) + fatal("maximum table size exceeded"); + + newmax = maxtable; + do { newmax += 200; } while (newmax <= loc); + table = (short *) REALLOC(table, newmax*sizeof(short)); + if (table == 0) no_space(); + check = (short *) REALLOC(check, newmax*sizeof(short)); + if (check == 0) no_space(); + for (l = maxtable; l < newmax; ++l) + { + table[l] = 0; + check[l] = -1; + } + maxtable = newmax; + } + + if (check[loc] != -1) + ok = 0; + } + for (k = 0; ok && k < vector; k++) + { + if (pos[k] == j) + ok = 0; + } + if (ok) + { + for (k = 0; k < t; k++) + { + loc = j + from[k]; + table[loc] = to[k]; + check[loc] = from[k]; + if (loc > high) high = loc; + } + + while (check[lowzero] != -1) + ++lowzero; + + return (j); + } + } +} + + + +static void +output_base(void) +{ + int i, j; + + fprintf(output_file, "static short %ssindex[] = {%39d,", symbol_prefix, base[0]); + + j = 10; + for (i = 1; i < nstates; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\nstatic short %srindex[] = {%39d,", symbol_prefix, + base[nstates]); + + j = 10; + for (i = nstates + 1; i < 2*nstates; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\nstatic short %sgindex[] = {%39d,", symbol_prefix, + base[2*nstates]); + + j = 10; + for (i = 2*nstates + 1; i < nvectors - 1; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(base); +} + + + +static void +output_table(void) +{ + int i; + int j; + + ++outline; + fprintf(code_file, "#define YYTABLESIZE %d\n", high); + fprintf(output_file, "static short %stable[] = {%40d,", symbol_prefix, + table[0]); + + j = 10; + for (i = 1; i <= high; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", table[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(table); +} + + + +static void +output_check(void) +{ + int i; + int j; + + fprintf(output_file, "static short %scheck[] = {%40d,", symbol_prefix, + check[0]); + + j = 10; + for (i = 1; i <= high; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", check[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(check); +} + + +static int +is_C_identifier(char *name) +{ + char *s; + int c; + + s = name; + c = *s; + if (c == '"') + { + c = *++s; + if (!isalpha(c) && c != '_' && c != '$') + return (0); + while ((c = *++s) != '"') + { + if (!isalnum(c) && c != '_' && c != '$') + return (0); + } + return (1); + } + + if (!isalpha(c) && c != '_' && c != '$') + return (0); + while ((c = *++s)) + { + if (!isalnum(c) && c != '_' && c != '$') + return (0); + } + return (1); +} + + +static void +output_defines(void) +{ + int c, i; + char *s; + + for (i = 2; i < ntokens; ++i) + { + s = symbol_name[i]; + if (is_C_identifier(s)) + { + fprintf(code_file, "#define "); + if (dflag) fprintf(defines_file, "#define "); + c = *s; + if (c == '"') + { + while ((c = *++s) != '"') + { + putc(c, code_file); + if (dflag) putc(c, defines_file); + } + } + else + { + do + { + putc(c, code_file); + if (dflag) putc(c, defines_file); + } + while ((c = *++s)); + } + ++outline; + fprintf(code_file, " %d\n", symbol_value[i]); + if (dflag) fprintf(defines_file, " %d\n", symbol_value[i]); + } + } + + ++outline; + fprintf(code_file, "#define YYERRCODE %d\n", symbol_value[1]); + + if (dflag && unionized) + { + rewind(union_file); + while ((c = getc(union_file)) != EOF) + putc(c, defines_file); + fprintf(defines_file, " YYSTYPE;\nstatic YYSTYPE %slval;\n", + symbol_prefix); + } +} + + +static void +output_stored_text(void) +{ + int c; + FILE *in, *out; + + rewind(text_file); + in = text_file; + if ((c = getc(in)) == EOF) + return; + out = code_file; + if (c == '\n') + ++outline; + putc(c, out); + while ((c = getc(in)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + } + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +static void +output_debug(void) +{ + int i, j, k, max; + char **symnam, *s; + + ++outline; + fprintf(code_file, "#define YYFINAL %d\n", final_state); + outline += 3; + fprintf(code_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", + tflag); + if (rflag) + fprintf(output_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", + tflag); + + max = 0; + for (i = 2; i < ntokens; ++i) + if (symbol_value[i] > max) + max = symbol_value[i]; + ++outline; + fprintf(code_file, "#define YYMAXTOKEN %d\n", max); + + symnam = (char **) MALLOC((max+1)*sizeof(char *)); + if (symnam == 0) no_space(); + + /* Note that it is not necessary to initialize the element */ + /* symnam[max]. */ + for (i = 0; i < max; ++i) + symnam[i] = 0; + for (i = ntokens - 1; i >= 2; --i) + symnam[symbol_value[i]] = symbol_name[i]; + symnam[0] = "end-of-file"; + + if (!rflag) ++outline; + fprintf(output_file, "#if YYDEBUG\nstatic char *%sname[] = {", symbol_prefix); + j = 80; + for (i = 0; i <= max; ++i) + { + if ((s = symnam[i])) + { + if (s[0] == '"') + { + k = 7; + while (*++s != '"') + { + ++k; + if (*s == '\\') + { + k += 2; + if (*++s == '\\') + ++k; + } + } + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + fprintf(output_file, "\"\\\""); + s = symnam[i]; + while (*++s != '"') + { + if (*s == '\\') + { + fprintf(output_file, "\\\\"); + if (*++s == '\\') + fprintf(output_file, "\\\\"); + else + putc(*s, output_file); + } + else + putc(*s, output_file); + } + fprintf(output_file, "\\\"\","); + } + else if (s[0] == '\'') + { + if (s[1] == '"') + { + j += 7; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 7; + } + fprintf(output_file, "\"'\\\"'\","); + } + else + { + k = 5; + while (*++s != '\'') + { + ++k; + if (*s == '\\') + { + k += 2; + if (*++s == '\\') + ++k; + } + } + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + fprintf(output_file, "\"'"); + s = symnam[i]; + while (*++s != '\'') + { + if (*s == '\\') + { + fprintf(output_file, "\\\\"); + if (*++s == '\\') + fprintf(output_file, "\\\\"); + else + putc(*s, output_file); + } + else + putc(*s, output_file); + } + fprintf(output_file, "'\","); + } + } + else + { + k = strlen(s) + 3; + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + putc('"', output_file); + do { putc(*s, output_file); } while (*++s); + fprintf(output_file, "\","); + } + } + else + { + j += 2; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 2; + } + fprintf(output_file, "0,"); + } + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(symnam); + + if (!rflag) ++outline; + fprintf(output_file, "static char *%srule[] = {\n", symbol_prefix); + for (i = 2; i < nrules; ++i) + { + fprintf(output_file, "\"%s :", symbol_name[rlhs[i]]); + for (j = rrhs[i]; ritem[j] > 0; ++j) + { + s = symbol_name[ritem[j]]; + if (s[0] == '"') + { + fprintf(output_file, " \\\""); + while (*++s != '"') + { + if (*s == '\\') + { + if (s[1] == '\\') + fprintf(output_file, "\\\\\\\\"); + else + fprintf(output_file, "\\\\%c", s[1]); + ++s; + } + else + putc(*s, output_file); + } + fprintf(output_file, "\\\""); + } + else if (s[0] == '\'') + { + if (s[1] == '"') + fprintf(output_file, " '\\\"'"); + else if (s[1] == '\\') + { + if (s[2] == '\\') + fprintf(output_file, " '\\\\\\\\"); + else + fprintf(output_file, " '\\\\%c", s[2]); + s += 2; + while (*++s != '\'') + putc(*s, output_file); + putc('\'', output_file); + } + else + fprintf(output_file, " '%c'", s[1]); + } + else + fprintf(output_file, " %s", s); + } + if (!rflag) ++outline; + fprintf(output_file, "\",\n"); + } + + if (!rflag) outline += 2; + fprintf(output_file, "};\n#endif\n"); +} + + +static void +output_stype(void) +{ + if (!unionized && ntags == 0) + { + outline += 3; + fprintf(code_file, "#ifndef YYSTYPE\ntypedef int YYSTYPE;\n#endif\n"); + } +} + + +static void +output_trailing_text(void) +{ + int c, last; + FILE *in, *out; + + if (line == 0) + return; + + in = input_file; + out = code_file; + c = *cptr; + if (c == '\n') + { + ++lineno; + if ((c = getc(in)) == EOF) + return; + if (!lflag) + { + ++outline; + fprintf(out, line_format, lineno, input_file_name); + } + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + else + { + if (!lflag) + { + ++outline; + fprintf(out, line_format, lineno, input_file_name); + } + do { putc(c, out); } while ((c = *++cptr) != '\n'); + ++outline; + putc('\n', out); + last = '\n'; + } + + while ((c = getc(in)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + + if (last != '\n') + { + ++outline; + putc('\n', out); + } + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +static void +output_semantic_actions(void) +{ + int c, last; + FILE *out; + + rewind(action_file); + + if ((c = getc(action_file)) == EOF) + return; + + out = code_file; + last = c; + if (c == '\n') + ++outline; + putc(c, out); + while ((c = getc(action_file)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + + if (last != '\n') + { + ++outline; + putc('\n', out); + } + + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +static void +free_itemsets(void) +{ + core *cp, *next; + + FREE(state_table); + for (cp = first_state; cp; cp = next) + { + next = cp->next; + FREE(cp); + } +} + + +static void +free_shifts(void) +{ + shifts *sp, *next; + + FREE(shift_table); + for (sp = first_shift; sp; sp = next) + { + next = sp->next; + FREE(sp); + } +} + + +static void +free_reductions(void) +{ + reductions *rp, *next; + + FREE(reduction_table); + for (rp = first_reduction; rp; rp = next) + { + next = rp->next; + FREE(rp); + } +} diff --git a/src/toolsComm/antelope/reader.c b/src/toolsComm/antelope/reader.c new file mode 100644 index 000000000..d6407fec1 --- /dev/null +++ b/src/toolsComm/antelope/reader.c @@ -0,0 +1,1805 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +/* The line size must be a positive integer. One hundred was chosen */ +/* because few lines in Yacc input grammars exceed 100 characters. */ +/* Note that if a line exceeds LINESIZE characters, the line buffer */ +/* will be expanded to accomodate it. */ + +#define LINESIZE 100 + +char *cache; +int cinc, cache_size; + +int ntags, tagmax; +char **tag_table; + +char saw_eof, unionized; +char *cptr, *line; +int linesize; + +bucket *goal; +int prec; +int gensym; +char last_was_action; + +int maxitems; +bucket **pitem; + +int maxrules; +bucket **plhs; + +int name_pool_size; +char *name_pool; + +char line_format[] = "#line %d \"%s\"\n"; + +#define static + +static void start_rule(bucket *bp, int s_lineno); + + +static void +cachec(int c) +{ + assert(cinc >= 0); + if (cinc >= cache_size) + { + cache_size += 256; + cache = REALLOC(cache, cache_size); + if (cache == 0) no_space(); + } + cache[cinc] = c; + ++cinc; +} + + +static void +get_line(void) +{ + FILE *f = input_file; + int c; + int i; + + if (saw_eof || (c = getc(f)) == EOF) + { + if (line) { FREE(line); line = 0; } + cptr = 0; + saw_eof = 1; + return; + } + + if (line == 0 || linesize != (LINESIZE + 1)) + { + if (line) FREE(line); + linesize = LINESIZE + 1; + line = MALLOC(linesize); + if (line == 0) no_space(); + } + + i = 0; + ++lineno; + for (;;) + { + line[i] = c; + if (c == '\n') { cptr = line; return; } + if (++i >= linesize) + { + linesize += LINESIZE; + line = REALLOC(line, linesize); + if (line == 0) no_space(); + } + c = getc(f); + if (c == EOF) + { + line[i] = '\n'; + saw_eof = 1; + cptr = line; + return; + } + } +} + + +static char * +dup_line(void) +{ + char *p, *s, *t; + + if (line == 0) return (0); + s = line; + while (*s != '\n') ++s; + p = MALLOC(s - line + 1); + if (p == 0) no_space(); + + s = line; + t = p; + while ((*t++ = *s++) != '\n') continue; + return (p); +} + + +static void +skip_comment(void) +{ + char *s; + + int st_lineno = lineno; + char *st_line = dup_line(); + char *st_cptr = st_line + (cptr - line); + + s = cptr + 2; + for (;;) + { + if (*s == '*' && s[1] == '/') + { + cptr = s + 2; + FREE(st_line); + return; + } + if (*s == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(st_lineno, st_line, st_cptr); + s = cptr; + } + else + ++s; + } +} + + +static int +nextc(void) +{ + char *s; + + if (line == 0) + { + get_line(); + if (line == 0) + return (EOF); + } + + s = cptr; + for (;;) + { + switch (*s) + { + case '\n': + get_line(); + if (line == 0) return (EOF); + s = cptr; + break; + + case ' ': + case '\t': + case '\f': + case '\r': + case '\v': + case ',': + case ';': + ++s; + break; + + case '\\': + cptr = s; + return ('%'); + + case '/': + if (s[1] == '*') + { + cptr = s; + skip_comment(); + s = cptr; + break; + } + else if (s[1] == '/') + { + get_line(); + if (line == 0) return (EOF); + s = cptr; + break; + } + /* fall through */ + + default: + cptr = s; + return (*s); + } + } +} + + +static int +keyword(void) +{ + int c; + char *t_cptr = cptr; + + c = *++cptr; + if (isalpha(c)) + { + cinc = 0; + for (;;) + { + if (isalpha(c)) + { + if (isupper(c)) c = tolower(c); + cachec(c); + } + else if (isdigit(c) || c == '_' || c == '.' || c == '$') + cachec(c); + else + break; + c = *++cptr; + } + cachec(NUL); + + if (strcmp(cache, "token") == 0 || strcmp(cache, "term") == 0) + return (TOKEN); + if (strcmp(cache, "type") == 0) + return (TYPE); + if (strcmp(cache, "left") == 0) + return (LEFT); + if (strcmp(cache, "right") == 0) + return (RIGHT); + if (strcmp(cache, "nonassoc") == 0 || strcmp(cache, "binary") == 0) + return (NONASSOC); + if (strcmp(cache, "start") == 0) + return (START); + if (strcmp(cache, "union") == 0) + return (UNION); + if (strcmp(cache, "ident") == 0) + return (IDENT); + } + else + { + ++cptr; + if (c == '{') + return (TEXT); + if (c == '%' || c == '\\') + return (MARK); + if (c == '<') + return (LEFT); + if (c == '>') + return (RIGHT); + if (c == '0') + return (TOKEN); + if (c == '2') + return (NONASSOC); + } + syntax_error(lineno, line, t_cptr); + /*NOTREACHED*/ +} + + +static void +copy_ident(void) +{ + int c; + FILE *f = output_file; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '"') syntax_error(lineno, line, cptr); + ++outline; + fprintf(f, "#ident \""); + for (;;) + { + c = *++cptr; + if (c == '\n') + { + fprintf(f, "\"\n"); + return; + } + putc(c, f); + if (c == '"') + { + putc('\n', f); + ++cptr; + return; + } + } +} + + +static void +copy_text(void) +{ + int c; + int quote; + FILE *f = text_file; + int need_newline = 0; + int t_lineno = lineno; + char *t_line = dup_line(); + char *t_cptr = t_line + (cptr - line - 2); + + if (*cptr == '\n') + { + get_line(); + if (line == 0) + unterminated_text(t_lineno, t_line, t_cptr); + } + if (!lflag) fprintf(f, line_format, lineno, input_file_name); + +loop: + c = *cptr++; + switch (c) + { + case '\n': + next_line: + putc('\n', f); + need_newline = 0; + get_line(); + if (line) goto loop; + unterminated_text(t_lineno, t_line, t_cptr); + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + putc(c, f); + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == quote) + { + need_newline = 1; + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, f); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + putc(c, f); + need_newline = 1; + c = *cptr; + if (c == '/') + { + putc('*', f); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + fprintf(f, "* "); + else + putc(c, f); + } + fprintf(f, "*/"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', f); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == '*' && *cptr == '/') + { + putc('/', f); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + need_newline = 1; + goto loop; + + case '%': + case '\\': + if (*cptr == '}') + { + if (need_newline) putc('\n', f); + ++cptr; + FREE(t_line); + return; + } + /* fall through */ + + default: + putc(c, f); + need_newline = 1; + goto loop; + } +} + + +static void +copy_union(void) +{ + int c; + int quote; + int depth; + int u_lineno = lineno; + char *u_line = dup_line(); + char *u_cptr = u_line + (cptr - line - 6); + + if (unionized) over_unionized(cptr - 6); + unionized = 1; + + if (!lflag) + fprintf(text_file, line_format, lineno, input_file_name); + + fprintf(text_file, "typedef union"); + if (dflag) fprintf(union_file, "typedef union"); + + depth = 0; +loop: + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + switch (c) + { + case '\n': + next_line: + get_line(); + if (line == 0) unterminated_union(u_lineno, u_line, u_cptr); + goto loop; + + case '{': + ++depth; + goto loop; + + case '}': + if (--depth == 0) + { + fprintf(text_file, " YYSTYPE;\n"); + FREE(u_line); + return; + } + goto loop; + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + for (;;) + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == quote) + { + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + c = *cptr; + if (c == '/') + { + putc('*', text_file); + if (dflag) putc('*', union_file); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + { + fprintf(text_file, "* "); + if (dflag) fprintf(union_file, "* "); + } + else + { + putc(c, text_file); + if (dflag) putc(c, union_file); + } + } + fprintf(text_file, "*/\n"); + if (dflag) fprintf(union_file, "*/\n"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', text_file); + if (dflag) putc('*', union_file); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == '*' && *cptr == '/') + { + putc('/', text_file); + if (dflag) putc('/', union_file); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + goto loop; + + default: + goto loop; + } +} + + +static int +hexval(int c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + return (-1); +} + + +static bucket * +get_literal(void) +{ + int c, quote; + int i; + int n; + char *s; + bucket *bp; + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line); + + quote = *cptr++; + cinc = 0; + for (;;) + { + c = *cptr++; + if (c == quote) break; + if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + char *c_cptr = cptr - 1; + + c = *cptr++; + switch (c) + { + case '\n': + get_line(); + if (line == 0) unterminated_string(s_lineno, s_line, s_cptr); + continue; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + n = c - '0'; + c = *cptr; + if (IS_OCTAL(c)) + { + n = (n << 3) + (c - '0'); + c = *++cptr; + if (IS_OCTAL(c)) + { + n = (n << 3) + (c - '0'); + ++cptr; + } + } + if (n > MAXCHAR) illegal_character(c_cptr); + c = n; + break; + + case 'x': + c = *cptr++; + n = hexval(c); + if (n < 0 || n >= 16) + illegal_character(c_cptr); + for (;;) + { + c = *cptr; + i = hexval(c); + if (i < 0 || i >= 16) break; + ++cptr; + n = (n << 4) + i; + if (n > MAXCHAR) illegal_character(c_cptr); + } + c = n; + break; + + case 'a': c = 7; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + } + } + cachec(c); + } + FREE(s_line); + + n = cinc; + s = MALLOC(n); + if (s == 0) no_space(); + + for (i = 0; i < n; ++i) + s[i] = cache[i]; + + cinc = 0; + if (n == 1) + cachec('\''); + else + cachec('"'); + + for (i = 0; i < n; ++i) + { + c = ((unsigned char *)s)[i]; + if (c == '\\' || c == cache[0]) + { + cachec('\\'); + cachec(c); + } + else if (isprint(c)) + cachec(c); + else + { + cachec('\\'); + switch (c) + { + case 7: cachec('a'); break; + case '\b': cachec('b'); break; + case '\f': cachec('f'); break; + case '\n': cachec('n'); break; + case '\r': cachec('r'); break; + case '\t': cachec('t'); break; + case '\v': cachec('v'); break; + default: + cachec(((c >> 6) & 7) + '0'); + cachec(((c >> 3) & 7) + '0'); + cachec((c & 7) + '0'); + break; + } + } + } + + if (n == 1) + cachec('\''); + else + cachec('"'); + + cachec(NUL); + bp = lookup(cache); + bp->class = TERM; + if (n == 1 && bp->value == UNDEFINED) + bp->value = *(unsigned char *)s; + FREE(s); + + return (bp); +} + + +static int +is_reserved(char *name) +{ + char *s; + + if (strcmp(name, ".") == 0 || + strcmp(name, "$accept") == 0 || + strcmp(name, "$end") == 0) + return (1); + + if (name[0] == '$' && name[1] == '$' && isdigit(name[2])) + { + s = name + 3; + while (isdigit(*s)) ++s; + if (*s == NUL) return (1); + } + + return (0); +} + + +static bucket * +get_name(void) +{ + int c; + + cinc = 0; + for (c = *cptr; IS_IDENT(c); c = *++cptr) + cachec(c); + cachec(NUL); + + if (is_reserved(cache)) used_reserved(cache); + + return (lookup(cache)); +} + + +static int +get_number(void) +{ + int c; + int n; + + n = 0; + for (c = *cptr; isdigit(c); c = *++cptr) + n = 10*n + (c - '0'); + + return (n); +} + + +static char * +get_tag(void) +{ + int c; + int i; + char *s; + int t_lineno = lineno; + char *t_line = dup_line(); + char *t_cptr = t_line + (cptr - line); + + ++cptr; + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (!isalpha(c) && c != '_' && c != '$') + illegal_tag(t_lineno, t_line, t_cptr); + + cinc = 0; + do { cachec(c); c = *++cptr; } while (IS_IDENT(c)); + cachec(NUL); + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '>') + illegal_tag(t_lineno, t_line, t_cptr); + ++cptr; + + for (i = 0; i < ntags; ++i) + { + if (strcmp(cache, tag_table[i]) == 0) + return (tag_table[i]); + } + + if (ntags >= tagmax) + { + tagmax += 16; + tag_table = (char **) + (tag_table ? REALLOC(tag_table, tagmax*sizeof(char *)) + : MALLOC(tagmax*sizeof(char *))); + if (tag_table == 0) no_space(); + } + + s = MALLOC(cinc); + if (s == 0) no_space(); + strcpy(s, cache); + tag_table[ntags] = s; + ++ntags; + FREE(t_line); + return (s); +} + + +static void +declare_tokens(int assoc) +{ + int c; + bucket *bp; + int value; + char *tag = 0; + + if (assoc != TOKEN) ++prec; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c == '<') + { + tag = get_tag(); + c = nextc(); + if (c == EOF) unexpected_EOF(); + } + + for (;;) + { + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + return; + + if (bp == goal) tokenized_start(bp->name); + bp->class = TERM; + + if (tag) + { + if (bp->tag && tag != bp->tag) + retyped_warning(bp->name); + bp->tag = tag; + } + + if (assoc != TOKEN) + { + if (bp->prec && prec != bp->prec) + reprec_warning(bp->name); + bp->assoc = assoc; + bp->prec = prec; + } + + c = nextc(); + if (c == EOF) unexpected_EOF(); + value = UNDEFINED; + if (isdigit(c)) + { + value = get_number(); + if (bp->value != UNDEFINED && value != bp->value) + revalued_warning(bp->name); + bp->value = value; + c = nextc(); + if (c == EOF) unexpected_EOF(); + } + } +} + + +static void +declare_types(void) +{ + int c; + bucket *bp; + char *tag; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '<') syntax_error(lineno, line, cptr); + tag = get_tag(); + + for (;;) + { + c = nextc(); + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + return; + + if (bp->tag && tag != bp->tag) + retyped_warning(bp->name); + bp->tag = tag; + } +} + + +static void +declare_start(void) +{ + int c; + bucket *bp; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (!isalpha(c) && c != '_' && c != '.' && c != '$') + syntax_error(lineno, line, cptr); + bp = get_name(); + if (bp->class == TERM) + terminal_start(bp->name); + if (goal && goal != bp) + restarted_warning(); + goal = bp; +} + + +static void +read_declarations(void) +{ + int c, k; + + cache_size = 256; + cache = MALLOC(cache_size); + if (cache == 0) no_space(); + + for (;;) + { + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '%') syntax_error(lineno, line, cptr); + switch (k = keyword()) + { + case MARK: + return; + + case IDENT: + copy_ident(); + break; + + case TEXT: + copy_text(); + break; + + case UNION: + copy_union(); + break; + + case TOKEN: + case LEFT: + case RIGHT: + case NONASSOC: + declare_tokens(k); + break; + + case TYPE: + declare_types(); + break; + + case START: + declare_start(); + break; + } + } +} + + +static void +initialize_grammar(void) +{ + nitems = 4; + maxitems = 300; + pitem = (bucket **) MALLOC(maxitems*sizeof(bucket *)); + if (pitem == 0) no_space(); + pitem[0] = 0; + pitem[1] = 0; + pitem[2] = 0; + pitem[3] = 0; + + nrules = 3; + maxrules = 100; + plhs = (bucket **) MALLOC(maxrules*sizeof(bucket *)); + if (plhs == 0) no_space(); + plhs[0] = 0; + plhs[1] = 0; + plhs[2] = 0; + rprec = (short *) MALLOC(maxrules*sizeof(short)); + if (rprec == 0) no_space(); + rprec[0] = 0; + rprec[1] = 0; + rprec[2] = 0; + rassoc = (char *) MALLOC(maxrules*sizeof(char)); + if (rassoc == 0) no_space(); + rassoc[0] = TOKEN; + rassoc[1] = TOKEN; + rassoc[2] = TOKEN; +} + + +static void +expand_items(void) +{ + maxitems += 300; + pitem = (bucket **) REALLOC(pitem, maxitems*sizeof(bucket *)); + if (pitem == 0) no_space(); +} + + +static void +expand_rules(void) +{ + maxrules += 100; + plhs = (bucket **) REALLOC(plhs, maxrules*sizeof(bucket *)); + if (plhs == 0) no_space(); + rprec = (short *) REALLOC(rprec, maxrules*sizeof(short)); + if (rprec == 0) no_space(); + rassoc = (char *) REALLOC(rassoc, maxrules*sizeof(char)); + if (rassoc == 0) no_space(); +} + + +static void +advance_to_start(void) +{ + int c; + bucket *bp; + char *s_cptr; + int s_lineno; + + for (;;) + { + c = nextc(); + if (c != '%') break; + s_cptr = cptr; + switch (keyword()) + { + case MARK: + no_grammar(); + + case TEXT: + copy_text(); + break; + + case START: + declare_start(); + break; + + default: + syntax_error(lineno, line, s_cptr); + } + } + + c = nextc(); + if (!isalpha(c) && c != '_' && c != '.' && c != '_') + syntax_error(lineno, line, cptr); + bp = get_name(); + if (goal == 0) + { + if (bp->class == TERM) + terminal_start(bp->name); + goal = bp; + } + + s_lineno = lineno; + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != ':') syntax_error(lineno, line, cptr); + start_rule(bp, s_lineno); + ++cptr; +} + + +static void +start_rule(bucket *bp, int s_lineno) +{ + if (bp->class == TERM) + terminal_lhs(s_lineno); + bp->class = NONTERM; + if (nrules >= maxrules) + expand_rules(); + plhs[nrules] = bp; + rprec[nrules] = UNDEFINED; + rassoc[nrules] = TOKEN; +} + + +static void +end_rule(void) +{ + int i; + + if (!last_was_action && plhs[nrules]->tag) + { + for (i = nitems - 1; pitem[i]; --i) continue; + if (pitem[i+1] == 0 || pitem[i+1]->tag != plhs[nrules]->tag) + default_action_warning(); + } + + last_was_action = 0; + if (nitems >= maxitems) expand_items(); + pitem[nitems] = 0; + ++nitems; + ++nrules; +} + + +static void +insert_empty_rule(void) +{ + bucket *bp, **bpp; + + assert(cache); + sprintf(cache, "$$%d", ++gensym); + bp = make_bucket(cache); + last_symbol->next = bp; + last_symbol = bp; + bp->tag = plhs[nrules]->tag; + bp->class = NONTERM; + + if ((nitems += 2) > maxitems) + expand_items(); + bpp = pitem + nitems - 1; + *bpp-- = bp; + while ((bpp[0] = bpp[-1])) --bpp; + + if (++nrules >= maxrules) + expand_rules(); + plhs[nrules] = plhs[nrules-1]; + plhs[nrules-1] = bp; + rprec[nrules] = rprec[nrules-1]; + rprec[nrules-1] = 0; + rassoc[nrules] = rassoc[nrules-1]; + rassoc[nrules-1] = TOKEN; +} + + +static void +add_symbol(void) +{ + int c; + bucket *bp; + int s_lineno = lineno; + + c = *cptr; + if (c == '\'' || c == '"') + bp = get_literal(); + else + bp = get_name(); + + c = nextc(); + if (c == ':') + { + end_rule(); + start_rule(bp, s_lineno); + ++cptr; + return; + } + + if (last_was_action) + insert_empty_rule(); + last_was_action = 0; + + if (++nitems > maxitems) + expand_items(); + pitem[nitems-1] = bp; +} + + +static void +copy_action(void) +{ + int c; + int i, n; + int depth; + int quote; + char *tag; + FILE *f = action_file; + int a_lineno = lineno; + char *a_line = dup_line(); + char *a_cptr = a_line + (cptr - line); + + if (last_was_action) + insert_empty_rule(); + last_was_action = 1; + + fprintf(f, "case %d:\n", nrules - 2); + if (!lflag) + fprintf(f, line_format, lineno, input_file_name); + if (*cptr == '=') ++cptr; + + n = 0; + for (i = nitems - 1; pitem[i]; --i) ++n; + + depth = 0; +loop: + c = *cptr; + if (c == '$') + { + if (cptr[1] == '<') + { + int d_lineno = lineno; + char *d_line = dup_line(); + char *d_cptr = d_line + (cptr - line); + + ++cptr; + tag = get_tag(); + c = *cptr; + if (c == '$') + { + fprintf(f, "yyval.%s", tag); + ++cptr; + FREE(d_line); + goto loop; + } + else if (isdigit(c)) + { + i = get_number(); + if (i > n) dollar_warning(d_lineno, i); + fprintf(f, "yyvsp[%d].%s", i - n, tag); + FREE(d_line); + goto loop; + } + else if (c == '-' && isdigit(cptr[1])) + { + ++cptr; + i = -get_number() - n; + fprintf(f, "yyvsp[%d].%s", i, tag); + FREE(d_line); + goto loop; + } + else + dollar_error(d_lineno, d_line, d_cptr); + } + else if (cptr[1] == '$') + { + if (ntags) + { + tag = plhs[nrules]->tag; + if (tag == 0) untyped_lhs(); + fprintf(f, "yyval.%s", tag); + } + else + fprintf(f, "yyval"); + cptr += 2; + goto loop; + } + else if (isdigit(cptr[1])) + { + ++cptr; + i = get_number(); + if (ntags) + { + if (i <= 0 || i > n) + unknown_rhs(i); + tag = pitem[nitems + i - n - 1]->tag; + if (tag == 0) untyped_rhs(i, pitem[nitems + i - n - 1]->name); + fprintf(f, "yyvsp[%d].%s", i - n, tag); + } + else + { + if (i > n) + dollar_warning(lineno, i); + fprintf(f, "yyvsp[%d]", i - n); + } + goto loop; + } + else if (cptr[1] == '-') + { + cptr += 2; + i = get_number(); + if (ntags) + unknown_rhs(-i); + fprintf(f, "yyvsp[%d]", -i - n); + goto loop; + } + } + if (isalpha(c) || c == '_' || c == '$') + { + do + { + putc(c, f); + c = *++cptr; + } while (isalnum(c) || c == '_' || c == '$'); + goto loop; + } + putc(c, f); + ++cptr; + switch (c) + { + case '\n': + next_line: + get_line(); + if (line) goto loop; + unterminated_action(a_lineno, a_line, a_cptr); + + case ';': + if (depth > 0) goto loop; + fprintf(f, "\nbreak;\n"); + FREE(a_line); + return; + + case '{': + ++depth; + goto loop; + + case '}': + if (--depth > 0) goto loop; + fprintf(f, "\nbreak;\n"); + FREE(a_line); + return; + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == quote) + { + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, f); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + c = *cptr; + if (c == '/') + { + putc('*', f); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + fprintf(f, "* "); + else + putc(c, f); + } + fprintf(f, "*/\n"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', f); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == '*' && *cptr == '/') + { + putc('/', f); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + goto loop; + + default: + goto loop; + } +} + + +static int +mark_symbol(void) +{ + int c; + bucket *bp; + + c = cptr[1]; + if (c == '%' || c == '\\') + { + cptr += 2; + return (1); + } + + if (c == '=') + cptr += 2; + else if ((c == 'p' || c == 'P') && + ((c = cptr[2]) == 'r' || c == 'R') && + ((c = cptr[3]) == 'e' || c == 'E') && + ((c = cptr[4]) == 'c' || c == 'C') && + ((c = cptr[5], !IS_IDENT(c)))) + cptr += 5; + else + syntax_error(lineno, line, cptr); + + c = nextc(); + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + { + syntax_error(lineno, line, cptr); + /*NOTREACHED*/ + } + + if (rprec[nrules] != UNDEFINED && bp->prec != rprec[nrules]) + prec_redeclared(); + + rprec[nrules] = bp->prec; + rassoc[nrules] = bp->assoc; + return (0); +} + + +static void +read_grammar(void) +{ + int c; + + initialize_grammar(); + advance_to_start(); + + for (;;) + { + c = nextc(); + if (c == EOF) break; + if (isalpha(c) || c == '_' || c == '.' || c == '$' || c == '\'' || + c == '"') + add_symbol(); + else if (c == '{' || c == '=') + copy_action(); + else if (c == '|') + { + end_rule(); + start_rule(plhs[nrules-1], 0); + ++cptr; + } + else if (c == '%') + { + if (mark_symbol()) break; + } + else + syntax_error(lineno, line, cptr); + } + end_rule(); +} + + +static void +free_tags(void) +{ + int i; + + if (tag_table == 0) return; + + for (i = 0; i < ntags; ++i) + { + assert(tag_table[i]); + FREE(tag_table[i]); + } + FREE(tag_table); +} + + +static void +pack_names(void) +{ + bucket *bp; + char *p, *s, *t; + + name_pool_size = 13; /* 13 == sizeof("$end") + sizeof("$accept") */ + for (bp = first_symbol; bp; bp = bp->next) + name_pool_size += strlen(bp->name) + 1; + name_pool = MALLOC(name_pool_size); + if (name_pool == 0) no_space(); + + strcpy(name_pool, "$accept"); + strcpy(name_pool+8, "$end"); + t = name_pool + 13; + for (bp = first_symbol; bp; bp = bp->next) + { + p = t; + s = bp->name; + while ((*t++ = *s++)) continue; + FREE(bp->name); + bp->name = p; + } +} + + +static void +check_symbols(void) +{ + bucket *bp; + + if (goal->class == UNKNOWN) + undefined_goal(goal->name); + + for (bp = first_symbol; bp; bp = bp->next) + { + if (bp->class == UNKNOWN) + { + undefined_symbol_warning(bp->name); + bp->class = TERM; + } + } +} + + +static void +pack_symbols(void) +{ + bucket *bp; + bucket **v; + int i, j, k, n; + + nsyms = 2; + ntokens = 1; + for (bp = first_symbol; bp; bp = bp->next) + { + ++nsyms; + if (bp->class == TERM) ++ntokens; + } + start_symbol = ntokens; + nvars = nsyms - ntokens; + + symbol_name = (char **) MALLOC(nsyms*sizeof(char *)); + if (symbol_name == 0) no_space(); + symbol_value = (short *) MALLOC(nsyms*sizeof(short)); + if (symbol_value == 0) no_space(); + symbol_prec = (short *) MALLOC(nsyms*sizeof(short)); + if (symbol_prec == 0) no_space(); + symbol_assoc = MALLOC(nsyms); + if (symbol_assoc == 0) no_space(); + + v = (bucket **) MALLOC(nsyms*sizeof(bucket *)); + if (v == 0) no_space(); + + v[0] = 0; + v[start_symbol] = 0; + + i = 1; + j = start_symbol + 1; + for (bp = first_symbol; bp; bp = bp->next) + { + if (bp->class == TERM) + v[i++] = bp; + else + v[j++] = bp; + } + assert(i == ntokens && j == nsyms); + + for (i = 1; i < ntokens; ++i) + v[i]->index = i; + + goal->index = start_symbol + 1; + k = start_symbol + 2; + while (++i < nsyms) + if (v[i] != goal) + { + v[i]->index = k; + ++k; + } + + goal->value = 0; + k = 1; + for (i = start_symbol + 1; i < nsyms; ++i) + { + if (v[i] != goal) + { + v[i]->value = k; + ++k; + } + } + + k = 0; + for (i = 1; i < ntokens; ++i) + { + n = v[i]->value; + if (n > 256) + { + for (j = k++; j > 0 && symbol_value[j-1] > n; --j) + symbol_value[j] = symbol_value[j-1]; + symbol_value[j] = n; + } + } + + if (v[1]->value == UNDEFINED) + v[1]->value = 256; + + j = 0; + n = 257; + for (i = 2; i < ntokens; ++i) + { + if (v[i]->value == UNDEFINED) + { + while (j < k && n == symbol_value[j]) + { + while (++j < k && n == symbol_value[j]) continue; + ++n; + } + v[i]->value = n; + ++n; + } + } + + symbol_name[0] = name_pool + 8; + symbol_value[0] = 0; + symbol_prec[0] = 0; + symbol_assoc[0] = TOKEN; + for (i = 1; i < ntokens; ++i) + { + symbol_name[i] = v[i]->name; + symbol_value[i] = v[i]->value; + symbol_prec[i] = v[i]->prec; + symbol_assoc[i] = v[i]->assoc; + } + symbol_name[start_symbol] = name_pool; + symbol_value[start_symbol] = -1; + symbol_prec[start_symbol] = 0; + symbol_assoc[start_symbol] = TOKEN; + for (++i; i < nsyms; ++i) + { + k = v[i]->index; + symbol_name[k] = v[i]->name; + symbol_value[k] = v[i]->value; + symbol_prec[k] = v[i]->prec; + symbol_assoc[k] = v[i]->assoc; + } + + FREE(v); +} + + +static void +pack_grammar(void) +{ + int i, j; + int assoc, prec; + + ritem = (short *) MALLOC(nitems*sizeof(short)); + if (ritem == 0) no_space(); + rlhs = (short *) MALLOC(nrules*sizeof(short)); + if (rlhs == 0) no_space(); + rrhs = (short *) MALLOC((nrules+1)*sizeof(short)); + if (rrhs == 0) no_space(); + rprec = (short *) REALLOC(rprec, nrules*sizeof(short)); + if (rprec == 0) no_space(); + rassoc = REALLOC(rassoc, nrules); + if (rassoc == 0) no_space(); + + ritem[0] = -1; + ritem[1] = goal->index; + ritem[2] = 0; + ritem[3] = -2; + rlhs[0] = 0; + rlhs[1] = 0; + rlhs[2] = start_symbol; + rrhs[0] = 0; + rrhs[1] = 0; + rrhs[2] = 1; + + j = 4; + for (i = 3; i < nrules; ++i) + { + rlhs[i] = plhs[i]->index; + rrhs[i] = j; + assoc = TOKEN; + prec = 0; + while (pitem[j]) + { + ritem[j] = pitem[j]->index; + if (pitem[j]->class == TERM) + { + prec = pitem[j]->prec; + assoc = pitem[j]->assoc; + } + ++j; + } + ritem[j] = -i; + ++j; + if (rprec[i] == UNDEFINED) + { + rprec[i] = prec; + rassoc[i] = assoc; + } + } + rrhs[i] = j; + + FREE(plhs); + FREE(pitem); +} + + +static void +print_grammar(void) +{ + int i, j, k; + int spacing = 0; + FILE *f = verbose_file; + + if (!vflag) return; + + k = 1; + for (i = 2; i < nrules; ++i) + { + if (rlhs[i] != rlhs[i-1]) + { + if (i != 2) fprintf(f, "\n"); + fprintf(f, "%4d %s :", i - 2, symbol_name[rlhs[i]]); + spacing = strlen(symbol_name[rlhs[i]]) + 1; + } + else + { + fprintf(f, "%4d ", i - 2); + j = spacing; + while (--j >= 0) putc(' ', f); + putc('|', f); + } + + while (ritem[k] >= 0) + { + fprintf(f, " %s", symbol_name[ritem[k]]); + ++k; + } + ++k; + putc('\n', f); + } +} + + +void +reader(void) +{ + write_section(banner); + create_symbol_table(); + read_declarations(); + read_grammar(); + free_symbol_table(); + free_tags(); + pack_names(); + check_symbols(); + pack_symbols(); + pack_grammar(); + free_symbols(); + print_grammar(); +} diff --git a/src/toolsComm/antelope/skeleton.c b/src/toolsComm/antelope/skeleton.c new file mode 100644 index 000000000..ab683c666 --- /dev/null +++ b/src/toolsComm/antelope/skeleton.c @@ -0,0 +1,302 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +/* If the skeleton is changed, the banner should be changed so that */ +/* the altered version can be easily distinguished from the original. */ +/* */ +/* The #defines included with the banner are there because they are */ +/* useful in subsequent code. The macros #defined in the header or */ +/* the body either are not useful outside of semantic actions or */ +/* are conditional. */ + +char *banner[] = +{ + "#define YYBYACC 1", + "#define YYMAJOR 1", + "#define YYMINOR 9", + "#define yyclearin (yychar=(-1))", + "#define yyerrok (yyerrflag=0)", + "#define YYRECOVERING (yyerrflag!=0)", + "static int yyparse(void);",/* JRW */ + 0 +}; + + +char *tables[] = +{ + "static short yylhs[];", /* JRW */ + "static short yylen[];", /* JRW */ + "static short yydefred[];", /* JRW */ + "static short yydgoto[];", /* JRW */ + "static short yysindex[];", /* JRW */ + "static short yyrindex[];", /* JRW */ + "static short yygindex[];", /* JRW */ + "static short yytable[];", /* JRW */ + "static short yycheck[];", /* JRW */ + "#if YYDEBUG", + "static char *yyname[];", /* JRW */ + "static char *yyrule[];", /* JRW */ + "#endif", + 0 +}; + + +char *header[] = +{ + "#ifdef YYSTACKSIZE", + "#undef YYMAXDEPTH", + "#define YYMAXDEPTH YYSTACKSIZE", + "#else", + "#ifdef YYMAXDEPTH", + "#define YYSTACKSIZE YYMAXDEPTH", + "#else", + "#define YYSTACKSIZE 500", + "#define YYMAXDEPTH 500", + "#endif", + "#endif", + "#if YYDEBUG", /* MRK */ + "static int yydebug;", /* JRW */ + "#endif", /* MRK */ + "static int yynerrs;", /* JRW */ + "static int yyerrflag;", /* JRW */ + "static int yychar;", /* JRW */ + "static short *yyssp;", /* JRW */ + "static YYSTYPE *yyvsp;", /* JRW */ + "static YYSTYPE yyval;", /* JRW */ + "static YYSTYPE yylval;", /* JRW */ + "static short yyss[YYSTACKSIZE];", /* JRW */ + "static YYSTYPE yyvs[YYSTACKSIZE];", /* JRW */ + "#define yystacksize YYSTACKSIZE", + 0 +}; + + +char *body[] = +{ + "#define YYABORT goto yyabort", + "#define YYREJECT goto yyabort", + "#define YYACCEPT goto yyaccept", + "#define YYERROR goto yyerrlab", + "static int", /* JRW */ + "yyparse(void)", /* JRW */ + "{", + " int yym, yyn, yystate;", + "#if YYDEBUG", + " char *yys;", + " extern char *getenv();", + "", + " if ((yys = getenv(\"YYDEBUG\")))", + " {", + " yyn = *yys;", + " if (yyn >= '0' && yyn <= '9')", + " yydebug = yyn - '0';", + " }", + "#endif", + "", + " yynerrs = 0;", + " yyerrflag = 0;", + " yychar = (-1);", + "", + " yyssp = yyss;", + " yyvsp = yyvs;", + " *yyssp = yystate = 0;", + "", + "yyloop:", + " if ((yyn = yydefred[yystate])) goto yyreduce;", + " if (yychar < 0)", + " {", + " if ((yychar = yylex()) < 0) yychar = 0;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, yystate, yychar, yys);", + " }", + "#endif", + " }", + " if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, shifting to state %d\\n\",", + " YYPREFIX, yystate, yytable[yyn]);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate = yytable[yyn];", + " *++yyvsp = yylval;", + " yychar = (-1);", + " if (yyerrflag > 0) --yyerrflag;", + " goto yyloop;", + " }", + " if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", + " {", + " yyn = yytable[yyn];", + " goto yyreduce;", + " }", + " if (yyerrflag) goto yyinrecovery;", + " yyerror(\"syntax error\");", + " ++yynerrs;", + "yyinrecovery:", + " if (yyerrflag < 3)", + " {", + " yyerrflag = 3;", + " for (;;)", + " {", + " if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, error recovery shifting\\", + " to state %d\\n\", YYPREFIX, *yyssp, yytable[yyn]);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate = yytable[yyn];", + " *++yyvsp = yylval;", + " goto yyloop;", + " }", + " else", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: error recovery discarding state %d\ +\\n\",", + " YYPREFIX, *yyssp);", + "#endif", + " if (yyssp <= yyss) goto yyabort;", + " --yyssp;", + " --yyvsp;", + " }", + " }", + " }", + " else", + " {", + " if (yychar == 0) goto yyabort;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, error recovery discards token %d\ + (%s)\\n\",", + " YYPREFIX, yystate, yychar, yys);", + " }", + "#endif", + " yychar = (-1);", + " goto yyloop;", + " }", + "yyreduce:", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, reducing by rule %d (%s)\\n\",", + " YYPREFIX, yystate, yyn, yyrule[yyn]);", + "#endif", + " yym = yylen[yyn];", + " yyval = yyvsp[1-yym];", + " switch (yyn)", + " {", + 0 +}; + + +char *trailer[] = +{ + " }", + " yyssp -= yym;", + " yystate = *yyssp;", + " yyvsp -= yym;", + " yym = yylhs[yyn];", + " if (yystate == 0 && yym == 0)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: after reduction, shifting from state 0 to\\", + " state %d\\n\", YYPREFIX, YYFINAL);", + "#endif", + " yystate = YYFINAL;", + " *++yyssp = YYFINAL;", + " *++yyvsp = yyval;", + " if (yychar < 0)", + " {", + " if ((yychar = yylex()) < 0) yychar = 0;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, YYFINAL, yychar, yys);", + " }", + "#endif", + " }", + " if (yychar == 0) goto yyaccept;", + " goto yyloop;", + " }", + " if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yystate)", + " yystate = yytable[yyn];", + " else", + " yystate = yydgoto[yym];", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: after reduction, shifting from state %d \\", + "to state %d\\n\", YYPREFIX, *yyssp, yystate);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate;", + " *++yyvsp = yyval;", + " goto yyloop;", + "yyoverflow:", + " yyerror(\"yacc stack overflow\");", + "yyabort:", + " return (1);", + "yyaccept:", + " return (0);", + "}", + 0 +}; + + +void +write_section(char *section[]) +{ + int c; + int i; + char *s; + FILE *f; + + f = code_file; + for (i = 0; (s = section[i]); ++i) + { + ++outline; + while ((c = *s)) + { + putc(c, f); + ++s; + } + putc('\n', f); + } +} diff --git a/src/toolsComm/antelope/symtab.c b/src/toolsComm/antelope/symtab.c new file mode 100644 index 000000000..486835a21 --- /dev/null +++ b/src/toolsComm/antelope/symtab.c @@ -0,0 +1,126 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + + +/* TABLE_SIZE is the number of entries in the symbol table. */ +/* TABLE_SIZE must be a power of two. */ + +#define TABLE_SIZE 1024 + + +bucket **symbol_table; +bucket *first_symbol; +bucket *last_symbol; + + +static int +hash(char *name) +{ + char *s; + int c, k; + + assert(name && *name); + s = name; + k = *s; + while ((c = *++s)) + k = (31*k + c) & (TABLE_SIZE - 1); + + return (k); +} + + +bucket * +make_bucket(char *name) +{ + bucket *bp; + + assert(name); + bp = (bucket *) MALLOC(sizeof(bucket)); + if (bp == 0) no_space(); + bp->link = 0; + bp->next = 0; + bp->name = MALLOC(strlen(name) + 1); + if (bp->name == 0) no_space(); + bp->tag = 0; + bp->value = UNDEFINED; + bp->index = 0; + bp->prec = 0; + bp-> class = UNKNOWN; + bp->assoc = TOKEN; + + if (bp->name == 0) no_space(); + strcpy(bp->name, name); + + return (bp); +} + + +bucket * +lookup(char *name) +{ + bucket *bp, **bpp; + + bpp = symbol_table + hash(name); + bp = *bpp; + + while (bp) + { + if (strcmp(name, bp->name) == 0) return (bp); + bpp = &bp->link; + bp = *bpp; + } + + *bpp = bp = make_bucket(name); + last_symbol->next = bp; + last_symbol = bp; + + return (bp); +} + +void +create_symbol_table(void) +{ + int i; + bucket *bp; + + symbol_table = (bucket **) MALLOC(TABLE_SIZE*sizeof(bucket *)); + if (symbol_table == 0) no_space(); + for (i = 0; i < TABLE_SIZE; i++) + symbol_table[i] = 0; + + bp = make_bucket("error"); + bp->index = 1; + bp->class = TERM; + + first_symbol = bp; + last_symbol = bp; + symbol_table[hash("error")] = bp; +} + + +void +free_symbol_table(void) +{ + FREE(symbol_table); + symbol_table = 0; +} + + +void +free_symbols(void) +{ + bucket *p, *q; + + for (p = first_symbol; p; p = q) + { + q = p->next; + FREE(p); + } +} diff --git a/src/toolsComm/antelope/verbose.c b/src/toolsComm/antelope/verbose.c new file mode 100644 index 000000000..eb03cf386 --- /dev/null +++ b/src/toolsComm/antelope/verbose.c @@ -0,0 +1,349 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "defs.h" + +static short *null_rules; + +static void log_unused(void); +static void log_conflicts(void); +static void print_state(int state); +static void print_conflicts(int state); +static void print_core(int state); +static void print_nulls(int state); +static void print_actions(int state); +static void print_shifts(action *p); +static void print_reductions(action *p, int defred); +static void print_gotos(int stateno); + + +void +verbose(void) +{ + int i; + + if (!vflag) return; + + null_rules = (short *) MALLOC(nrules*sizeof(short)); + if (null_rules == 0) no_space(); + fprintf(verbose_file, "\f\n"); + for (i = 0; i < nstates; i++) + print_state(i); + FREE(null_rules); + + if (nunused) + log_unused(); + if (SRtotal || RRtotal) + log_conflicts(); + + fprintf(verbose_file, "\n\n%d terminals, %d nonterminals\n", ntokens, nvars); + fprintf(verbose_file, "%d grammar rules, %d states\n", nrules - 2, nstates); +} + + +static void +log_unused(void) +{ + int i; + short *p; + + fprintf(verbose_file, "\n\nRules never reduced:\n"); + for (i = 3; i < nrules; ++i) + { + if (!rules_used[i]) + { + fprintf(verbose_file, "\t%s :", symbol_name[rlhs[i]]); + for (p = ritem + rrhs[i]; *p >= 0; ++p) + fprintf(verbose_file, " %s", symbol_name[*p]); + fprintf(verbose_file, " (%d)\n", i - 2); + } + } +} + + +static void +log_conflicts(void) +{ + int i; + + fprintf(verbose_file, "\n\n"); + for (i = 0; i < nstates; i++) + { + if (SRconflicts[i] || RRconflicts[i]) + { + fprintf(verbose_file, "State %d contains ", i); + if (SRconflicts[i] == 1) + fprintf(verbose_file, "1 shift/reduce conflict"); + else if (SRconflicts[i] > 1) + fprintf(verbose_file, "%d shift/reduce conflicts", + SRconflicts[i]); + if (SRconflicts[i] && RRconflicts[i]) + fprintf(verbose_file, ", "); + if (RRconflicts[i] == 1) + fprintf(verbose_file, "1 reduce/reduce conflict"); + else if (RRconflicts[i] > 1) + fprintf(verbose_file, "%d reduce/reduce conflicts", + RRconflicts[i]); + fprintf(verbose_file, ".\n"); + } + } +} + + +static void +print_state(int state) +{ + if (state) + fprintf(verbose_file, "\n\n"); + if (SRconflicts[state] || RRconflicts[state]) + print_conflicts(state); + fprintf(verbose_file, "state %d\n", state); + print_core(state); + print_nulls(state); + print_actions(state); +} + + +static void +print_conflicts(int state) +{ + int symbol, act = 0, number = 0; + action *p; + + symbol = -1; + for (p = parser[state]; p; p = p->next) + { + if (p->suppressed == 2) + continue; + + if (p->symbol != symbol) + { + symbol = p->symbol; + number = p->number; + if (p->action_code == SHIFT) + act = SHIFT; + else + act = REDUCE; + } + else if (p->suppressed == 1) + { + if (state == final_state && symbol == 0) + { + fprintf(verbose_file, "%d: shift/reduce conflict \ +(accept, reduce %d) on $end\n", state, p->number - 2); + } + else + { + if (act == SHIFT) + { + fprintf(verbose_file, "%d: shift/reduce conflict \ +(shift %d, reduce %d) on %s\n", state, number, p->number - 2, + symbol_name[symbol]); + } + else + { + fprintf(verbose_file, "%d: reduce/reduce conflict \ +(reduce %d, reduce %d) on %s\n", state, number - 2, p->number - 2, + symbol_name[symbol]); + } + } + } + } +} + + +static void +print_core(int state) +{ + int i; + int k; + int rule; + core *statep; + short *sp; + short *sp1; + + statep = state_table[state]; + k = statep->nitems; + + for (i = 0; i < k; i++) + { + sp1 = sp = ritem + statep->items[i]; + + while (*sp >= 0) ++sp; + rule = -(*sp); + fprintf(verbose_file, "\t%s : ", symbol_name[rlhs[rule]]); + + for (sp = ritem + rrhs[rule]; sp < sp1; sp++) + fprintf(verbose_file, "%s ", symbol_name[*sp]); + + putc('.', verbose_file); + + while (*sp >= 0) + { + fprintf(verbose_file, " %s", symbol_name[*sp]); + sp++; + } + fprintf(verbose_file, " (%d)\n", -2 - *sp); + } +} + + +static void +print_nulls(int state) +{ + action *p; + int i, j, k, nnulls; + + nnulls = 0; + for (p = parser[state]; p; p = p->next) + { + if (p->action_code == REDUCE && + (p->suppressed == 0 || p->suppressed == 1)) + { + i = p->number; + if (rrhs[i] + 1 == rrhs[i+1]) + { + for (j = 0; j < nnulls && i > null_rules[j]; ++j) + continue; + + if (j == nnulls) + { + ++nnulls; + null_rules[j] = i; + } + else if (i != null_rules[j]) + { + ++nnulls; + for (k = nnulls - 1; k > j; --k) + null_rules[k] = null_rules[k-1]; + null_rules[j] = i; + } + } + } + } + + for (i = 0; i < nnulls; ++i) + { + j = null_rules[i]; + fprintf(verbose_file, "\t%s : . (%d)\n", symbol_name[rlhs[j]], + j - 2); + } + fprintf(verbose_file, "\n"); +} + + +static void +print_actions(int stateno) +{ + action *p; + shifts *sp; + int as; + + if (stateno == final_state) + fprintf(verbose_file, "\t$end accept\n"); + + p = parser[stateno]; + if (p) + { + print_shifts(p); + print_reductions(p, defred[stateno]); + } + + sp = shift_table[stateno]; + if (sp && sp->nshifts > 0) + { + as = accessing_symbol[sp->shift[sp->nshifts - 1]]; + if (ISVAR(as)) + print_gotos(stateno); + } +} + + +static void +print_shifts(action *p) +{ + int count; + action *q; + + count = 0; + for (q = p; q; q = q->next) + { + if (q->suppressed < 2 && q->action_code == SHIFT) + ++count; + } + + if (count > 0) + { + for (; p; p = p->next) + { + if (p->action_code == SHIFT && p->suppressed == 0) + fprintf(verbose_file, "\t%s shift %d\n", + symbol_name[p->symbol], p->number); + } + } +} + + +static void +print_reductions(action *p, int defred) +{ + int k, anyreds; + action *q; + + anyreds = 0; + for (q = p; q ; q = q->next) + { + if (q->action_code == REDUCE && q->suppressed < 2) + { + anyreds = 1; + break; + } + } + + if (anyreds == 0) + fprintf(verbose_file, "\t. error\n"); + else + { + for (; p; p = p->next) + { + if (p->action_code == REDUCE && p->number != defred) + { + k = p->number - 2; + if (p->suppressed == 0) + fprintf(verbose_file, "\t%s reduce %d\n", + symbol_name[p->symbol], k); + } + } + + if (defred > 0) + fprintf(verbose_file, "\t. reduce %d\n", defred - 2); + } +} + + +static void +print_gotos(int stateno) +{ + int i, k; + int as; + short *to_state; + shifts *sp; + + putc('\n', verbose_file); + sp = shift_table[stateno]; + to_state = sp->shift; + for (i = 0; i < sp->nshifts; ++i) + { + k = to_state[i]; + as = accessing_symbol[k]; + if (ISVAR(as)) + fprintf(verbose_file, "\t%s goto %d\n", symbol_name[as], k); + } +} + diff --git a/src/toolsComm/antelope/warshall.c b/src/toolsComm/antelope/warshall.c new file mode 100644 index 000000000..4477074c5 --- /dev/null +++ b/src/toolsComm/antelope/warshall.c @@ -0,0 +1,88 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +#include "defs.h" + +static void +transitive_closure(unsigned int *R, int n) +{ + int rowsize; + unsigned i; + unsigned *rowj; + unsigned *rp; + unsigned *rend; + unsigned *ccol; + unsigned *relend; + unsigned *cword; + unsigned *rowi; + + rowsize = WORDSIZE(n); + relend = R + n*rowsize; + + cword = R; + i = 0; + rowi = R; + while (rowi < relend) + { + ccol = cword; + rowj = R; + + while (rowj < relend) + { + if (*ccol & (1 << i)) + { + rp = rowi; + rend = rowj + rowsize; + while (rowj < rend) + *rowj++ |= *rp++; + } + else + { + rowj += rowsize; + } + + ccol += rowsize; + } + + if (++i >= BITS_PER_WORD) + { + i = 0; + cword++; + } + + rowi += rowsize; + } +} + +void +reflexive_transitive_closure(unsigned int *R, int n) +{ + int rowsize; + unsigned i; + unsigned *rp; + unsigned *relend; + + transitive_closure(R, n); + + rowsize = WORDSIZE(n); + relend = R + n*rowsize; + + i = 0; + rp = R; + while (rp < relend) + { + *rp |= (1 << i); + if (++i >= BITS_PER_WORD) + { + i = 0; + rp++; + } + + rp += rowsize; + } +} diff --git a/src/toolsComm/antelope/yacc.html b/src/toolsComm/antelope/yacc.html new file mode 100644 index 000000000..1258b439f --- /dev/null +++ b/src/toolsComm/antelope/yacc.html @@ -0,0 +1,135 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +
    +
    +
    +
    +

    NAME

    +     Yacc - an LALR(1) parser generator
    +
    +
    +
    +

    SYNOPSIS

    +     yacc [ -dlrtv ] [ -b file_prefix  ]  [  -p  symbol_prefix  ]
    +     filename
    +
    +
    +
    +

    DESCRIPTION

    +     Yacc reads the grammar specification in  the  file  filename
    +     and  generates  an LR(1) parser for it.  The parsers consist
    +     of a set of LALR(1) parsing  tables  and  a  driver  routine
    +     written in the C programming language.  Yacc normally writes
    +     the parse tables and the driver routine to the file y.tab.c.
    +
    +     The following options are available:
    +
    +          -b file_prefix
    +               The -b option changes the prefix prepended to  the
    +               output   file  names  to  the  string  denoted  by
    +               file_prefix.  The default prefix is the  character
    +               y.
    +
    +          -d   The -d option causes the header file y.tab.h to be
    +               written.
    +
    +          -l   If the -l  option  is  not  specified,  yacc  will
    +               insert  #line  directives  in  the generated code.
    +               The #line directives let  the  C  compiler  relate
    +               errors  in the generated code to the user's origi-
    +               nal code.  If the -l  option  is  specified,  yacc
    +               will  not  insert  the  #line  directives.   #line
    +               directives specified by the user will be retained.
    +
    +          -p symbol_prefix
    +               The -p option  changes  the  prefix  prepended  to
    +               yacc-generated  symbols  to  the string denoted by
    +               symbol_prefix.  The default prefix is  the  string
    +               yy.
    +
    +          -r   The -r option  causes  yacc  to  produce  separate
    +               files for code and tables.  The code file is named
    +               y.code.c, and the tables file is named y.tab.c.
    +
    +          -t   The -t option changes the preprocessor  directives
    +               generated  by  yacc  so  that debugging statements
    +               will be incorporated in the compiled code.
    +
    +          -v   The -v option causes a human-readable  description
    +               of  the generated parser to be written to the file
    +               y.output.
    +
    +
    +     If the  environment  variable  TMPDIR  is  set,  the  string
    +     denoted  by TMPDIR will be used as the name of the directory
    +     where the temporary files are created.
    +
    +
    +
    +

    FILES

    +     y.code.c
    +     y.tab.c
    +     y.tab.h
    +     y.output
    +     /tmp/yacc.aXXXXXX
    +     /tmp/yacc.tXXXXXX
    +     /tmp/yacc.uXXXXXX
    +
    +
    +
    +

    DIAGNOSTICS

    +     If there are rules that are never  reduced,  the  number  of
    +     such  rules is reported on standard error.  If there are any
    +     LALR(1) conflicts, the number of conflicts  is  reported  on
    +     standard error.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +Man(1) output converted with +man2html +
    + + diff --git a/src/toolsComm/flex/COPYING b/src/toolsComm/flex/COPYING new file mode 100644 index 000000000..9b01361ca --- /dev/null +++ b/src/toolsComm/flex/COPYING @@ -0,0 +1,38 @@ +Flex carries the copyright used for BSD software, slightly modified +because it originated at the Lawrence Berkeley (not Livermore!) Laboratory, +which operates under a contract with the Department of Energy: + + Copyright (c) 1990 The Regents of the University of California. + All rights reserved. + + This code is derived from software contributed to Berkeley by + Vern Paxson. + + The United States Government has rights in this work pursuant + to contract no. DE-AC03-76SF00098 between the United States + Department of Energy and the University of California. + + Redistribution and use in source and binary forms are permitted + provided that: (1) source distributions retain this entire + copyright notice and comment, and (2) distributions including + binaries display the following acknowledgement: ``This product + includes software developed by the University of California, + Berkeley and its contributors'' in the documentation or other + materials provided with the distribution and in all advertising + materials mentioning features or use of this software. Neither the + name of the University nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. + +This basically says "do whatever you please with this software except +remove this notice or take advantage of the University's (or the flex +authors') name". + +Note that the "flex.skel" scanner skeleton carries no copyright notice. +You are free to do whatever you please with scanners generated using flex; +for them, you are not even bound by the above copyright. diff --git a/src/toolsComm/flex/Changes b/src/toolsComm/flex/Changes new file mode 100644 index 000000000..0111a1f21 --- /dev/null +++ b/src/toolsComm/flex/Changes @@ -0,0 +1,345 @@ +Changes between 2.3 Patch #7 (28Mar91) and 2.3 Patch #6: + + - Fixed out-of-bounds array access that caused bad tables + to be produced on machines where the bad reference happened + to yield a 1. This caused problems installing or running + flex on some Suns, in particular. + + +Changes between 2.3 Patch #6 (29Aug90) and 2.3 Patch #5: + + - Fixed a serious bug in yymore() which basically made it + completely broken. Thanks goes to Jean Christophe of + the Nethack development team for finding the problem + and passing along the fix. + + +Changes between 2.3 Patch #5 (16Aug90) and 2.3 Patch #4: + + - An up-to-date version of initscan.c so "make test" will + work after applying the previous patches + + +Changes between 2.3 Patch #4 (14Aug90) and 2.3 Patch #3: + + - Fixed bug in hexadecimal escapes which allowed only digits, + not letters, in escapes + - Fixed bug in previous "Changes" file! + + +Changes between 2.3 Patch #3 (03Aug90) and 2.3 Patch #2: + + - Correction to patch #2 for gcc compilation; thanks goes to + Paul Eggert for catching this. + + +Changes between 2.3 Patch #2 (02Aug90) and original 2.3 release: + + - Fixed (hopefully) headaches involving declaring malloc() + and free() for gcc, which defines __STDC__ but (often) doesn't + come with the standard include files such as . + Reordered #ifdef maze in the scanner skeleton in the hope of + getting the declarations right for cfront and g++, too. + + - Note that this patch supercedes patch #1 for release 2.3, + which was never announced but was available briefly for + anonymous ftp. + + +Changes between 2.3 (full) release of 28Jun90 and 2.2 (alpha) release: + + User-visible: + + - A lone <> rule (that is, one which is not qualified with + a list of start conditions) now specifies the EOF action for + *all* start conditions which haven't already had <> actions + given. To specify an end-of-file action for just the initial + state, use <>. + + - -d debug output is now contigent on the global yy_flex_debug + being set to a non-zero value, which it is by default. + + - A new macro, YY_USER_INIT, is provided for the user to specify + initialization action to be taken on the first call to the + scanner. This action is done before the scanner does its + own initialization. + + - yy_new_buffer() has been added as an alias for yy_create_buffer() + + - Comments beginning with '#' and extending to the end of the line + now work, but have been deprecated (in anticipation of making + flex recognize #line directives). + + - The funky restrictions on when semi-colons could follow the + YY_NEW_FILE and yyless macros have been removed. They now + behave identically to functions. + + - A bug in the sample redefinition of YY_INPUT in the documentation + has been corrected. + + - A bug in the sample simple tokener in the documentation has + been corrected. + + - The documentation on the incompatibilities between flex and + lex has been reordered so that the discussion of yylineno + and input() come first, as it's anticipated that these will + be the most common source of headaches. + + + Things which didn't used to be documented but now are: + + - flex interprets "^foo|bar" differently from lex. flex interprets + it as "match either a 'foo' or a 'bar', providing it comes at the + beginning of a line", whereas lex interprets it as "match either + a 'foo' at the beginning of a line, or a 'bar' anywhere". + + - flex initializes the global "yyin" on the first call to the + scanner, while lex initializes it at compile-time. + + - yy_switch_to_buffer() can be used in the yywrap() macro/routine. + + - flex scanners do not use stdio for their input, and hence when + writing an interactive scanner one must explictly call fflush() + after writing out a prompt. + + - flex scanner can be made reentrant (after a fashion) by using + "yyrestart( yyin );". This is useful for interactive scanners + which have interrupt handlers that long-jump out of the scanner. + + - a defense of why yylineno is not supported is included, along + with a suggestion on how to convert scanners which rely on it. + + + Other changes: + + - Prototypes and proper declarations of void routines have + been added to the flex source code, courtesy of Kevin B. Kenny. + + - Routines dealing with memory allocation now use void* pointers + instead of char* - see Makefile for porting implications. + + - Error-checking is now done when flex closes a file. + + - Various lint tweaks were added to reduce the number of gripes. + + - Makefile has been further parameterized to aid in porting. + + - Support for SCO Unix added. + + - Flex now sports the latest & greatest UC copyright notice + (which is only slightly different from the previous one). + + - A note has been added to flexdoc.1 mentioning work in progress + on modifying flex to generate straight C code rather than a + table-driven automaton, with an email address of whom to contact + if you are working along similar lines. + + +Changes between 2.2 Patch #3 (30Mar90) and 2.2 Patch #2: + + - fixed bug which caused -I scanners to bomb + + +Changes between 2.2 Patch #2 (27Mar90) and 2.2 Patch #1: + + - fixed bug writing past end of input buffer in yyunput() + - fixed bug detecting NUL's at the end of a buffer + + +Changes between 2.2 Patch #1 (23Mar90) and 2.2 (alpha) release: + + - Makefile fixes: definition of MAKE variable for systems + which don't have it; installation of flexdoc.1 along with + flex.1; fixed two bugs which could cause "bigtest" to fail. + + - flex.skel fix for compiling with g++. + + - README and flexdoc.1 no longer list an out-of-date BITNET address + for contacting me. + + - minor typos and formatting changes to flex.1 and flexdoc.1. + + +Changes between 2.2 (alpha) release of March '90 and previous release: + + User-visible: + + - Full user documentation now available. + + - Support for 8-bit scanners. + + - Scanners now accept NUL's. + + - A facility has been added for dealing with multiple + input buffers. + + - Two manual entries now. One which fully describes flex + (rather than just its differences from lex), and the + other for quick(er) reference. + + - A number of changes to bring flex closer into compliance + with the latest POSIX lex draft: + + %t support + flex now accepts multiple input files and concatenates + them together to form its input + previous -c (compress) flag renamed -C + do-nothing -c and -n flags added + Any indented code or code within %{}'s in section 2 is + now copied to the output + + - yyleng is now a bona fide global integer. + + - -d debug information now gives the line number of the + matched rule instead of which number rule it was from + the beginning of the file. + + - -v output now includes a summary of the flags used to generate + the scanner. + + - unput() and yyrestart() are now globally callable. + + - yyrestart() no longer closes the previous value of yyin. + + - C++ support; generated scanners can be compiled with C++ compiler. + + - Primitive -lfl library added, containing default main() + which calls yylex(). A number of routines currently living + in the scanner skeleton will probably migrate to here + in the future (in particular, yywrap() will probably cease + to be a macro and instead be a function in the -lfl library). + + - Hexadecimal (\x) escape sequences added. + + - Support for MS-DOS, VMS, and Turbo-C integrated. + + - The %used/%unused operators have been deprecated. They + may go away soon. + + + Other changes: + + - Makefile enhanced for easier testing and installation. + - The parser has been tweaked to detect some erroneous + constructions which previously were missed. + - Scanner input buffer overflow is now detected. + - Bugs with missing "const" declarations fixed. + - Out-of-date Minix/Atari patches provided. + - Scanners no longer require printf() unless FLEX_DEBUG is being used. + - A subtle input() bug has been fixed. + - Line numbers for "continued action" rules (those following + the special '|' action) are now correct. + - unput() bug fixed; had been causing problems porting flex to VMS. + - yymore() handling rewritten to fix bug with interaction + between yymore() and trailing context. + - EOF in actions now generates an error message. + - Bug involving -CFe and generating equivalence classes fixed. + - Bug which made -CF be treated as -Cf fixed. + - Support for SysV tmpnam() added. + - Unused #define's for scanner no longer generated. + - Error messages which are associated with a particular input + line are now all identified with their input line in standard + format. + - % directives which are valid to lex but not to flex are + now ignored instead of generating warnings. + - -DSYS_V flag can now also be specified -DUSG for System V + compilation. + + +Changes between 2.1 beta-test release of June '89 and previous release: + + User-visible: + + - -p flag generates a performance report to stderr. The report + consists of comments regarding features of the scanner rules + which result in slower scanners. + + - -b flag generates backtracking information to lex.backtrack. + This is a list of scanner states which require backtracking + and the characters on which they do so. By adding rules + one can remove backtracking states. If all backtracking states + are eliminated, the generated scanner will run faster. + Backtracking is not yet documented in the manual entry. + + - Variable trailing context now works, i.e., one can have + rules like "(foo)*/[ \t]*bletch". Some trailing context + patterns still cannot be properly matched and generate + error messages. These are patterns where the ending of the + first part of the rule matches the beginning of the second + part, such as "zx*/xy*", where the 'x*' matches the 'x' at + the beginning of the trailing context. Lex won't get these + patterns right either. + + - Faster scanners. + + - End-of-file rules. The special rule "<>" indicates + actions which are to be taken when an end-of-file is + encountered and yywrap() returns non-zero (i.e., indicates + no further files to process). See manual entry for example. + + - The -r (reject used) flag is gone. flex now scans the input + for occurrences of the string "REJECT" to determine if the + action is needed. It tries to be intelligent about this but + can be fooled. One can force the presence or absence of + REJECT by adding a line in the first section of the form + "%used REJECT" or "%unused REJECT". + + - yymore() has been implemented. Similarly to REJECT, flex + detects the use of yymore(), which can be overridden using + "%used" or "%unused". + + - Patterns like "x{0,3}" now work (i.e., with lower-limit == 0). + + - Removed '\^x' for ctrl-x misfeature. + + - Added '\a' and '\v' escape sequences. + + - \ now works for octal escape sequences; previously + \0 was required. + + - Better error reporting; line numbers are associated with rules. + + - yyleng is a macro; it cannot be accessed outside of the + scanner source file. + + - yytext and yyleng should not be modified within a flex action. + + - Generated scanners #define the name FLEX_SCANNER. + + - Rules are internally separated by YY_BREAK in lex.yy.c rather + than break, to allow redefinition. + + - The macro YY_USER_ACTION can be redefined to provide an action + which is always executed prior to the matched rule's action. + + - yyrestart() is a new action which can be used to restart + the scanner after it has seen an end-of-file (a "real" one, + that is, one for which yywrap() returned non-zero). It takes + a FILE* argument indicating a new file to scan and sets + things up so that a subsequent call to yylex() will start + scanning that file. + + - Internal scanner names all preceded by "yy_" + + - lex.yy.c is deleted if errors are encountered during processing. + + - Comments may be put in the first section of the input by preceding + them with '#'. + + + + Other changes: + + - Some portability-related bugs fixed, in particular for machines + with unsigned characters or sizeof( int* ) != sizeof( int ). + Also, tweaks for VMS and Microsoft C (MS-DOS), and identifiers all + trimmed to be 31 or fewer characters. Shortened file names + for dinosaur OS's. Checks for allocating > 64K memory + on 16 bit'ers. Amiga tweaks. Compiles using gcc on a Sun-3. + - Compressed and fast scanner skeletons merged. + - Skeleton header files done away with. + - Generated scanner uses prototypes and "const" for __STDC__. + - -DSV flag is now -DSYS_V for System V compilation. + - Removed all references to FTL language. + - Software now covered by BSD Copyright. + - flex will replace lex in subsequent BSD releases. diff --git a/src/toolsComm/flex/EPICS_READ_THIS b/src/toolsComm/flex/EPICS_READ_THIS new file mode 100644 index 000000000..a1d04b97d --- /dev/null +++ b/src/toolsComm/flex/EPICS_READ_THIS @@ -0,0 +1,32 @@ +This is a version of the BSD flex that has had its skeleton file munged in +order to force it to build lex programs that have all their functions and +variables defined as static. + +The file flex.skel.static is simply a copy of flex.skel that has been altered +to make all the components into static variables. + +In order to be able to actually use the lex files produced by this flavor of +flex, you must #include them into your C programs. Otherwise they will +be uncallable (all functions are static). This is typical of lex programs +that are used by yacc programs anyway. + +The scan.c file is actually the output of scan.l.DISTRIB when run through +itself, using the regular flex.skel skeleton with the -i option. + +To regenerate scan.c, make sure you have a build of a working e_flex binary +somewhere, then in this directory (not an O. build directory): + +% mv scan.l.DISTRIB scan.l +% /path/to/e_flex -Sflex.skel -8 -i scan.l +% mv lex.yy.c scan.c +% make + +Then use the new binary to make sure it can build itself: + +% O./e_flex -Sflex.skel -8 -i scan.l +% mv lex.yy.c scan.c +% make + +If that succeeds, don't forget to rename scan.l back again: + +% mv scan.l scan.l.DISTRIB diff --git a/src/toolsComm/flex/Flex.doc b/src/toolsComm/flex/Flex.doc new file mode 100644 index 000000000..3a37128e1 --- /dev/null +++ b/src/toolsComm/flex/Flex.doc @@ -0,0 +1,1792 @@ + 26 May 1990 FLEX(1) + + NAME + flex - fast lexical analyzer generator + + SYNOPSIS + flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...] + + DESCRIPTION + flex is a tool for generating scanners: programs which recognized lexi- + cal patterns in text. flex reads the given input files, or its standard + input if no file names are given, for a description of a scanner to gen- + erate. The description is in the form of pairs of regular expressions + and C code, called rules. flex generates as output a C source file, + lex.yy.c, which defines a routine yylex(). This file is compiled and + linked with the -lfl library to produce an executable. When the execut- + able is run, it analyzes its input for occurrences of the regular + expressions. Whenever it finds one, it executes the corresponding C + code. + + SOME SIMPLE EXAMPLES + + First some simple examples to get the flavor of how one uses flex. The + following flex input specifies a scanner which whenever it encounters + the string "username" will replace it with the user's login name: + + %% + username printf( "%s", getlogin() ); + + By default, any text not matched by a flex scanner is copied to the out- + put, so the net effect of this scanner is to copy its input file to its + output with each occurrence of "username" expanded. In this input, + there is just one rule. "username" is the pattern and the "printf" is + the action. The "%%" marks the beginning of the rules. + + Here's another simple example: + + int num_lines = 0, num_chars = 0; + + %% + \n ++num_lines; ++num_chars; + . ++num_chars; + + %% + main() + { + yylex(); + printf( "# of lines = %d, # of chars = %d\n", + num_lines, num_chars ); + } + + This scanner counts the number of characters and the number of lines in + its input (it produces no output other than the final report on the + counts). The first line declares two globals, "num_lines" and + "num_chars", which are accessible both inside yylex() and in the main() + + Version 2.3 1 + + FLEX(1) 26 May 1990 + + routine declared after the second "%%". There are two rules, one which + matches a newline ("\n") and increments both the line count and the + character count, and one which matches any character other than a new- + line (indicated by the "." regular expression). + + A somewhat more complicated example: + + /* scanner for a toy Pascal-like language */ + + %{ + /* need this for the call to atof() below */ + #include + %} + + DIGIT [0-9] + ID [a-z][a-z0-9]* + + %% + + {DIGIT}+ { + printf( "An integer: %s (%d)\n", yytext, + atoi( yytext ) ); + } + + {DIGIT}+"."{DIGIT}* { + printf( "A float: %s (%g)\n", yytext, + atof( yytext ) ); + } + + if|then|begin|end|procedure|function { + printf( "A keyword: %s\n", yytext ); + } + + {ID} printf( "An identifier: %s\n", yytext ); + + "+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext ); + + "{"[^}\n]*"}" /* eat up one-line comments */ + + [ \t\n]+ /* eat up whitespace */ + + . printf( "Unrecognized character: %s\n", yytext ); + + %% + + main( argc, argv ) + int argc; + char **argv; + { + ++argv, --argc; /* skip over program name */ + if ( argc > 0 ) + yyin = fopen( argv[0], "r" ); + else + yyin = stdin; + + 2 Version 2.3 + + 26 May 1990 FLEX(1) + + yylex(); + } + + This is the beginnings of a simple scanner for a language like Pascal. + It identifies different types of tokens and reports on what it has seen. + + The details of this example will be explained in the following sections. + + FORMAT OF THE INPUT FILE + The flex input file consists of three sections, separated by a line with + just %% in it: + + definitions + %% + rules + %% + user code + + The definitions section contains declarations of simple name definitions + to simplify the scanner specification, and declarations of start condi- + tions, which are explained in a later section. + + Name definitions have the form: + + name definition + + The "name" is a word beginning with a letter or an underscore ('_') fol- + lowed by zero or more letters, digits, '_', or '-' (dash). The defini- + tion is taken to begin at the first non-white-space character following + the name and continuing to the end of the line. The definition can sub- + sequently be referred to using "{name}", which will expand to "(defini- + tion)". For example, + + DIGIT [0-9] + ID [a-z][a-z0-9]* + + defines "DIGIT" to be a regular expression which matches a single digit, + and "ID" to be a regular expression which matches a letter followed by + zero-or-more letters-or-digits. A subsequent reference to + + {DIGIT}+"."{DIGIT}* + + is identical to + + ([0-9])+"."([0-9])* + + and matches one-or-more digits followed by a '.' followed by zero-or- + more digits. + + The rules section of the flex input contains a series of rules of the + form: + + pattern action + + Version 2.3 3 + + FLEX(1) 26 May 1990 + + where the pattern must be unindented and the action must begin on the + same line. + + See below for a further description of patterns and actions. + + Finally, the user code section is simply copied to lex.yy.c verbatim. + It is used for companion routines which call or are called by the + scanner. The presence of this section is optional; if it is missing, + the second %% in the input file may be skipped, too. + + In the definitions and rules sections, any indented text or text + enclosed in %{ and %} is copied verbatim to the output (with the %{}'s + removed). The %{}'s must appear unindented on lines by themselves. + + In the rules section, any indented or %{} text appearing before the + first rule may be used to declare variables which are local to the scan- + ning routine and (after the declarations) code which is to be executed + whenever the scanning routine is entered. Other indented or %{} text in + the rule section is still copied to the output, but its meaning is not + well-defined and it may well cause compile-time errors (this feature is + present for POSIX compliance; see below for other such features). + + In the definitions section, an unindented comment (i.e., a line begin- + ning with "/*") is also copied verbatim to the output up to the next + "*/". Also, any line in the definitions section beginning with '#' is + ignored, though this style of comment is deprecated and may go away in + the future. + + PATTERNS + The patterns in the input are written using an extended set of regular + expressions. These are: + + x match the character 'x' + . any character except newline + [xyz] a "character class"; in this case, the pattern + matches either an 'x', a 'y', or a 'z' + [abj-oZ] a "character class" with a range in it; matches + an 'a', a 'b', any letter from 'j' through 'o', + or a 'Z' + [^A-Z] a "negated character class", i.e., any character + but those in the class. In this case, any + character EXCEPT an uppercase letter. + [^A-Z\n] any character EXCEPT an uppercase letter or + a newline + r* zero or more r's, where r is any regular expression + r+ one or more r's + r? zero or one r's (that is, "an optional r") + r{2,5} anywhere from two to five r's + r{2,} two or more r's + r{4} exactly 4 r's + {name} the expansion of the "name" definition + (see above) + "[xyz]\"foo" + + 4 Version 2.3 + + 26 May 1990 FLEX(1) + + the literal string: [xyz]"foo + \X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v', + then the ANSI-C interpretation of \x. + Otherwise, a literal 'X' (used to escape + operators such as '*') + \123 the character with octal value 123 + \x2a the character with hexadecimal value 2a + (r) match an r; parentheses are used to override + precedence (see below) + + rs the regular expression r followed by the + regular expression s; called "concatenation" + + r|s either an r or an s + + r/s an r but only if it is followed by an s. The + s is not part of the matched text. This type + of pattern is called as "trailing context". + ^r an r, but only at the beginning of a line + r$ an r, but only at the end of a line. Equivalent + to "r/\n". + + r an r, but only in start condition s (see + below for discussion of start conditions) + r + same, but in any of start conditions s1, + s2, or s3 + + <> an end-of-file + <> + an end-of-file when in start condition s1 or s2 + + The regular expressions listed above are grouped according to pre- + cedence, from highest precedence at the top to lowest at the bottom. + Those grouped together have equal precedence. For example, + + foo|bar* + + is the same as + + (foo)|(ba(r*)) + + since the '*' operator has higher precedence than concatenation, and + concatenation higher than alternation ('|'). This pattern therefore + matches either the string "foo" or the string "ba" followed by zero-or- + more r's. To match "foo" or zero-or-more "bar"'s, use: + + foo|(bar)* + + Version 2.3 5 + + FLEX(1) 26 May 1990 + + and to match zero-or-more "foo"'s-or-"bar"'s: + + (foo|bar)* + + Some notes on patterns: + + - A negated character class such as the example "[^A-Z]" above will + match a newline unless "\n" (or an equivalent escape sequence) is + one of the characters explicitly present in the negated character + class (e.g., "[^A-Z\n]"). This is unlike how many other regular + expression tools treat negated character classes, but unfortunately + the inconsistency is historically entrenched. Matching newlines + means that a pattern like [^"]* can match an entire input (over- + flowing the scanner's input buffer) unless there's another quote in + the input. + + - A rule can have at most one instance of trailing context (the '/' + operator or the '$' operator). The start condition, '^', and + "<>" patterns can only occur at the beginning of a pattern, + and, as well as with '/' and '$', cannot be grouped inside + parentheses. A '^' which does not occur at the beginning of a rule + or a '$' which does not occur at the end of a rule loses its spe- + cial properties and is treated as a normal character. + + The following are illegal: + + foo/bar$ + foobar + + Note that the first of these, can be written "foo/bar\n". + + The following will result in '$' or '^' being treated as a normal + character: + + foo|(bar$) + foo|^bar + + If what's wanted is a "foo" or a bar-followed-by-a-newline, the + following could be used (the special '|' action is explained + below): + + foo | + bar$ /* action goes here */ + + A similar trick will work for matching a foo or a bar-at-the- + beginning-of-a-line. + + HOW THE INPUT IS MATCHED + When the generated scanner is run, it analyzes its input looking for + strings which match any of its patterns. If it finds more than one + match, it takes the one matching the most text (for trailing context + rules, this includes the length of the trailing part, even though it + will then be returned to the input). If it finds two or more matches of + + 6 Version 2.3 + + 26 May 1990 FLEX(1) + + the same length, the rule listed first in the flex input file is chosen. + + Once the match is determined, the text corresponding to the match + (called the token) is made available in the global character pointer + yytext, and its length in the global integer yyleng. The action + corresponding to the matched pattern is then executed (a more detailed + description of actions follows), and then the remaining input is scanned + for another match. + + If no match is found, then the default rule is executed: the next char- + acter in the input is considered matched and copied to the standard out- + put. Thus, the simplest legal flex input is: + + %% + + which generates a scanner that simply copies its input (one character at + a time) to its output. + + ACTIONS + Each pattern in a rule has a corresponding action, which can be any + arbitrary C statement. The pattern ends at the first non-escaped whi- + tespace character; the remainder of the line is its action. If the + action is empty, then when the pattern is matched the input token is + simply discarded. For example, here is the specification for a program + which deletes all occurrences of "zap me" from its input: + + %% + "zap me" + + (It will copy all other characters in the input to the output since they + will be matched by the default rule.) + + Here is a program which compresses multiple blanks and tabs down to a + single blank, and throws away whitespace found at the end of a line: + + %% + [ \t]+ putchar( ' ' ); + [ \t]+$ /* ignore this token */ + + If the action contains a '{', then the action spans till the balancing + '}' is found, and the action may cross multiple lines. flex knows about + C strings and comments and won't be fooled by braces found within them, + but also allows actions to begin with %{ and will consider the action to + be all the text up to the next %} (regardless of ordinary braces inside + the action). + + An action consisting solely of a vertical bar ('|') means "same as the + action for the next rule." See below for an illustration. + + Actions can include arbitrary C code, including return statements to + return a value to whatever routine called yylex(). Each time yylex() is + called it continues processing tokens from where it last left off until + it either reaches the end of the file or executes a return. Once it + + Version 2.3 7 + + FLEX(1) 26 May 1990 + + reaches an end-of-file, however, then any subsequent call to yylex() + will simply immediately return, unless yyrestart() is first called (see + below). + + Actions are not allowed to modify yytext or yyleng. + + There are a number of special directives which can be included within an + action: + + - ECHO copies yytext to the scanner's output. + + - BEGIN followed by the name of a start condition places the scanner + in the corresponding start condition (see below). + + - REJECT directs the scanner to proceed on to the "second best" rule + which matched the input (or a prefix of the input). The rule is + chosen as described above in "How the Input is Matched", and yytext + and yyleng set up appropriately. It may either be one which + matched as much text as the originally chosen rule but came later + in the flex input file, or one which matched less text. For exam- + ple, the following will both count the words in the input and call + the routine special() whenever "frob" is seen: + + int word_count = 0; + %% + + frob special(); REJECT; + [^ \t\n]+ ++word_count; + + Without the REJECT, any "frob"'s in the input would not be counted + as words, since the scanner normally executes only one action per + token. Multiple REJECT's are allowed, each one finding the next + best choice to the currently active rule. For example, when the + following scanner scans the token "abcd", it will write "abcdab- + caba" to the output: + + %% + a | + ab | + abc | + abcd ECHO; REJECT; + .|\n /* eat up any unmatched character */ + + (The first three rules share the fourth's action since they use the + special '|' action.) REJECT is a particularly expensive feature in + terms scanner performance; if it is used in any of the scanner's + actions it will slow down all of the scanner's matching. Further- + more, REJECT cannot be used with the -f or -F options (see below). + + Note also that unlike the other special actions, REJECT is a + branch; code immediately following it in the action will not be + executed. + + - yymore() tells the scanner that the next time it matches a rule, + + 8 Version 2.3 + + 26 May 1990 FLEX(1) + + the corresponding token should be appended onto the current value + of yytext rather than replacing it. For example, given the input + "mega-kludge" the following will write "mega-mega-kludge" to the + output: + + %% + mega- ECHO; yymore(); + kludge ECHO; + + First "mega-" is matched and echoed to the output. Then "kludge" + is matched, but the previous "mega-" is still hanging around at the + beginning of yytext so the ECHO for the "kludge" rule will actually + write "mega-kludge". The presence of yymore() in the scanner's + action entails a minor performance penalty in the scanner's match- + ing speed. + + - yyless(n) returns all but the first n characters of the current + token back to the input stream, where they will be rescanned when + the scanner looks for the next match. yytext and yyleng are + adjusted appropriately (e.g., yyleng will now be equal to n ). For + example, on the input "foobar" the following will write out + "foobarbar": + + %% + foobar ECHO; yyless(3); + [a-z]+ ECHO; + + An argument of 0 to yyless will cause the entire current input + string to be scanned again. Unless you've changed how the scanner + will subsequently process its input (using BEGIN, for example), + this will result in an endless loop. + + - unput(c) puts the character c back onto the input stream. It will + be the next character scanned. The following action will take the + current token and cause it to be rescanned enclosed in parentheses. + + { + int i; + unput( ')' ); + for ( i = yyleng - 1; i >= 0; --i ) + unput( yytext[i] ); + unput( '(' ); + } + + Note that since each unput() puts the given character back at the + beginning of the input stream, pushing back strings must be done + back-to-front. + + - input() reads the next character from the input stream. For exam- + ple, the following is one way to eat up C comments: + + %% + "/*" { + register int c; + + Version 2.3 9 + + FLEX(1) 26 May 1990 + + for ( ; ; ) + { + while ( (c = input()) != '*' && + c != EOF ) + ; /* eat up text of comment */ + + if ( c == '*' ) + { + while ( (c = input()) == '*' ) + ; + if ( c == '/' ) + break; /* found the end */ + } + + if ( c == EOF ) + { + error( "EOF in comment" ); + break; + } + } + } + + (Note that if the scanner is compiled using C++, then input() is + instead referred to as yyinput(), in order to avoid a name clash + with the C++ stream by the name of input.) + + - yyterminate() can be used in lieu of a return statement in an + action. It terminates the scanner and returns a 0 to the scanner's + caller, indicating "all done". Subsequent calls to the scanner + will immediately return unless preceded by a call to yyrestart() + (see below). By default, yyterminate() is also called when an + end-of-file is encountered. It is a macro and may be redefined. + + THE GENERATED SCANNER + The output of flex is the file lex.yy.c, which contains the scanning + routine yylex(), a number of tables used by it for matching tokens, and + a number of auxiliary routines and macros. By default, yylex() is + declared as follows: + + int yylex() + { + ... various definitions and the actions in here ... + } + + (If your environment supports function prototypes, then it will be "int + yylex( void )".) This definition may be changed by redefining the + "YY_DECL" macro. For example, you could use: + + #undef YY_DECL + #define YY_DECL float lexscan( a, b ) float a, b; + + to give the scanning routine the name lexscan, returning a float, and + taking two floats as arguments. Note that if you give arguments to the + + 10 Version 2.3 + + 26 May 1990 FLEX(1) + + scanning routine using a K&R-style/non-prototyped function declaration, + you must terminate the definition with a semi-colon (;). + + Whenever yylex() is called, it scans tokens from the global input file + yyin (which defaults to stdin). It continues until it either reaches an + end-of-file (at which point it returns the value 0) or one of its + actions executes a return statement. In the former case, when called + again the scanner will immediately return unless yyrestart() is called + to point yyin at the new input file. ( yyrestart() takes one argument, + a FILE * pointer.) In the latter case (i.e., when an action executes a + return), the scanner may then be called again and it will resume scan- + ning where it left off. + + By default (and for purposes of efficiency), the scanner uses block- + reads rather than simple getc() calls to read characters from yyin. The + nature of how it gets its input can be controlled by redefining the + YY_INPUT macro. YY_INPUT's calling sequence is + "YY_INPUT(buf,result,max_size)". Its action is to place up to max_size + characters in the character array buf and return in the integer variable + result either the number of characters read or the constant YY_NULL (0 + on Unix systems) to indicate EOF. The default YY_INPUT reads from the + global file-pointer "yyin". + + A sample redefinition of YY_INPUT (in the definitions section of the + input file): + + %{ + #undef YY_INPUT + #define YY_INPUT(buf,result,max_size) \ + { \ + int c = getchar(); \ + result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ + } + %} + + This definition will change the input processing to occur one character + at a time. + + You also can add in things like keeping track of the input line number + this way; but don't expect your scanner to go very fast. + + When the scanner receives an end-of-file indication from YY_INPUT, it + then checks the yywrap() function. If yywrap() returns false (zero), + then it is assumed that the function has gone ahead and set up yyin to + point to another input file, and scanning continues. If it returns true + (non-zero), then the scanner terminates, returning 0 to its caller. + + The default yywrap() always returns 1. Presently, to redefine it you + must first "#undef yywrap", as it is currently implemented as a macro. + As indicated by the hedging in the previous sentence, it may be changed + to a true function in the near future. + + The scanner writes its ECHO output to the yyout global (default, + stdout), which may be redefined by the user simply by assigning it to + + Version 2.3 11 + + FLEX(1) 26 May 1990 + + some other FILE pointer. + + START CONDITIONS + flex provides a mechanism for conditionally activating rules. Any rule + whose pattern is prefixed with "" will only be active when the + scanner is in the start condition named "sc". For example, + + [^"]* { /* eat up the string body ... */ + ... + } + + will be active only when the scanner is in the "STRING" start condition, + and + + \. { /* handle an escape ... */ + ... + } + + will be active only when the current start condition is either "INI- + TIAL", "STRING", or "QUOTE". + + Start conditions are declared in the definitions (first) section of the + input using unindented lines beginning with either %s or %x followed by + a list of names. The former declares inclusive start conditions, the + latter exclusive start conditions. A start condition is activated using + the BEGIN action. Until the next BEGIN action is executed, rules with + the given start condition will be active and rules with other start con- + ditions will be inactive. If the start condition is inclusive, then + rules with no start conditions at all will also be active. If it is + exclusive, then only rules qualified with the start condition will be + active. A set of rules contingent on the same exclusive start condition + describe a scanner which is independent of any of the other rules in the + flex input. Because of this, exclusive start conditions make it easy to + specify "mini-scanners" which scan portions of the input that are syn- + tactically different from the rest (e.g., comments). + + If the distinction between inclusive and exclusive start conditions is + still a little vague, here's a simple example illustrating the connec- + tion between the two. The set of rules: + + %s example + %% + foo /* do something */ + + is equivalent to + + %x example + %% + foo /* do something */ + + The default rule (to ECHO any unmatched character) remains active in + start conditions. + + 12 Version 2.3 + + 26 May 1990 FLEX(1) + + BEGIN(0) returns to the original state where only the rules with no + start conditions are active. This state can also be referred to as the + start-condition "INITIAL", so BEGIN(INITIAL) is equivalent to BEGIN(0). + (The parentheses around the start condition name are not required but + are considered good style.) + + BEGIN actions can also be given as indented code at the beginning of the + rules section. For example, the following will cause the scanner to + enter the "SPECIAL" start condition whenever yylex() is called and the + global variable enter_special is true: + + int enter_special; + + %x SPECIAL + %% + if ( enter_special ) + BEGIN(SPECIAL); + + blahblahblah + ...more rules follow... + + To illustrate the uses of start conditions, here is a scanner which pro- + vides two different interpretations of a string like "123.456". By + default it will treat it as as three tokens, the integer "123", a dot + ('.'), and the integer "456". But if the string is preceded earlier in + the line by the string "expect-floats" it will treat it as a single + token, the floating-point number 123.456: + + %{ + #include + %} + %s expect + + %% + expect-floats BEGIN(expect); + + [0-9]+"."[0-9]+ { + printf( "found a float, = %f\n", + atof( yytext ) ); + } + \n { + /* that's the end of the line, so + * we need another "expect-number" + * before we'll recognize any more + * numbers + */ + BEGIN(INITIAL); + } + + [0-9]+ { + printf( "found an integer, = %d\n", + atoi( yytext ) ); + } + + Version 2.3 13 + + FLEX(1) 26 May 1990 + + "." printf( "found a dot\n" ); + + Here is a scanner which recognizes (and discards) C comments while main- + taining a count of the current input line. + + %x comment + %% + int line_num = 1; + + "/*" BEGIN(comment); + + [^*\n]* /* eat anything that's not a '*' */ + "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ + \n ++line_num; + "*"+"/" BEGIN(INITIAL); + + Note that start-conditions names are really integer values and can be + stored as such. Thus, the above could be extended in the following + fashion: + + %x comment foo + %% + int line_num = 1; + int comment_caller; + + "/*" { + comment_caller = INITIAL; + BEGIN(comment); + } + + ... + + "/*" { + comment_caller = foo; + BEGIN(comment); + } + + [^*\n]* /* eat anything that's not a '*' */ + "*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ + \n ++line_num; + "*"+"/" BEGIN(comment_caller); + + One can then implement a "stack" of start conditions using an array of + integers. (It is likely that such stacks will become a full-fledged + flex feature in the future.) Note, though, that start conditions do not + have their own name-space; %s's and %x's declare names in the same + fashion as #define's. + + MULTIPLE INPUT BUFFERS + Some scanners (such as those which support "include" files) require + reading from several input streams. As flex scanners do a large amount + of buffering, one cannot control where the next input will be read from + by simply writing a YY_INPUT which is sensitive to the scanning context. + + 14 Version 2.3 + + 26 May 1990 FLEX(1) + + YY_INPUT is only called when the scanner reaches the end of its buffer, + which may be a long time after scanning a statement such as an "include" + which requires switching the input source. + + To negotiate these sorts of problems, flex provides a mechanism for + creating and switching between multiple input buffers. An input buffer + is created by using: + + YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + + which takes a FILE pointer and a size and creates a buffer associated + with the given file and large enough to hold size characters (when in + doubt, use YY_BUF_SIZE for the size). It returns a YY_BUFFER_STATE han- + dle, which may then be passed to other routines: + + void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + + switches the scanner's input buffer so subsequent tokens will come from + new_buffer. Note that yy_switch_to_buffer() may be used by yywrap() to + sets things up for continued scanning, instead of opening a new file and + pointing yyin at it. + + void yy_delete_buffer( YY_BUFFER_STATE buffer ) + + is used to reclaim the storage associated with a buffer. + + yy_new_buffer() is an alias for yy_create_buffer(), provided for compa- + tibility with the C++ use of new and delete for creating and destroying + dynamic objects. + + Finally, the YY_CURRENT_BUFFER macro returns a YY_BUFFER_STATE handle to + the current buffer. + + Here is an example of using these features for writing a scanner which + expands include files (the <> feature is discussed below): + + /* the "incl" state is used for picking up the name + * of an include file + */ + %x incl + + %{ + #define MAX_INCLUDE_DEPTH 10 + YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; + int include_stack_ptr = 0; + %} + + %% + include BEGIN(incl); + + [a-z]+ ECHO; + [^a-z\n]*\n? ECHO; + + [ \t]* /* eat the whitespace */ + + Version 2.3 15 + + FLEX(1) 26 May 1990 + + [^ \t\n]+ { /* got the include file name */ + if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) + { + fprintf( stderr, "Includes nested too deeply" ); + exit( 1 ); + } + + include_stack[include_stack_ptr++] = + YY_CURRENT_BUFFER; + + yyin = fopen( yytext, "r" ); + + if ( ! yyin ) + error( ... ); + + yy_switch_to_buffer( + yy_create_buffer( yyin, YY_BUF_SIZE ) ); + + BEGIN(INITIAL); + } + + <> { + if ( --include_stack_ptr < 0 ) + { + yyterminate(); + } + + else + yy_switch_to_buffer( + include_stack[include_stack_ptr] ); + } + + END-OF-FILE RULES + The special rule "<>" indicates actions which are to be taken when + an end-of-file is encountered and yywrap() returns non-zero (i.e., indi- + cates no further files to process). The action must finish by doing one + of four things: + + - the special YY_NEW_FILE action, if yyin has been pointed at a new + file to process; + + - a return statement; + + - the special yyterminate() action; + + - or, switching to a new buffer using yy_switch_to_buffer() as shown + in the example above. + + <> rules may not be used with other patterns; they may only be + qualified with a list of start conditions. If an unqualified <> + rule is given, it applies to all start conditions which do not already + have <> actions. To specify an <> rule for only the initial + start condition, use + + 16 Version 2.3 + + 26 May 1990 FLEX(1) + + <> + + These rules are useful for catching things like unclosed comments. An + example: + + %x quote + %% + + ...other rules for dealing with quotes... + + <> { + error( "unterminated quote" ); + yyterminate(); + } + <> { + if ( *++filelist ) + { + yyin = fopen( *filelist, "r" ); + YY_NEW_FILE; + } + else + yyterminate(); + } + + MISCELLANEOUS MACROS + The macro YY_USER_ACTION can be redefined to provide an action which is + always executed prior to the matched rule's action. For example, it + could be #define'd to call a routine to convert yytext to lower-case. + + The macro YY_USER_INIT may be redefined to provide an action which is + always executed before the first scan (and before the scanner's internal + initializations are done). For example, it could be used to call a rou- + tine to read in a data table or open a logging file. + + In the generated scanner, the actions are all gathered in one large + switch statement and separated using YY_BREAK, which may be redefined. + By default, it is simply a "break", to separate each rule's action from + the following rule's. Redefining YY_BREAK allows, for example, C++ + users to #define YY_BREAK to do nothing (while being very careful that + every rule ends with a "break" or a "return"!) to avoid suffering from + unreachable statement warnings where because a rule's action ends with + "return", the YY_BREAK is inaccessible. + + INTERFACING WITH YACC + One of the main uses of flex is as a companion to the yacc parser- + generator. yacc parsers expect to call a routine named yylex() to find + the next input token. The routine is supposed to return the type of the + next token as well as putting any associated value in the global yylval. + To use flex with yacc, one specifies the -d option to yacc to instruct + it to generate the file y.tab.h containing definitions of all the + %tokens appearing in the yacc input. This file is then included in the + + Version 2.3 17 + + FLEX(1) 26 May 1990 + + flex scanner. For example, if one of the tokens is "TOK_NUMBER", part + of the scanner might look like: + + %{ + #include "y.tab.h" + %} + + %% + + [0-9]+ yylval = atoi( yytext ); return TOK_NUMBER; + + TRANSLATION TABLE + In the name of POSIX compliance, flex supports a translation table for + mapping input characters into groups. The table is specified in the + first section, and its format looks like: + + %t + 1 abcd + 2 ABCDEFGHIJKLMNOPQRSTUVWXYZ + 52 0123456789 + 6 \t\ \n + %t + + This example specifies that the characters 'a', 'b', 'c', and 'd' are to + all be lumped into group #1, upper-case letters in group #2, digits in + group #52, tabs, blanks, and newlines into group #6, and no other char- + acters will appear in the patterns. The group numbers are actually + disregarded by flex; %t serves, though, to lump characters together. + Given the above table, for example, the pattern "a(AA)*5" is equivalent + to "d(ZQ)*0". They both say, "match any character in group #1, followed + by zero-or-more pairs of characters from group #2, followed by a charac- + ter from group #52." Thus %t provides a crude way for introducing + equivalence classes into the scanner specification. + + Note that the -i option (see below) coupled with the equivalence classes + which flex automatically generates take care of virtually all the + instances when one might consider using %t. But what the hell, it's + there if you want it. + + OPTIONS + flex has the following options: + + -b Generate backtracking information to lex.backtrack. This is a list + of scanner states which require backtracking and the input charac- + ters on which they do so. By adding rules one can remove back- + tracking states. If all backtracking states are eliminated and -f + or -F is used, the generated scanner will run faster (see the -p + flag). Only users who wish to squeeze every last cycle out of + their scanners need worry about this option. (See the section on + PERFORMANCE CONSIDERATIONS below.) + + -c is a do-nothing, deprecated option included for POSIX compliance. + + 18 Version 2.3 + + 26 May 1990 FLEX(1) + + NOTE: in previous releases of flex -c specified table-compression + options. This functionality is now given by the -C flag. To ease + the the impact of this change, when flex encounters -c, it + currently issues a warning message and assumes that -C was desired + instead. In the future this "promotion" of -c to -C will go away + in the name of full POSIX compliance (unless the POSIX meaning is + removed first). + + -d makes the generated scanner run in debug mode. Whenever a pattern + is recognized and the global yy_flex_debug is non-zero (which is + the default), the scanner will write to stderr a line of the form: + + --accepting rule at line 53 ("the matched text") + + The line number refers to the location of the rule in the file + defining the scanner (i.e., the file that was fed to flex). Mes- + sages are also generated when the scanner backtracks, accepts the + default rule, reaches the end of its input buffer (or encounters a + NUL; at this point, the two look the same as far as the scanner's + concerned), or reaches an end-of-file. + + -f specifies (take your pick) full table or fast scanner. No table + compression is done. The result is large but fast. This option is + equivalent to -Cf (see below). + + -i instructs flex to generate a case-insensitive scanner. The case of + letters given in the flex input patterns will be ignored, and + tokens in the input will be matched regardless of case. The + matched text given in yytext will have the preserved case (i.e., it + will not be folded). + + -n is another do-nothing, deprecated option included only for POSIX + compliance. + + -p generates a performance report to stderr. The report consists of + comments regarding features of the flex input file which will cause + a loss of performance in the resulting scanner. Note that the use + of REJECT and variable trailing context (see the BUGS section in + flex(1)) entails a substantial performance penalty; use of + yymore(), the ^ operator, and the -I flag entail minor performance + penalties. + + -s causes the default rule (that unmatched scanner input is echoed to + stdout) to be suppressed. If the scanner encounters input that + does not match any of its rules, it aborts with an error. This + option is useful for finding holes in a scanner's rule set. + + -t instructs flex to write the scanner it generates to standard output + instead of lex.yy.c. + + -v specifies that flex should write to stderr a summary of statistics + regarding the scanner it generates. Most of the statistics are + meaningless to the casual flex user, but the first line identifies + the version of flex, which is useful for figuring out where you + + Version 2.3 19 + + FLEX(1) 26 May 1990 + + stand with respect to patches and new releases, and the next two + lines give the date when the scanner was created and a summary of + the flags which were in effect. + + -F specifies that the fast scanner table representation should be + used. This representation is about as fast as the full table + representation (-f), and for some sets of patterns will be consid- + erably smaller (and for others, larger). In general, if the pat- + tern set contains both "keywords" and a catch-all, "identifier" + rule, such as in the set: + + "case" return TOK_CASE; + "switch" return TOK_SWITCH; + ... + "default" return TOK_DEFAULT; + [a-z]+ return TOK_ID; + + then you're better off using the full table representation. If + only the "identifier" rule is present and you then use a hash table + or some such to detect the keywords, you're better off using -F. + + This option is equivalent to -CF (see below). + + -I instructs flex to generate an interactive scanner. Normally, + scanners generated by flex always look ahead one character before + deciding that a rule has been matched. At the cost of some scan- + ning overhead, flex will generate a scanner which only looks ahead + when needed. Such scanners are called interactive because if you + want to write a scanner for an interactive system such as a command + shell, you will probably want the user's input to be terminated + with a newline, and without -I the user will have to type a charac- + ter in addition to the newline in order to have the newline recog- + nized. This leads to dreadful interactive performance. + + If all this seems to confusing, here's the general rule: if a human + will be typing in input to your scanner, use -I, otherwise don't; + if you don't care about squeezing the utmost performance from your + scanner and you don't want to make any assumptions about the input + to your scanner, use -I. + + + Note, -I cannot be used in conjunction with full or fast tables, + i.e., the -f, -F, -Cf, or -CF flags. + + -L instructs flex not to generate #line directives. Without this + option, flex peppers the generated scanner with #line directives so + error messages in the actions will be correctly located with + respect to the original flex input file, and not to the fairly + meaningless line numbers of lex.yy.c. (Unfortunately flex does not + presently generate the necessary directives to "retarget" the line + numbers for those parts of lex.yy.c which it generated. So if + there is an error in the generated code, a meaningless line number + is reported.) + + -T makes flex run in trace mode. It will generate a lot of messages + + 20 Version 2.3 + + 26 May 1990 FLEX(1) + + to stdout concerning the form of the input and the resultant non- + deterministic and deterministic finite automata. This option is + mostly for use in maintaining flex. + + -8 instructs flex to generate an 8-bit scanner, i.e., one which can + recognize 8-bit characters. On some sites, flex is installed with + this option as the default. On others, the default is 7-bit char- + acters. To see which is the case, check the verbose (-v) output + for "equivalence classes created". If the denominator of the + number shown is 128, then by default flex is generating 7-bit char- + acters. If it is 256, then the default is 8-bit characters and the + -8 flag is not required (but may be a good idea to keep the scanner + specification portable). Feeding a 7-bit scanner 8-bit characters + will result in infinite loops, bus errors, or other such fireworks, + so when in doubt, use the flag. Note that if equivalence classes + are used, 8-bit scanners take only slightly more table space than + 7-bit scanners (128 bytes, to be exact); if equivalence classes are + not used, however, then the tables may grow up to twice their 7-bit + size. + + -C[efmF] + controls the degree of table compression. + + -Ce directs flex to construct equivalence classes, i.e., sets of + characters which have identical lexical properties (for example, if + the only appearance of digits in the flex input is in the character + class "[0-9]" then the digits '0', '1', ..., '9' will all be put in + the same equivalence class). Equivalence classes usually give + dramatic reductions in the final table/object file sizes (typically + a factor of 2-5) and are pretty cheap performance-wise (one array + look-up per character scanned). + + -Cf specifies that the full scanner tables should be generated - + flex should not compress the tables by taking advantages of similar + transition functions for different states. + + -CF specifies that the alternate fast scanner representation + (described above under the -F flag) should be used. + + -Cm directs flex to construct meta-equivalence classes, which are + sets of equivalence classes (or characters, if equivalence classes + are not being used) that are commonly used together. Meta- + equivalence classes are often a big win when using compressed + tables, but they have a moderate performance impact (one or two + "if" tests and one array look-up per character scanned). + + A lone -C specifies that the scanner tables should be compressed + but neither equivalence classes nor meta-equivalence classes should + be used. + + The options -Cf or -CF and -Cm do not make sense together - there + is no opportunity for meta-equivalence classes if the table is not + being compressed. Otherwise the options may be freely mixed. + + Version 2.3 21 + + FLEX(1) 26 May 1990 + + The default setting is -Cem, which specifies that flex should gen- + erate equivalence classes and meta-equivalence classes. This set- + ting provides the highest degree of table compression. You can + trade off faster-executing scanners at the cost of larger tables + with the following generally being true: + + slowest & smallest + -Cem + -Cm + -Ce + -C + -C{f,F}e + -C{f,F} + fastest & largest + + Note that scanners with the smallest tables are usually generated + and compiled the quickest, so during development you will usually + want to use the default, maximal compression. + + -Cfe is often a good compromise between speed and size for produc- + tion scanners. + + -C options are not cumulative; whenever the flag is encountered, + the previous -C settings are forgotten. + + -Sskeleton_file + overrides the default skeleton file from which flex constructs its + scanners. You'll never need this option unless you are doing flex + maintenance or development. + + PERFORMANCE CONSIDERATIONS + The main design goal of flex is that it generate high-performance + scanners. It has been optimized for dealing well with large sets of + rules. Aside from the effects of table compression on scanner speed + outlined above, there are a number of options/actions which degrade per- + formance. These are, from most expensive to least: + + REJECT + + pattern sets that require backtracking + arbitrary trailing context + + '^' beginning-of-line operator + yymore() + + with the first three all being quite expensive and the last two being + quite cheap. + + REJECT should be avoided at all costs when performance is important. It + is a particularly expensive option. + + Getting rid of backtracking is messy and often may be an enormous amount + of work for a complicated scanner. In principal, one begins by using + the -b flag to generate a lex.backtrack file. For example, on the input + + 22 Version 2.3 + + 26 May 1990 FLEX(1) + + %% + foo return TOK_KEYWORD; + foobar return TOK_KEYWORD; + + the file looks like: + + State #6 is non-accepting - + associated rule line numbers: + 2 3 + out-transitions: [ o ] + jam-transitions: EOF [ \001-n p-\177 ] + + State #8 is non-accepting - + associated rule line numbers: + 3 + out-transitions: [ a ] + jam-transitions: EOF [ \001-` b-\177 ] + + State #9 is non-accepting - + associated rule line numbers: + 3 + out-transitions: [ r ] + jam-transitions: EOF [ \001-q s-\177 ] + + Compressed tables always backtrack. + + The first few lines tell us that there's a scanner state in which it can + make a transition on an 'o' but not on any other character, and that in + that state the currently scanned text does not match any rule. The + state occurs when trying to match the rules found at lines 2 and 3 in + the input file. If the scanner is in that state and then reads some- + thing other than an 'o', it will have to backtrack to find a rule which + is matched. With a bit of headscratching one can see that this must be + the state it's in when it has seen "fo". When this has happened, if + anything other than another 'o' is seen, the scanner will have to back + up to simply match the 'f' (by the default rule). + + The comment regarding State #8 indicates there's a problem when "foob" + has been scanned. Indeed, on any character other than a 'b', the + scanner will have to back up to accept "foo". Similarly, the comment + for State #9 concerns when "fooba" has been scanned. + + The final comment reminds us that there's no point going to all the + trouble of removing backtracking from the rules unless we're using -f or + -F, since there's no performance gain doing so with compressed scanners. + + The way to remove the backtracking is to add "error" rules: + + %% + foo return TOK_KEYWORD; + foobar return TOK_KEYWORD; + + fooba | + + Version 2.3 23 + + FLEX(1) 26 May 1990 + + foob | + fo { + /* false alarm, not really a keyword */ + return TOK_ID; + } + + Eliminating backtracking among a list of keywords can also be done using + a "catch-all" rule: + + %% + foo return TOK_KEYWORD; + foobar return TOK_KEYWORD; + + [a-z]+ return TOK_ID; + + This is usually the best solution when appropriate. + + Backtracking messages tend to cascade. With a complicated set of rules + it's not uncommon to get hundreds of messages. If one can decipher + them, though, it often only takes a dozen or so rules to eliminate the + backtracking (though it's easy to make a mistake and have an error rule + accidentally match a valid token. A possible future flex feature will + be to automatically add rules to eliminate backtracking). + + Variable trailing context (where both the leading and trailing parts do + not have a fixed length) entails almost the same performance loss as + REJECT (i.e., substantial). So when possible a rule like: + + %% + mouse|rat/(cat|dog) run(); + + is better written: + + %% + mouse/cat|dog run(); + rat/cat|dog run(); + + or as + + %% + mouse|rat/cat run(); + mouse|rat/dog run(); + + Note that here the special '|' action does not provide any savings, and + can even make things worse (see BUGS in flex(1)). + + Another area where the user can increase a scanner's performance (and + one that's easier to implement) arises from the fact that the longer the + tokens matched, the faster the scanner will run. This is because with + long tokens the processing of most input characters takes place in the + (short) inner scanning loop, and does not often have to go through the + additional work of setting up the scanning environment (e.g., yytext) + for the action. Recall the scanner for C comments: + + 24 Version 2.3 + + 26 May 1990 FLEX(1) + + %x comment + %% + int line_num = 1; + + "/*" BEGIN(comment); + + [^*\n]* + "*"+[^*/\n]* + \n ++line_num; + "*"+"/" BEGIN(INITIAL); + + This could be sped up by writing it as: + + %x comment + %% + int line_num = 1; + + "/*" BEGIN(comment); + + [^*\n]* + [^*\n]*\n ++line_num; + "*"+[^*/\n]* + "*"+[^*/\n]*\n ++line_num; + "*"+"/" BEGIN(INITIAL); + + Now instead of each newline requiring the processing of another action, + recognizing the newlines is "distributed" over the other rules to keep + the matched text as long as possible. Note that adding rules does not + slow down the scanner! The speed of the scanner is independent of the + number of rules or (modulo the considerations given at the beginning of + this section) how complicated the rules are with regard to operators + such as '*' and '|'. + + A final example in speeding up a scanner: suppose you want to scan + through a file containing identifiers and keywords, one per line and + with no other extraneous characters, and recognize all the keywords. A + natural first approach is: + + %% + asm | + auto | + break | + ... etc ... + volatile | + while /* it's a keyword */ + + .|\n /* it's not a keyword */ + + To eliminate the back-tracking, introduce a catch-all rule: + + %% + asm | + auto | + + Version 2.3 25 + + FLEX(1) 26 May 1990 + + break | + ... etc ... + volatile | + while /* it's a keyword */ + + [a-z]+ | + .|\n /* it's not a keyword */ + + Now, if it's guaranteed that there's exactly one word per line, then we + can reduce the total number of matches by a half by merging in the + recognition of newlines with that of the other tokens: + + %% + asm\n | + auto\n | + break\n | + ... etc ... + volatile\n | + while\n /* it's a keyword */ + + [a-z]+\n | + .|\n /* it's not a keyword */ + + One has to be careful here, as we have now reintroduced backtracking + into the scanner. In particular, while we know that there will never be + any characters in the input stream other than letters or newlines, flex + can't figure this out, and it will plan for possibly needing backtrack- + ing when it has scanned a token like "auto" and then the next character + is something other than a newline or a letter. Previously it would then + just match the "auto" rule and be done, but now it has no "auto" rule, + only a "auto\n" rule. To eliminate the possibility of backtracking, we + could either duplicate all rules but without final newlines, or, since + we never expect to encounter such an input and therefore don't how it's + classified, we can introduce one more catch-all rule, this one which + doesn't include a newline: + + %% + asm\n | + auto\n | + break\n | + ... etc ... + volatile\n | + while\n /* it's a keyword */ + + [a-z]+\n | + [a-z]+ | + .|\n /* it's not a keyword */ + + Compiled with -Cf, this is about as fast as one can get a flex scanner + to go for this particular problem. + + A final note: flex is slow when matching NUL's, particularly when a + token contains multiple NUL's. It's best to write rules which match + short amounts of text if it's anticipated that the text will often + + 26 Version 2.3 + + 26 May 1990 FLEX(1) + + include NUL's. + + INCOMPATIBILITIES WITH LEX AND POSIX + flex is a rewrite of the Unix lex tool (the two implementations do not + share any code, though), with some extensions and incompatibilities, + both of which are of concern to those who wish to write scanners accept- + able to either implementation. At present, the POSIX lex draft is very + close to the original lex implementation, so some of these incompatibil- + ities are also in conflict with the POSIX draft. But the intent is that + except as noted below, flex as it presently stands will ultimately be + POSIX conformant (i.e., that those areas of conflict with the POSIX + draft will be resolved in flex's favor). Please bear in mind that all + the comments which follow are with regard to the POSIX draft standard of + Summer 1989, and not the final document (or subsequent drafts); they are + included so flex users can be aware of the standardization issues and + those areas where flex may in the near future undergo changes incompati- + ble with its current definition. + + flex is fully compatible with lex with the following exceptions: + + - The undocumented lex scanner internal variable yylineno is not sup- + ported. It is difficult to support this option efficiently, since + it requires examining every character scanned and reexamining the + characters when the scanner backs up. Things get more complicated + when the end of buffer or file is reached or a NUL is scanned + (since the scan must then be restarted with the proper line number + count), or the user uses the yyless(), unput(), or REJECT actions, + or the multiple input buffer functions. + + The fix is to add rules which, upon seeing a newline, increment + yylineno. This is usually an easy process, though it can be a drag + if some of the patterns can match multiple newlines along with + other characters. + + yylineno is not part of the POSIX draft. + + - The input() routine is not redefinable, though it may be called to + read characters following whatever has been matched by a rule. If + input() encounters an end-of-file the normal yywrap() processing is + done. A ``real'' end-of-file is returned by input() as EOF. + + Input is instead controlled by redefining the YY_INPUT macro. + + The flex restriction that input() cannot be redefined is in accor- + dance with the POSIX draft, but YY_INPUT has not yet been accepted + into the draft (and probably won't; it looks like the draft will + simply not specify any way of controlling the scanner's input other + than by making an initial assignment to yyin). + + - flex scanners do not use stdio for input. Because of this, when + writing an interactive scanner one must explicitly call fflush() on + the stream associated with the terminal after writing out a prompt. + With lex such writes are automatically flushed since lex scanners + use getchar() for their input. Also, when writing interactive + + Version 2.3 27 + + FLEX(1) 26 May 1990 + + scanners with flex, the -I flag must be used. + + - flex scanners are not as reentrant as lex scanners. In particular, + if you have an interactive scanner and an interrupt handler which + long-jumps out of the scanner, and the scanner is subsequently + called again, you may get the following message: + + fatal flex scanner internal error--end of buffer missed + + To reenter the scanner, first use + + yyrestart( yyin ); + + - output() is not supported. Output from the ECHO macro is done to + the file-pointer yyout (default stdout). + + The POSIX draft mentions that an output() routine exists but + currently gives no details as to what it does. + + - lex does not support exclusive start conditions (%x), though they + are in the current POSIX draft. + + - When definitions are expanded, flex encloses them in parentheses. + With lex, the following: + + NAME [A-Z][A-Z0-9]* + %% + foo{NAME}? printf( "Found it\n" ); + %% + + will not match the string "foo" because when the macro is expanded + the rule is equivalent to "foo[A-Z][A-Z0-9]*?" and the precedence + is such that the '?' is associated with "[A-Z0-9]*". With flex, + the rule will be expanded to "foo([A-Z][A-Z0-9]*)?" and so the + string "foo" will match. Note that because of this, the ^, $, , + /, and <> operators cannot be used in a flex definition. + + The POSIX draft interpretation is the same as flex's. + + - To specify a character class which matches anything but a left + bracket (']'), in lex one can use "[^]]" but with flex one must use + "[^\]]". The latter works with lex, too. + + - The lex %r (generate a Ratfor scanner) option is not supported. It + is not part of the POSIX draft. + + - If you are providing your own yywrap() routine, you must include a + "#undef yywrap" in the definitions section (section 1). Note that + the "#undef" will have to be enclosed in %{}'s. + + The POSIX draft specifies that yywrap() is a function and this is + very unlikely to change; so flex users are warned that yywrap() is + likely to be changed to a function in the near future. + + 28 Version 2.3 + + 26 May 1990 FLEX(1) + + - After a call to unput(), yytext and yyleng are undefined until the + next token is matched. This is not the case with lex or the + present POSIX draft. + + - The precedence of the {} (numeric range) operator is different. + lex interprets "abc{1,3}" as "match one, two, or three occurrences + of 'abc'", whereas flex interprets it as "match 'ab' followed by + one, two, or three occurrences of 'c'". The latter is in agreement + with the current POSIX draft. + + - The precedence of the ^ operator is different. lex interprets + "^foo|bar" as "match either 'foo' at the beginning of a line, or + 'bar' anywhere", whereas flex interprets it as "match either 'foo' + or 'bar' if they come at the beginning of a line". The latter is + in agreement with the current POSIX draft. + + - To refer to yytext outside of the scanner source file, the correct + definition with flex is "extern char *yytext" rather than "extern + char yytext[]". This is contrary to the current POSIX draft but a + point on which flex will not be changing, as the array representa- + tion entails a serious performance penalty. It is hoped that the + POSIX draft will be emended to support the flex variety of declara- + tion (as this is a fairly painless change to require of lex users). + + - yyin is initialized by lex to be stdin; flex, on the other hand, + initializes yyin to NULL and then assigns it to stdin the first + time the scanner is called, providing yyin has not already been + assigned to a non-NULL value. The difference is subtle, but the + net effect is that with flex scanners, yyin does not have a valid + value until the scanner has been called. + + - The special table-size declarations such as %a supported by lex are + not required by flex scanners; flex ignores them. + + - The name FLEX_SCANNER is #define'd so scanners may be written for + use with either flex or lex. + + The following flex features are not included in lex or the POSIX draft + standard: + + yyterminate() + <> + YY_DECL + #line directives + %{}'s around actions + yyrestart() + comments beginning with '#' (deprecated) + multiple actions on a line + + This last feature refers to the fact that with flex you can put multiple + actions on the same line, separated with semi-colons, while with lex, + the following + + foo handle_foo(); ++num_foos_seen; + + Version 2.3 29 + + FLEX(1) 26 May 1990 + + is (rather surprisingly) truncated to + + foo handle_foo(); + + flex does not truncate the action. Actions that are not enclosed in + braces are simply terminated at the end of the line. + + DIAGNOSTICS + reject_used_but_not_detected undefined or yymore_used_but_not_detected + undefined - These errors can occur at compile time. They indicate that + the scanner uses REJECT or yymore() but that flex failed to notice the + fact, meaning that flex scanned the first two sections looking for + occurrences of these actions and failed to find any, but somehow you + snuck some in (via a #include file, for example). Make an explicit + reference to the action in your flex input file. (Note that previously + flex supported a %used/%unused mechanism for dealing with this problem; + this feature is still supported but now deprecated, and will go away + soon unless the author hears from people who can argue compellingly that + they need it.) + + flex scanner jammed - a scanner compiled with -s has encountered an + input string which wasn't matched by any of its rules. + + flex input buffer overflowed - a scanner rule matched a string long + enough to overflow the scanner's internal input buffer (16K bytes by + default - controlled by YY_BUF_SIZE in "flex.skel". Note that to rede- + fine this macro, you must first #undefine it). + + scanner requires -8 flag - Your scanner specification includes recogniz- + ing 8-bit characters and you did not specify the -8 flag (and your site + has not installed flex with -8 as the default). + + fatal flex scanner internal error--end of buffer missed - This can occur + in an scanner which is reentered after a long-jump has jumped out (or + over) the scanner's activation frame. Before reentering the scanner, + use: + + yyrestart( yyin ); + + too many %t classes! - You managed to put every single character into + its own %t class. flex requires that at least one of the classes share + characters. + + DEFICIENCIES / BUGS + See flex(1). + + SEE ALSO + + flex(1), lex(1), yacc(1), sed(1), awk(1). + + M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator + + 30 Version 2.3 + + 26 May 1990 FLEX(1) + + AUTHOR + Vern Paxson, with the help of many ideas and much inspiration from Van + Jacobson. Original version by Jef Poskanzer. The fast table represen- + tation is a partial implementation of a design done by Van Jacobson. + The implementation was done by Kevin Gong and Vern Paxson. + + Thanks to the many flex beta-testers, feedbackers, and contributors, + especially Casey Leedom, benson@odi.com, Keith Bostic, Frederic Brehm, + Nick Christopher, Jason Coughlin, Scott David Daniels, Leo Eskin, Chris + Faylor, Eric Goldman, Eric Hughes, Jeffrey R. Jones, Kevin B. Kenny, + Ronald Lamprecht, Greg Lee, Craig Leres, Mohamed el Lozy, Jim Meyering, + Marc Nozell, Esmond Pitt, Jef Poskanzer, Jim Roskind, Dave Tallman, + Frank Whaley, Ken Yap, and those whose names have slipped my marginal + mail-archiving skills but whose contributions are appreciated all the + same. + + Thanks to Keith Bostic, John Gilmore, Craig Leres, Bob Mulcahy, Rich + Salz, and Richard Stallman for help with various distribution headaches. + + Thanks to Esmond Pitt and Earle Horton for 8-bit character support; to + Benson Margulies and Fred Burke for C++ support; to Ove Ewerlid for the + basics of support for NUL's; and to Eric Hughes for the basics of sup- + port for multiple buffers. + + Work is being done on extending flex to generate scanners in which the + state machine is directly represented in C code rather than tables. + These scanners may well be substantially faster than those generated + using -f or -F. If you are working in this area and are interested in + comparing notes and seeing whether redundant work can be avoided, con- + tact Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE). + + This work was primarily done when I was at the Real Time Systems Group + at the Lawrence Berkeley Laboratory in Berkeley, CA. Many thanks to all + there for the support I received. + + Send comments to: + + Vern Paxson + Computer Science Department + 4126 Upson Hall + Cornell University + Ithaca, NY 14853-7501 + + vern@cs.cornell.edu + decvax!cornell!vern + + Version 2.3 31 + +99 diff --git a/src/toolsComm/flex/Makefile b/src/toolsComm/flex/Makefile new file mode 100644 index 000000000..6086e0ff9 --- /dev/null +++ b/src/toolsComm/flex/Makefile @@ -0,0 +1,44 @@ +#************************************************************************* +# 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../.. + +include $(TOP)/configure/CONFIG + +HOST_WARN=NO + +YACC = $(EYACC) +YACCOPT = -l -d + +SKELETON_FILE = include/flex.skel.static + +USR_CPPFLAGS = -DDEFAULT_SKELETON_FILE=$(SKELETON_FILE) + +INC += flex.skel.static + +# main.c is included in parse.c +# +SRCS += ccl.c +SRCS += dfa.c +SRCS += ecs.c +SRCS += gen.c +SRCS += misc.c +SRCS += nfa.c +SRCS += sym.c +SRCS += tblcmp.c +SRCS += parse.c + +PROD_HOST = e_flex +PROD_LIBS = Com + +include $(TOP)/configure/RULES + +clean:: + $(RM) parse.c parse.h + +# EOF Makefile.Host for base/src/toolsComm/flex diff --git a/src/toolsComm/flex/README b/src/toolsComm/flex/README new file mode 100644 index 000000000..eab2da318 --- /dev/null +++ b/src/toolsComm/flex/README @@ -0,0 +1,78 @@ +// Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + +This is release 2.3 of flex - a full release. + +The flex distribution consists of the following files: + + README This message + + Makefile + flexdef.h + parse.y + scan.l + ccl.c + dfa.c + ecs.c flex sources + gen.c + main.c + misc.c + nfa.c + sym.c + tblcmp.c + yylex.c + + libmain.c flex library (-lfl) source + + initscan.c pre-flex'd version of scan.l + + flex.skel skeleton for generated scanners + + flexdoc.1 full user documentation + flex.1 reference documentation + + Changes Differences between this release and the previous one + + COPYING flex's copyright + + MISC/ a directory containing miscellaneous porting-related + notes (for Atari, MS-DOS, Turbo-C, and VMS) + + +Decide where you want to keep flex.skel (suggestion: /usr/local/lib), +but don't move it there yet. Edit "Makefile" and change the definition +of SKELETON_FILE to reflect the full pathname of flex.skel. + +Read the "Porting considerations" note in the Makefile and make +the necessary changes. + +To make flex for the first time, use: + + make first_flex + +which uses the pre-generated copy of the flex scanner (the scanner +itself is written using flex). + +Assuming it builds successfully, you can test it using + + make test + +The "diff" should not show any differences. + +If you're feeling adventurous, issue "make bigtest" and be prepared +to wait a while. + +Install flex using: + + make install + + +Please send problems and feedback to: + + vern@cs.cornell.edu + decvax!cornell!vern + + Vern Paxson + CS Department + 4126 Upson Hall + Cornell University + Ithaca, NY 14853-7501 diff --git a/src/toolsComm/flex/ccl.c b/src/toolsComm/flex/ccl.c new file mode 100644 index 000000000..572178bcb --- /dev/null +++ b/src/toolsComm/flex/ccl.c @@ -0,0 +1,174 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ccl - routines for character classes */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + +/* ccladd - add a single character to a ccl + * + * synopsis + * int cclp; + * int ch; + * ccladd( cclp, ch ); + */ + +void ccladd(int cclp, int ch) +{ + int ind, len, newpos, i; + + len = ccllen[cclp]; + ind = cclmap[cclp]; + + /* check to see if the character is already in the ccl */ + + for ( i = 0; i < len; ++i ) + if ( ccltbl[ind + i] == ch ) + return; + + newpos = ind + len; + + if ( newpos >= current_max_ccl_tbl_size ) + { + current_max_ccl_tbl_size += MAX_CCL_TBL_SIZE_INCREMENT; + + ++num_reallocs; + + ccltbl = reallocate_character_array( ccltbl, current_max_ccl_tbl_size ); + } + + ccllen[cclp] = len + 1; + ccltbl[newpos] = ch; + } + + +/* cclinit - make an empty ccl + * + * synopsis + * int cclinit(); + * new_ccl = cclinit(); + */ + +int cclinit(void) +{ + if ( ++lastccl >= current_maxccls ) + { + current_maxccls += MAX_CCLS_INCREMENT; + + ++num_reallocs; + + cclmap = reallocate_integer_array( cclmap, current_maxccls ); + ccllen = reallocate_integer_array( ccllen, current_maxccls ); + cclng = reallocate_integer_array( cclng, current_maxccls ); + } + + if ( lastccl == 1 ) + /* we're making the first ccl */ + cclmap[lastccl] = 0; + + else + /* the new pointer is just past the end of the last ccl. Since + * the cclmap points to the \first/ character of a ccl, adding the + * length of the ccl to the cclmap pointer will produce a cursor + * to the first free space + */ + cclmap[lastccl] = cclmap[lastccl - 1] + ccllen[lastccl - 1]; + + ccllen[lastccl] = 0; + cclng[lastccl] = 0; /* ccl's start out life un-negated */ + + return ( lastccl ); + } + + +/* cclnegate - negate a ccl + * + * synopsis + * int cclp; + * cclnegate( ccl ); + */ + +void cclnegate(int cclp) +{ + cclng[cclp] = 1; + } + + +/* list_character_set - list the members of a set of characters in CCL form + * + * synopsis + * int cset[CSIZE]; + * FILE *file; + * list_character_set( cset ); + * + * writes to the given file a character-class representation of those + * characters present in the given set. A character is present if it + * has a non-zero value in the set array. + */ + +void list_character_set(FILE *file, int cset[]) +{ + int i; + char *readable_form(); + + putc( '[', file ); + + for ( i = 0; i < csize; ++i ) + { + if ( cset[i] ) + { + int start_char = i; + + putc( ' ', file ); + + fputs( readable_form( i ), file ); + + while ( ++i < csize && cset[i] ) + ; + + if ( i - 1 > start_char ) + /* this was a run */ + fprintf( file, "-%s", readable_form( i - 1 ) ); + + putc( ' ', file ); + } + } + + putc( ']', file ); + } diff --git a/src/toolsComm/flex/dfa.c b/src/toolsComm/flex/dfa.c new file mode 100644 index 000000000..5eedbb847 --- /dev/null +++ b/src/toolsComm/flex/dfa.c @@ -0,0 +1,1061 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* dfa - DFA construction routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +void dump_associated_rules (FILE*, int); +void dump_transitions (FILE*, int[]); +void sympartition (int[], int, int[], int[]); +int symfollowset (int[], int, int, int[]); + + +/* check_for_backtracking - check a DFA state for backtracking + * + * synopsis + * int ds, state[numecs]; + * check_for_backtracking( ds, state ); + * + * ds is the number of the state to check and state[] is its out-transitions, + * indexed by equivalence class, and state_rules[] is the set of rules + * associated with this state + */ + +void check_for_backtracking(int ds, int state[]) +{ + if ( (reject && ! dfaacc[ds].dfaacc_set) || ! dfaacc[ds].dfaacc_state ) + { /* state is non-accepting */ + ++num_backtracking; + + if ( backtrack_report ) + { + fprintf( backtrack_file, "State #%d is non-accepting -\n", ds ); + + /* identify the state */ + dump_associated_rules( backtrack_file, ds ); + + /* now identify it further using the out- and jam-transitions */ + dump_transitions( backtrack_file, state ); + + putc( '\n', backtrack_file ); + } + } + } + + +/* check_trailing_context - check to see if NFA state set constitutes + * "dangerous" trailing context + * + * synopsis + * int nfa_states[num_states+1], num_states; + * int accset[nacc+1], nacc; + * check_trailing_context( nfa_states, num_states, accset, nacc ); + * + * NOTES + * Trailing context is "dangerous" if both the head and the trailing + * part are of variable size \and/ there's a DFA state which contains + * both an accepting state for the head part of the rule and NFA states + * which occur after the beginning of the trailing context. + * When such a rule is matched, it's impossible to tell if having been + * in the DFA state indicates the beginning of the trailing context + * or further-along scanning of the pattern. In these cases, a warning + * message is issued. + * + * nfa_states[1 .. num_states] is the list of NFA states in the DFA. + * accset[1 .. nacc] is the list of accepting numbers for the DFA state. + */ + +void check_trailing_context(int *nfa_states, int num_states, int *accset, int nacc) +{ + int i, j; + + for ( i = 1; i <= num_states; ++i ) + { + int ns = nfa_states[i]; + int type = state_type[ns]; + int ar = assoc_rule[ns]; + + if ( type == STATE_NORMAL || rule_type[ar] != RULE_VARIABLE ) + { /* do nothing */ + } + + else if ( type == STATE_TRAILING_CONTEXT ) + { + /* potential trouble. Scan set of accepting numbers for + * the one marking the end of the "head". We assume that + * this looping will be fairly cheap since it's rare that + * an accepting number set is large. + */ + for ( j = 1; j <= nacc; ++j ) + if ( accset[j] & YY_TRAILING_HEAD_MASK ) + { + fprintf( stderr, + "%s: Dangerous trailing context in rule at line %d\n", + program_name, rule_linenum[ar] ); + return; + } + } + } + } + + +/* dump_associated_rules - list the rules associated with a DFA state + * + * synopisis + * int ds; + * FILE *file; + * dump_associated_rules( file, ds ); + * + * goes through the set of NFA states associated with the DFA and + * extracts the first MAX_ASSOC_RULES unique rules, sorts them, + * and writes a report to the given file + */ + +void dump_associated_rules(FILE *file, int ds) +{ + int i, j; + int num_associated_rules = 0; + int rule_set[MAX_ASSOC_RULES + 1]; + int *dset = dss[ds]; + int size = dfasiz[ds]; + + for ( i = 1; i <= size; ++i ) + { + int rule_num = rule_linenum[assoc_rule[dset[i]]]; + + for ( j = 1; j <= num_associated_rules; ++j ) + if ( rule_num == rule_set[j] ) + break; + + if ( j > num_associated_rules ) + { /* new rule */ + if ( num_associated_rules < MAX_ASSOC_RULES ) + rule_set[++num_associated_rules] = rule_num; + } + } + + bubble( rule_set, num_associated_rules ); + + fprintf( file, " associated rule line numbers:" ); + + for ( i = 1; i <= num_associated_rules; ++i ) + { + if ( i % 8 == 1 ) + putc( '\n', file ); + + fprintf( file, "\t%d", rule_set[i] ); + } + + putc( '\n', file ); + } + + +/* dump_transitions - list the transitions associated with a DFA state + * + * synopisis + * int state[numecs]; + * FILE *file; + * dump_transitions( file, state ); + * + * goes through the set of out-transitions and lists them in human-readable + * form (i.e., not as equivalence classes); also lists jam transitions + * (i.e., all those which are not out-transitions, plus EOF). The dump + * is done to the given file. + */ + +void dump_transitions(FILE *file, int state[]) +{ + int i, ec; + int out_char_set[CSIZE]; + + for ( i = 0; i < csize; ++i ) + { + ec = abs( ecgroup[i] ); + out_char_set[i] = state[ec]; + } + + fprintf( file, " out-transitions: " ); + + list_character_set( file, out_char_set ); + + /* now invert the members of the set to get the jam transitions */ + for ( i = 0; i < csize; ++i ) + out_char_set[i] = ! out_char_set[i]; + + fprintf( file, "\n jam-transitions: EOF " ); + + list_character_set( file, out_char_set ); + + putc( '\n', file ); + } + + +/* epsclosure - construct the epsilon closure of a set of ndfa states + * + * synopsis + * int t[current_max_dfa_size], numstates, accset[num_rules + 1], nacc; + * int hashval; + * int *epsclosure(); + * t = epsclosure( t, &numstates, accset, &nacc, &hashval ); + * + * NOTES + * the epsilon closure is the set of all states reachable by an arbitrary + * number of epsilon transitions which themselves do not have epsilon + * transitions going out, unioned with the set of states which have non-null + * accepting numbers. t is an array of size numstates of nfa state numbers. + * Upon return, t holds the epsilon closure and numstates is updated. accset + * holds a list of the accepting numbers, and the size of accset is given + * by nacc. t may be subjected to reallocation if it is not large enough + * to hold the epsilon closure. + * + * hashval is the hash value for the dfa corresponding to the state set + */ + +int *epsclosure(int *t, int *ns_addr, int accset[], int *nacc_addr, int *hv_addr) +{ + int stkpos, ns, tsp; + int numstates = *ns_addr, nacc, hashval, transsym, nfaccnum; + int stkend, nstate; + static int did_stk_init = false, *stk; + +#define MARK_STATE(state) \ + trans1[state] = trans1[state] - MARKER_DIFFERENCE; + +#define IS_MARKED(state) (trans1[state] < 0) + +#define UNMARK_STATE(state) \ + trans1[state] = trans1[state] + MARKER_DIFFERENCE; + +#define CHECK_ACCEPT(state) \ + { \ + nfaccnum = accptnum[state]; \ + if ( nfaccnum != NIL ) \ + accset[++nacc] = nfaccnum; \ + } + +#define DO_REALLOCATION \ + { \ + current_max_dfa_size += MAX_DFA_SIZE_INCREMENT; \ + ++num_reallocs; \ + t = reallocate_integer_array( t, current_max_dfa_size ); \ + stk = reallocate_integer_array( stk, current_max_dfa_size ); \ + } \ + +#define PUT_ON_STACK(state) \ + { \ + if ( ++stkend >= current_max_dfa_size ) \ + DO_REALLOCATION \ + stk[stkend] = state; \ + MARK_STATE(state) \ + } + +#define ADD_STATE(state) \ + { \ + if ( ++numstates >= current_max_dfa_size ) \ + DO_REALLOCATION \ + t[numstates] = state; \ + hashval = hashval + state; \ + } + +#define STACK_STATE(state) \ + { \ + PUT_ON_STACK(state) \ + CHECK_ACCEPT(state) \ + if ( nfaccnum != NIL || transchar[state] != SYM_EPSILON ) \ + ADD_STATE(state) \ + } + + if ( ! did_stk_init ) + { + stk = allocate_integer_array( current_max_dfa_size ); + did_stk_init = true; + } + + nacc = stkend = hashval = 0; + + for ( nstate = 1; nstate <= numstates; ++nstate ) + { + ns = t[nstate]; + + /* the state could be marked if we've already pushed it onto + * the stack + */ + if ( ! IS_MARKED(ns) ) + PUT_ON_STACK(ns) + + CHECK_ACCEPT(ns) + hashval = hashval + ns; + } + + for ( stkpos = 1; stkpos <= stkend; ++stkpos ) + { + ns = stk[stkpos]; + transsym = transchar[ns]; + + if ( transsym == SYM_EPSILON ) + { + tsp = trans1[ns] + MARKER_DIFFERENCE; + + if ( tsp != NO_TRANSITION ) + { + if ( ! IS_MARKED(tsp) ) + STACK_STATE(tsp) + + tsp = trans2[ns]; + + if ( tsp != NO_TRANSITION ) + if ( ! IS_MARKED(tsp) ) + STACK_STATE(tsp) + } + } + } + + /* clear out "visit" markers */ + + for ( stkpos = 1; stkpos <= stkend; ++stkpos ) + { + if ( IS_MARKED(stk[stkpos]) ) + { + UNMARK_STATE(stk[stkpos]) + } + else + flexfatal( "consistency check failed in epsclosure()" ); + } + + *ns_addr = numstates; + *hv_addr = hashval; + *nacc_addr = nacc; + + return ( t ); + } + + +/* increase_max_dfas - increase the maximum number of DFAs */ + +void increase_max_dfas(void) +{ + current_max_dfas += MAX_DFAS_INCREMENT; + + ++num_reallocs; + + base = reallocate_integer_array( base, current_max_dfas ); + def = reallocate_integer_array( def, current_max_dfas ); + dfasiz = reallocate_integer_array( dfasiz, current_max_dfas ); + accsiz = reallocate_integer_array( accsiz, current_max_dfas ); + dhash = reallocate_integer_array( dhash, current_max_dfas ); + dss = reallocate_int_ptr_array( dss, current_max_dfas ); + dfaacc = reallocate_dfaacc_union( dfaacc, current_max_dfas ); + + if ( nultrans ) + nultrans = reallocate_integer_array( nultrans, current_max_dfas ); + } + + +/* ntod - convert an ndfa to a dfa + * + * synopsis + * ntod(); + * + * creates the dfa corresponding to the ndfa we've constructed. the + * dfa starts out in state #1. + */ + +void ntod(void) +{ + int *accset, ds, nacc, newds; + int sym, hashval, numstates, dsize; + int num_full_table_rows = 0; /* used only for -f */ + int *nset, *dset; + int targptr, totaltrans, i, comstate, comfreq, targ; + int *epsclosure(int *t, int *ns_addr, int *accset, int *nacc_addr, int *hv_addr); + int snstods(int *sns, int numstates, int *accset, int nacc, int hashval, int *newds_addr); + int symlist[CSIZE + 1]; + int num_start_states; + int todo_head, todo_next; + + /* note that the following are indexed by *equivalence classes* + * and not by characters. Since equivalence classes are indexed + * beginning with 1, even if the scanner accepts NUL's, this + * means that (since every character is potentially in its own + * equivalence class) these arrays must have room for indices + * from 1 to CSIZE, so their size must be CSIZE + 1. + */ + int duplist[CSIZE + 1], state[CSIZE + 1]; + int targfreq[CSIZE + 1], targstate[CSIZE + 1]; + + /* this is so find_table_space(...) will know where to start looking in + * chk/nxt for unused records for space to put in the state + */ + if ( fullspd ) + firstfree = 0; + + accset = allocate_integer_array( num_rules + 1 ); + nset = allocate_integer_array( current_max_dfa_size ); + + /* the "todo" queue is represented by the head, which is the DFA + * state currently being processed, and the "next", which is the + * next DFA state number available (not in use). We depend on the + * fact that snstods() returns DFA's \in increasing order/, and thus + * need only know the bounds of the dfas to be processed. + */ + todo_head = todo_next = 0; + + for ( i = 0; i <= csize; ++i ) + { + duplist[i] = NIL; + symlist[i] = false; + } + + for ( i = 0; i <= num_rules; ++i ) + accset[i] = NIL; + + if ( trace ) + { + dumpnfa( scset[1] ); + fputs( "\n\nDFA Dump:\n\n", stderr ); + } + + inittbl(); + + /* check to see whether we should build a separate table for transitions + * on NUL characters. We don't do this for full-speed (-F) scanners, + * since for them we don't have a simple state number lying around with + * which to index the table. We also don't bother doing it for scanners + * unless (1) NUL is in its own equivalence class (indicated by a + * positive value of ecgroup[NUL]), (2) NUL's equilvalence class is + * the last equivalence class, and (3) the number of equivalence classes + * is the same as the number of characters. This latter case comes about + * when useecs is false or when its true but every character still + * manages to land in its own class (unlikely, but it's cheap to check + * for). If all these things are true then the character code needed + * to represent NUL's equivalence class for indexing the tables is + * going to take one more bit than the number of characters, and therefore + * we won't be assured of being able to fit it into a YY_CHAR variable. + * This rules out storing the transitions in a compressed table, since + * the code for interpreting them uses a YY_CHAR variable (perhaps it + * should just use an integer, though; this is worth pondering ... ###). + * + * Finally, for full tables, we want the number of entries in the + * table to be a power of two so the array references go fast (it + * will just take a shift to compute the major index). If encoding + * NUL's transitions in the table will spoil this, we give it its + * own table (note that this will be the case if we're not using + * equivalence classes). + */ + + /* note that the test for ecgroup[0] == numecs below accomplishes + * both (1) and (2) above + */ + if ( ! fullspd && ecgroup[0] == numecs ) + { /* NUL is alone in its equivalence class, which is the last one */ + int use_NUL_table = (numecs == csize); + + if ( fulltbl && ! use_NUL_table ) + { /* we still may want to use the table if numecs is a power of 2 */ + int power_of_two; + + for ( power_of_two = 1; power_of_two <= csize; power_of_two *= 2 ) + if ( numecs == power_of_two ) + { + use_NUL_table = true; + break; + } + } + + if ( use_NUL_table ) + nultrans = allocate_integer_array( current_max_dfas ); + /* from now on, nultrans != nil indicates that we're + * saving null transitions for later, separate encoding + */ + } + + + if ( fullspd ) + { + for ( i = 0; i <= numecs; ++i ) + state[i] = 0; + place_state( state, 0, 0 ); + } + + else if ( fulltbl ) + { + if ( nultrans ) + /* we won't be including NUL's transitions in the table, + * so build it for entries from 0 .. numecs - 1 + */ + num_full_table_rows = numecs; + + else + /* take into account the fact that we'll be including + * the NUL entries in the transition table. Build it + * from 0 .. numecs. + */ + num_full_table_rows = numecs + 1; + + /* declare it "short" because it's a real long-shot that that + * won't be large enough. + */ + printf( "static short int yy_nxt[][%d] =\n {\n", + /* '}' so vi doesn't get too confused */ + num_full_table_rows ); + + /* generate 0 entries for state #0 */ + for ( i = 0; i < num_full_table_rows; ++i ) + mk2data( 0 ); + + /* force ',' and dataflush() next call to mk2data */ + datapos = NUMDATAITEMS; + + /* force extra blank line next dataflush() */ + dataline = NUMDATALINES; + } + + /* create the first states */ + + num_start_states = lastsc * 2; + + for ( i = 1; i <= num_start_states; ++i ) + { + numstates = 1; + + /* for each start condition, make one state for the case when + * we're at the beginning of the line (the '%' operator) and + * one for the case when we're not + */ + if ( i % 2 == 1 ) + nset[numstates] = scset[(i / 2) + 1]; + else + nset[numstates] = mkbranch( scbol[i / 2], scset[i / 2] ); + + nset = epsclosure( nset, &numstates, accset, &nacc, &hashval ); + + if ( snstods( nset, numstates, accset, nacc, hashval, &ds ) ) + { + numas += nacc; + totnst += numstates; + ++todo_next; + + if ( variable_trailing_context_rules && nacc > 0 ) + check_trailing_context( nset, numstates, accset, nacc ); + } + } + + if ( ! fullspd ) + { + if ( ! snstods( nset, 0, accset, 0, 0, &end_of_buffer_state ) ) + flexfatal( "could not create unique end-of-buffer state" ); + + ++numas; + ++num_start_states; + ++todo_next; + } + + while ( todo_head < todo_next ) + { + targptr = 0; + totaltrans = 0; + + for ( i = 1; i <= numecs; ++i ) + state[i] = 0; + + ds = ++todo_head; + + dset = dss[ds]; + dsize = dfasiz[ds]; + + if ( trace ) + fprintf( stderr, "state # %d:\n", ds ); + + sympartition( dset, dsize, symlist, duplist ); + + for ( sym = 1; sym <= numecs; ++sym ) + { + if ( symlist[sym] ) + { + symlist[sym] = 0; + + if ( duplist[sym] == NIL ) + { /* symbol has unique out-transitions */ + numstates = symfollowset( dset, dsize, sym, nset ); + nset = epsclosure( nset, &numstates, accset, + &nacc, &hashval ); + + if ( snstods( nset, numstates, accset, + nacc, hashval, &newds ) ) + { + totnst = totnst + numstates; + ++todo_next; + numas += nacc; + + if ( variable_trailing_context_rules && nacc > 0 ) + check_trailing_context( nset, numstates, + accset, nacc ); + } + + state[sym] = newds; + + if ( trace ) + fprintf( stderr, "\t%d\t%d\n", sym, newds ); + + targfreq[++targptr] = 1; + targstate[targptr] = newds; + ++numuniq; + } + + else + { + /* sym's equivalence class has the same transitions + * as duplist(sym)'s equivalence class + */ + targ = state[duplist[sym]]; + state[sym] = targ; + + if ( trace ) + fprintf( stderr, "\t%d\t%d\n", sym, targ ); + + /* update frequency count for destination state */ + + i = 0; + while ( targstate[++i] != targ ) + ; + + ++targfreq[i]; + ++numdup; + } + + ++totaltrans; + duplist[sym] = NIL; + } + } + + numsnpairs = numsnpairs + totaltrans; + + if ( caseins && ! useecs ) + { + int j; + + for ( i = 'A', j = 'a'; i <= 'Z'; ++i, ++j ) + state[i] = state[j]; + } + + if ( ds > num_start_states ) + check_for_backtracking( ds, state ); + + if ( nultrans ) + { + nultrans[ds] = state[NUL_ec]; + state[NUL_ec] = 0; /* remove transition */ + } + + if ( fulltbl ) + { + /* supply array's 0-element */ + if ( ds == end_of_buffer_state ) + mk2data( -end_of_buffer_state ); + else + mk2data( end_of_buffer_state ); + + for ( i = 1; i < num_full_table_rows; ++i ) + /* jams are marked by negative of state number */ + mk2data( state[i] ? state[i] : -ds ); + + /* force ',' and dataflush() next call to mk2data */ + datapos = NUMDATAITEMS; + + /* force extra blank line next dataflush() */ + dataline = NUMDATALINES; + } + + else if ( fullspd ) + place_state( state, ds, totaltrans ); + + else if ( ds == end_of_buffer_state ) + /* special case this state to make sure it does what it's + * supposed to, i.e., jam on end-of-buffer + */ + stack1( ds, 0, 0, JAMSTATE ); + + else /* normal, compressed state */ + { + /* determine which destination state is the most common, and + * how many transitions to it there are + */ + + comfreq = 0; + comstate = 0; + + for ( i = 1; i <= targptr; ++i ) + if ( targfreq[i] > comfreq ) + { + comfreq = targfreq[i]; + comstate = targstate[i]; + } + + bldtbl( state, ds, totaltrans, comstate, comfreq ); + } + } + + if ( fulltbl ) + dataend(); + + else if ( ! fullspd ) + { + cmptmps(); /* create compressed template entries */ + + /* create tables for all the states with only one out-transition */ + while ( onesp > 0 ) + { + mk1tbl( onestate[onesp], onesym[onesp], onenext[onesp], + onedef[onesp] ); + --onesp; + } + + mkdeftbl(); + } + } + + +/* snstods - converts a set of ndfa states into a dfa state + * + * synopsis + * int sns[numstates], numstates, newds, accset[num_rules + 1], nacc, hashval; + * int snstods(); + * is_new_state = snstods( sns, numstates, accset, nacc, hashval, &newds ); + * + * on return, the dfa state number is in newds. + */ + +int snstods(int sns[], int numstates, int accset[], int nacc, int hashval, int *newds_addr) +{ + int didsort = 0; + int i, j; + int newds, *oldsns; + + for ( i = 1; i <= lastdfa; ++i ) + if ( hashval == dhash[i] ) + { + if ( numstates == dfasiz[i] ) + { + oldsns = dss[i]; + + if ( ! didsort ) + { + /* we sort the states in sns so we can compare it to + * oldsns quickly. we use bubble because there probably + * aren't very many states + */ + bubble( sns, numstates ); + didsort = 1; + } + + for ( j = 1; j <= numstates; ++j ) + if ( sns[j] != oldsns[j] ) + break; + + if ( j > numstates ) + { + ++dfaeql; + *newds_addr = i; + return ( 0 ); + } + + ++hshcol; + } + + else + ++hshsave; + } + + /* make a new dfa */ + + if ( ++lastdfa >= current_max_dfas ) + increase_max_dfas(); + + newds = lastdfa; + + dss[newds] = (int *) malloc( (unsigned) ((numstates + 1) * sizeof( int )) ); + + if ( ! dss[newds] ) + flexfatal( "dynamic memory failure in snstods()" ); + + /* if we haven't already sorted the states in sns, we do so now, so that + * future comparisons with it can be made quickly + */ + + if ( ! didsort ) + bubble( sns, numstates ); + + for ( i = 1; i <= numstates; ++i ) + dss[newds][i] = sns[i]; + + dfasiz[newds] = numstates; + dhash[newds] = hashval; + + if ( nacc == 0 ) + { + if ( reject ) + dfaacc[newds].dfaacc_set = (int *) 0; + else + dfaacc[newds].dfaacc_state = 0; + + accsiz[newds] = 0; + } + + else if ( reject ) + { + /* we sort the accepting set in increasing order so the disambiguating + * rule that the first rule listed is considered match in the event of + * ties will work. We use a bubble sort since the list is probably + * quite small. + */ + + bubble( accset, nacc ); + + dfaacc[newds].dfaacc_set = + (int *) malloc( (unsigned) ((nacc + 1) * sizeof( int )) ); + + if ( ! dfaacc[newds].dfaacc_set ) + flexfatal( "dynamic memory failure in snstods()" ); + + /* save the accepting set for later */ + for ( i = 1; i <= nacc; ++i ) + dfaacc[newds].dfaacc_set[i] = accset[i]; + + accsiz[newds] = nacc; + } + + else + { /* find lowest numbered rule so the disambiguating rule will work */ + j = num_rules + 1; + + for ( i = 1; i <= nacc; ++i ) + if ( accset[i] < j ) + j = accset[i]; + + dfaacc[newds].dfaacc_state = j; + } + + *newds_addr = newds; + + return ( 1 ); + } + + +/* symfollowset - follow the symbol transitions one step + * + * synopsis + * int ds[current_max_dfa_size], dsize, transsym; + * int nset[current_max_dfa_size], numstates; + * numstates = symfollowset( ds, dsize, transsym, nset ); + */ + +int symfollowset(int ds[], int dsize, int transsym, int nset[]) +{ + int ns, tsp, sym, i, j, lenccl, ch, numstates; + int ccllist; + + numstates = 0; + + for ( i = 1; i <= dsize; ++i ) + { /* for each nfa state ns in the state set of ds */ + ns = ds[i]; + sym = transchar[ns]; + tsp = trans1[ns]; + + if ( sym < 0 ) + { /* it's a character class */ + sym = -sym; + ccllist = cclmap[sym]; + lenccl = ccllen[sym]; + + if ( cclng[sym] ) + { + for ( j = 0; j < lenccl; ++j ) + { /* loop through negated character class */ + ch = ccltbl[ccllist + j]; + + if ( ch == 0 ) + ch = NUL_ec; + + if ( ch > transsym ) + break; /* transsym isn't in negated ccl */ + + else if ( ch == transsym ) + /* next 2 */ goto bottom; + } + + /* didn't find transsym in ccl */ + nset[++numstates] = tsp; + } + + else + for ( j = 0; j < lenccl; ++j ) + { + ch = ccltbl[ccllist + j]; + + if ( ch == 0 ) + ch = NUL_ec; + + if ( ch > transsym ) + break; + + else if ( ch == transsym ) + { + nset[++numstates] = tsp; + break; + } + } + } + + else if ( sym >= 'A' && sym <= 'Z' && caseins ) + flexfatal( "consistency check failed in symfollowset" ); + + else if ( sym == SYM_EPSILON ) + { /* do nothing */ + } + + else if ( abs( ecgroup[sym] ) == transsym ) + nset[++numstates] = tsp; + +bottom: + ; + } + + return ( numstates ); + } + + +/* sympartition - partition characters with same out-transitions + * + * synopsis + * integer ds[current_max_dfa_size], numstates, duplist[numecs]; + * symlist[numecs]; + * sympartition( ds, numstates, symlist, duplist ); + */ + +void sympartition(int ds[], int numstates, int symlist[], int duplist[]) +{ + int tch, i, j, k, ns, dupfwd[CSIZE + 1], lenccl, cclp, ich; + + /* partitioning is done by creating equivalence classes for those + * characters which have out-transitions from the given state. Thus + * we are really creating equivalence classes of equivalence classes. + */ + + for ( i = 1; i <= numecs; ++i ) + { /* initialize equivalence class list */ + duplist[i] = i - 1; + dupfwd[i] = i + 1; + } + + duplist[1] = NIL; + dupfwd[numecs] = NIL; + + for ( i = 1; i <= numstates; ++i ) + { + ns = ds[i]; + tch = transchar[ns]; + + if ( tch != SYM_EPSILON ) + { + if ( tch < -lastccl || tch > csize ) + { + if ( tch > csize && tch <= CSIZE ) + flexerror( "scanner requires -8 flag" ); + + else + flexfatal( + "bad transition character detected in sympartition()" ); + } + + if ( tch >= 0 ) + { /* character transition */ + /* abs() needed for fake %t ec's */ + int ec = abs( ecgroup[tch] ); + + mkechar( ec, dupfwd, duplist ); + symlist[ec] = 1; + } + + else + { /* character class */ + tch = -tch; + + lenccl = ccllen[tch]; + cclp = cclmap[tch]; + mkeccl( ccltbl + cclp, lenccl, dupfwd, duplist, numecs, + NUL_ec ); + + if ( cclng[tch] ) + { + j = 0; + + for ( k = 0; k < lenccl; ++k ) + { + ich = ccltbl[cclp + k]; + + if ( ich == 0 ) + ich = NUL_ec; + + for ( ++j; j < ich; ++j ) + symlist[j] = 1; + } + + for ( ++j; j <= numecs; ++j ) + symlist[j] = 1; + } + + else + for ( k = 0; k < lenccl; ++k ) + { + ich = ccltbl[cclp + k]; + + if ( ich == 0 ) + ich = NUL_ec; + + symlist[ich] = 1; + } + } + } + } + } diff --git a/src/toolsComm/flex/ecs.c b/src/toolsComm/flex/ecs.c new file mode 100644 index 000000000..d6d1b44e9 --- /dev/null +++ b/src/toolsComm/flex/ecs.c @@ -0,0 +1,347 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* ecs - equivalence class routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + +/* ccl2ecl - convert character classes to set of equivalence classes + * + * synopsis + * ccl2ecl(); + */ + +void ccl2ecl(void) +{ + int i, ich, newlen, cclp, ccls, cclmec; + + for ( i = 1; i <= lastccl; ++i ) + { + /* we loop through each character class, and for each character + * in the class, add the character's equivalence class to the + * new "character" class we are creating. Thus when we are all + * done, character classes will really consist of collections + * of equivalence classes + */ + + newlen = 0; + cclp = cclmap[i]; + + for ( ccls = 0; ccls < ccllen[i]; ++ccls ) + { + ich = ccltbl[cclp + ccls]; + cclmec = ecgroup[ich]; + + if ( xlation && cclmec < 0 ) + { + /* special hack--if we're doing %t tables then it's + * possible that no representative of this character's + * equivalence class is in the ccl. So waiting till + * we see the representative would be disastrous. Instead, + * we add this character's equivalence class anyway, if it's + * not already present. + */ + int j; + + /* this loop makes this whole process n^2; but we don't + * really care about %t performance anyway + */ + for ( j = 0; j < newlen; ++j ) + if ( ccltbl[cclp + j] == -cclmec ) + break; + + if ( j >= newlen ) + { /* no representative yet, add this one in */ + ccltbl[cclp + newlen] = -cclmec; + ++newlen; + } + } + + else if ( cclmec > 0 ) + { + ccltbl[cclp + newlen] = cclmec; + ++newlen; + } + } + + ccllen[i] = newlen; + } + } + + +/* cre8ecs - associate equivalence class numbers with class members + * + * synopsis + * int cre8ecs(); + * number of classes = cre8ecs( fwd, bck, num ); + * + * fwd is the forward linked-list of equivalence class members. bck + * is the backward linked-list, and num is the number of class members. + * + * Returned is the number of classes. + */ + +int cre8ecs(int fwd[], int bck[], int num) +{ + int i, j, numcl; + + numcl = 0; + + /* create equivalence class numbers. From now on, abs( bck(x) ) + * is the equivalence class number for object x. If bck(x) + * is positive, then x is the representative of its equivalence + * class. + */ + for ( i = 1; i <= num; ++i ) + if ( bck[i] == NIL ) + { + bck[i] = ++numcl; + for ( j = fwd[i]; j != NIL; j = fwd[j] ) + bck[j] = -numcl; + } + + return ( numcl ); + } + + +/* ecs_from_xlation - associate equivalence class numbers using %t table + * + * synopsis + * numecs = ecs_from_xlation( ecmap ); + * + * Upon return, ecmap will map each character code to its equivalence + * class. The mapping will be positive if the character is the representative + * of its class, negative otherwise. + * + * Returns the number of equivalence classes used. + */ + +int ecs_from_xlation(int ecmap[]) +{ + int i; + int nul_is_alone = false; + int did_default_xlation_class = false; + + if ( xlation[0] != 0 ) + { + /* if NUL shares its translation with other characters, choose one + * of the other characters as the representative for the equivalence + * class. This allows a cheap test later to see whether we can + * do away with NUL's equivalence class. + */ + for ( i = 1; i < csize; ++i ) + if ( xlation[i] == -xlation[0] ) + { + xlation[i] = xlation[0]; + ecmap[0] = -xlation[0]; + break; + } + + if ( i >= csize ) + /* didn't find a companion character--remember this fact */ + nul_is_alone = true; + } + + for ( i = 1; i < csize; ++i ) + if ( xlation[i] == 0 ) + { + if ( did_default_xlation_class ) + ecmap[i] = -num_xlations; + + else + { + /* make an equivalence class for those characters not + * specified in the %t table + */ + ++num_xlations; + ecmap[i] = num_xlations; + did_default_xlation_class = true; + } + } + + else + ecmap[i] = xlation[i]; + + if ( nul_is_alone ) + /* force NUL's equivalence class to be the last one */ + { + ++num_xlations; + ecmap[0] = num_xlations; + + /* there's actually a bug here: if someone is fanatic enough to + * put every character in its own translation class, then right + * now we just promoted NUL's equivalence class to be csize + 1; + * we can handle NUL's class number being == csize (by instead + * putting it in its own table), but we can't handle some *other* + * character having to be put in its own table, too. So in + * this case we bail out. + */ + if ( num_xlations > csize ) + flexfatal( "too many %t classes!" ); + } + + return num_xlations; + } + + +/* mkeccl - update equivalence classes based on character class xtions + * + * synopsis + * Char ccls[]; + * int lenccl, fwd[llsiz], bck[llsiz], llsiz, NUL_mapping; + * mkeccl( ccls, lenccl, fwd, bck, llsiz, NUL_mapping ); + * + * where ccls contains the elements of the character class, lenccl is the + * number of elements in the ccl, fwd is the forward link-list of equivalent + * characters, bck is the backward link-list, and llsiz size of the link-list + * + * NUL_mapping is the value which NUL (0) should be mapped to. + */ + +void mkeccl(unsigned char ccls[], int lenccl, int fwd[], int bck[], int llsiz, int NUL_mapping) +{ + int cclp, oldec, newec; + int cclm, i, j; + static unsigned char cclflags[CSIZE]; /* initialized to all '\0' */ + + /* note that it doesn't matter whether or not the character class is + * negated. The same results will be obtained in either case. + */ + + cclp = 0; + + while ( cclp < lenccl ) + { + cclm = ccls[cclp]; + + if ( NUL_mapping && cclm == 0 ) + cclm = NUL_mapping; + + oldec = bck[cclm]; + newec = cclm; + + j = cclp + 1; + + for ( i = fwd[cclm]; i != NIL && i <= llsiz; i = fwd[i] ) + { /* look for the symbol in the character class */ + for ( ; j < lenccl; ++j ) + { + int ccl_char; + + if ( NUL_mapping && ccls[j] == 0 ) + ccl_char = NUL_mapping; + else + ccl_char = ccls[j]; + + if ( ccl_char > i ) + break; + + if ( ccl_char == i && ! cclflags[j] ) + { + /* we found an old companion of cclm in the ccl. + * link it into the new equivalence class and flag it as + * having been processed + */ + + bck[i] = newec; + fwd[newec] = i; + newec = i; + cclflags[j] = 1; /* set flag so we don't reprocess */ + + /* get next equivalence class member */ + /* continue 2 */ + goto next_pt; + } + } + + /* symbol isn't in character class. Put it in the old equivalence + * class + */ + + bck[i] = oldec; + + if ( oldec != NIL ) + fwd[oldec] = i; + + oldec = i; +next_pt: + ; + } + + if ( bck[cclm] != NIL || oldec != bck[cclm] ) + { + bck[cclm] = NIL; + fwd[oldec] = NIL; + } + + fwd[newec] = NIL; + + /* find next ccl member to process */ + + for ( ++cclp; cclflags[cclp] && cclp < lenccl; ++cclp ) + { + /* reset "doesn't need processing" flag */ + cclflags[cclp] = 0; + } + } + } + + +/* mkechar - create equivalence class for single character + * + * synopsis + * int tch, fwd[], bck[]; + * mkechar( tch, fwd, bck ); + */ + +void mkechar(int tch, int fwd[], int bck[]) +{ + /* if until now the character has been a proper subset of + * an equivalence class, break it away to create a new ec + */ + + if ( fwd[tch] != NIL ) + bck[fwd[tch]] = bck[tch]; + + if ( bck[tch] != NIL ) + fwd[bck[tch]] = fwd[tch]; + + fwd[tch] = NIL; + bck[tch] = NIL; + } diff --git a/src/toolsComm/flex/flex.html b/src/toolsComm/flex/flex.html new file mode 100644 index 000000000..bb8c240a3 --- /dev/null +++ b/src/toolsComm/flex/flex.html @@ -0,0 +1,620 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +
    +
    +
    +
    +

    NAME

    +     flex - fast lexical analyzer generator
    +
    +
    +
    +

    SYNOPSIS

    +     flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...]
    +
    +
    +
    +

    DESCRIPTION

    +     flex is a  tool  for  generating  scanners:  programs  which
    +     recognized  lexical  patterns in text.  flex reads the given
    +     input files, or its standard input  if  no  file  names  are
    +     given,  for  a  description  of  a scanner to generate.  The
    +     description is in the form of pairs of  regular  expressions
    +     and  C  code,  called  rules.  flex  generates as output a C
    +     source file, lex.yy.c, which defines a routine yylex(). This
    +     file is compiled and linked with the -lfl library to produce
    +     an executable.  When the executable is run, it analyzes  its
    +     input  for occurrences of the regular expressions.  Whenever
    +     it finds one, it executes the corresponding C code.
    +
    +     For full documentation, see flexdoc(1). This manual entry is
    +     intended for use as a quick reference.
    +
    +
    +
    +

    OPTIONS

    +     flex has the following options:
    +
    +     -b   Generate  backtracking  information  to  lex.backtrack.
    +          This  is  a  list of scanner states which require back-
    +          tracking and the input characters on which they do  so.
    +          By adding rules one can remove backtracking states.  If
    +          all backtracking states are eliminated and -f or -F  is
    +          used, the generated scanner will run faster.
    +
    +     -c   is a do-nothing, deprecated option included  for  POSIX
    +          compliance.
    +
    +          NOTE: in previous releases of flex -c specified  table-
    +          compression  options.   This functionality is now given
    +          by the -C flag.  To ease the the impact of this change,
    +          when  flex encounters -c, it currently issues a warning
    +          message and assumes that -C was  desired  instead.   In
    +          the future this "promotion" of -c to -C will go away in
    +          the name of full POSIX  compliance  (unless  the  POSIX
    +          meaning is removed first).
    +
    +     -d   makes the generated scanner run in debug  mode.   When-
    +          ever   a   pattern   is   recognized   and  the  global
    +          yy_flex_debug is non-zero (which is the  default),  the
    +          scanner will write to stderr a line of the form:
    +
    +              --accepting rule at line 53 ("the matched text")
    +
    +          The line number refers to the location of the  rule  in
    +          the  file defining the scanner (i.e., the file that was
    +          fed to flex).  Messages are  also  generated  when  the
    +          scanner  backtracks,  accepts the default rule, reaches
    +          the end of its input buffer (or encounters a  NUL;  the
    +          two  look  the same as far as the scanner's concerned),
    +          or reaches an end-of-file.
    +
    +     -f   specifies (take your pick) full table or fast  scanner.
    +          No  table compression is done.  The result is large but
    +          fast.  This option is equivalent to -Cf (see below).
    +
    +     -i   instructs flex to generate a case-insensitive  scanner.
    +          The  case  of  letters given in the flex input patterns
    +          will be ignored,  and  tokens  in  the  input  will  be
    +          matched  regardless of case.  The matched text given in
    +          yytext will have the preserved case (i.e., it will  not
    +          be folded).
    +
    +     -n   is another do-nothing, deprecated option included  only
    +          for POSIX compliance.
    +
    +     -p   generates a performance report to stderr.   The  report
    +          consists  of  comments  regarding  features of the flex
    +          input file which will cause a loss  of  performance  in
    +          the resulting scanner.
    +
    +     -s   causes the default rule (that unmatched  scanner  input
    +          is  echoed to stdout) to be suppressed.  If the scanner
    +          encounters input that does not match any of its  rules,
    +          it aborts with an error.
    +
    +     -t   instructs flex to write the  scanner  it  generates  to
    +          standard output instead of lex.yy.c.
    +
    +     -v   specifies that flex should write to stderr a summary of
    +          statistics regarding the scanner it generates.
    +
    +     -F   specifies that the fast  scanner  table  representation
    +          should  be  used.  This representation is about as fast
    +          as the full table representation  (-f),  and  for  some
    +          sets  of patterns will be considerably smaller (and for
    +          others, larger).  See flexdoc(1) for details.
    +
    +          This option is equivalent to -CF (see below).
    +
    +     -I   instructs flex to generate an interactive scanner, that
    +          is, a scanner which stops immediately rather than look-
    +          ing ahead if it knows that the currently  scanned  text
    +          cannot  be  part  of a longer rule's match.  Again, see
    +          flexdoc(1) for details.
    +
    +          Note, -I cannot be used in  conjunction  with  full  or
    +          fast tables, i.e., the -f, -F, -Cf, or -CF flags.
    +
    +     -L   instructs flex not  to  generate  #line  directives  in
    +          lex.yy.c. The default is to generate such directives so
    +          error messages in the actions will be correctly located
    +          with  respect  to the original flex input file, and not
    +          to the fairly meaningless line numbers of lex.yy.c.
    +
    +     -T   makes flex run in trace mode.  It will generate  a  lot
    +          of  messages to stdout concerning the form of the input
    +          and the resultant non-deterministic  and  deterministic
    +          finite  automata.   This  option  is  mostly for use in
    +          maintaining flex.
    +
    +     -8   instructs flex to generate an 8-bit scanner.   On  some
    +          sites,  this is the default.  On others, the default is
    +          7-bit characters.  To see which is the case, check  the
    +          verbose  (-v) output for "equivalence classes created".
    +          If the denominator of the number shown is 128, then  by
    +          default  flex is generating 7-bit characters.  If it is
    +          256, then the default is 8-bit characters.
    +
    +     -C[efmF]
    +          controls the degree of table compression.
    +
    +          -Ce directs  flex  to  construct  equivalence  classes,
    +          i.e.,  sets  of characters which have identical lexical
    +          properties.  Equivalence classes usually give  dramatic
    +          reductions  in the final table/object file sizes (typi-
    +          cally  a  factor  of  2-5)   and   are   pretty   cheap
    +          performance-wise   (one  array  look-up  per  character
    +          scanned).
    +
    +          -Cf specifies that the full scanner  tables  should  be
    +          generated - flex should not compress the tables by tak-
    +          ing advantages of similar transition functions for dif-
    +          ferent states.
    +
    +          -CF specifies that the alternate fast scanner represen-
    +          tation (described in flexdoc(1)) should be used.
    +
    +          -Cm directs flex to construct meta-equivalence classes,
    +          which  are  sets of equivalence classes (or characters,
    +          if equivalence classes are not  being  used)  that  are
    +          commonly  used  together.  Meta-equivalence classes are
    +          often a big win when using compressed tables, but  they
    +          have  a  moderate  performance  impact (one or two "if"
    +          tests and one array look-up per character scanned).
    +
    +          A lone -C specifies that the scanner tables  should  be
    +          compressed  but  neither  equivalence classes nor meta-
    +          equivalence classes should be used.
    +          The options -Cf or  -CF  and  -Cm  do  not  make  sense
    +          together - there is no opportunity for meta-equivalence
    +          classes if the table is not being  compressed.   Other-
    +          wise the options may be freely mixed.
    +
    +          The default setting is -Cem, which specifies that  flex
    +          should   generate   equivalence   classes   and   meta-
    +          equivalence classes.  This setting provides the highest
    +          degree   of  table  compression.   You  can  trade  off
    +          faster-executing scanners at the cost of larger  tables
    +          with the following generally being true:
    +
    +              slowest & smallest
    +                    -Cem
    +                    -Cm
    +                    -Ce
    +                    -C
    +                    -C{f,F}e
    +                    -C{f,F}
    +              fastest & largest
    +
    +
    +          -C options are not cumulative;  whenever  the  flag  is
    +          encountered, the previous -C settings are forgotten.
    +
    +     -Sskeleton_file
    +          overrides the default skeleton  file  from  which  flex
    +          constructs its scanners.  You'll never need this option
    +          unless you are doing flex maintenance or development.
    +
    +
    +
    +

    SUMMARY OF FLEX REGULAR EXPRESSIONS

    +     The patterns in the input are written using an extended  set
    +     of regular expressions.  These are:
    +
    +         x          match the character 'x'
    +         .          any character except newline
    +         [xyz]      a "character class"; in this case, the pattern
    +                      matches either an 'x', a 'y', or a 'z'
    +         [abj-oZ]   a "character class" with a range in it; matches
    +                      an 'a', a 'b', any letter from 'j' through 'o',
    +                      or a 'Z'
    +         [^A-Z]     a "negated character class", i.e., any character
    +                      but those in the class.  In this case, any
    +                      character EXCEPT an uppercase letter.
    +         [^A-Z\n]   any character EXCEPT an uppercase letter or
    +                      a newline
    +         r*         zero or more r's, where r is any regular expression
    +         r+         one or more r's
    +         r?         zero or one r's (that is, "an optional r")
    +         r{2,5}     anywhere from two to five r's
    +         r{2,}      two or more r's
    +         r{4}       exactly 4 r's
    +         {name}     the expansion of the "name" definition
    +                    (see above)
    +         "[xyz]\"foo"
    +                    the literal string: [xyz]"foo
    +         \X         if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
    +                      then the ANSI-C interpretation of \x.
    +                      Otherwise, a literal 'X' (used to escape
    +                      operators such as '*')
    +         \123       the character with octal value 123
    +         \x2a       the character with hexadecimal value 2a
    +         (r)        match an r; parentheses are used to override
    +                      precedence (see below)
    +
    +
    +         rs         the regular expression r followed by the
    +                      regular expression s; called "concatenation"
    +
    +
    +         r|s        either an r or an s
    +
    +
    +         r/s        an r but only if it is followed by an s.  The
    +                      s is not part of the matched text.  This type
    +                      of pattern is called as "trailing context".
    +         ^r         an r, but only at the beginning of a line
    +         r$         an r, but only at the end of a line.  Equivalent
    +                      to "r/\n".
    +
    +
    +         <s>r       an r, but only in start condition s (see
    +                    below for discussion of start conditions)
    +         <s1,s2,s3>r
    +                    same, but in any of start conditions s1,
    +                    s2, or s3
    +
    +
    +         <<EOF>>    an end-of-file
    +         <s1,s2><<EOF>>
    +                    an end-of-file when in start condition s1 or s2
    +
    +     The regular expressions listed above are  grouped  according
    +     to  precedence, from highest precedence at the top to lowest
    +     at the bottom.   Those  grouped  together  have  equal  pre-
    +     cedence.
    +
    +     Some notes on patterns:
    +
    +     -    Negated character classes match  newlines  unless  "\n"
    +          (or  an equivalent escape sequence) is one of the char-
    +          acters explicitly  present  in  the  negated  character
    +          class (e.g., "[^A-Z\n]").
    +
    +     -    A rule can have at most one instance of  trailing  con-
    +          text (the '/' operator or the '$' operator).  The start
    +          condition, '^', and "<<EOF>>" patterns can  only  occur
    +          at the beginning of a pattern, and, as well as with '/'
    +          and '$', cannot be  grouped  inside  parentheses.   The
    +          following are all illegal:
    +
    +              foo/bar$
    +              foo|(bar$)
    +              foo|^bar
    +              <sc1>foo<sc2>bar
    +
    +
    +
    +
    +

    SUMMARY OF SPECIAL ACTIONS

    +     In addition to arbitrary C code, the following can appear in
    +     actions:
    +
    +     -    ECHO copies yytext to the scanner's output.
    +
    +     -    BEGIN followed by the name of a start condition  places
    +          the scanner in the corresponding start condition.
    +
    +     -    REJECT directs the scanner to proceed on to the "second
    +          best"  rule which matched the input (or a prefix of the
    +          input).  yytext and yyleng are  set  up  appropriately.
    +          Note that REJECT is a particularly expensive feature in
    +          terms scanner performance; if it is used in any of  the
    +          scanner's   actions  it  will  slow  down  all  of  the
    +          scanner's matching.  Furthermore, REJECT cannot be used
    +          with the -f or -F options.
    +
    +          Note also that unlike the other special actions, REJECT
    +          is  a  branch;  code  immediately  following  it in the
    +          action will not be executed.
    +
    +     -    yymore() tells  the  scanner  that  the  next  time  it
    +          matches  a  rule,  the  corresponding  token  should be
    +          appended onto the current value of yytext  rather  than
    +          replacing it.
    +
    +     -    yyless(n) returns all but the first n characters of the
    +          current token back to the input stream, where they will
    +          be rescanned when the scanner looks for the next match.
    +          yytext  and  yyleng  are  adjusted appropriately (e.g.,
    +          yyleng will now be equal to n ).
    +
    +     -    unput(c) puts the  character  c  back  onto  the  input
    +          stream.  It will be the next character scanned.
    +
    +     -    input() reads the next character from the input  stream
    +          (this  routine  is  called  yyinput() if the scanner is
    +          compiled using C++).
    +
    +     -    yyterminate() can be used in lieu of a return statement
    +          in  an action.  It terminates the scanner and returns a
    +          0 to the scanner's caller, indicating "all done".
    +
    +          By default, yyterminate() is also called when  an  end-
    +          of-file is encountered.  It is a macro and may be rede-
    +          fined.
    +
    +     -    YY_NEW_FILE is an  action  available  only  in  <<EOF>>
    +          rules.   It  means "Okay, I've set up a new input file,
    +          continue scanning".
    +
    +     -    yy_create_buffer( file, size ) takes a FILE pointer and
    +          an integer size. It returns a YY_BUFFER_STATE handle to
    +          a new input buffer  large  enough  to  accomodate  size
    +          characters and associated with the given file.  When in
    +          doubt, use YY_BUF_SIZE for the size.
    +
    +     -    yy_switch_to_buffer(   new_buffer   )   switches    the
    +          scanner's  processing to scan for tokens from the given
    +          buffer, which must be a YY_BUFFER_STATE.
    +
    +     -    yy_delete_buffer( buffer ) deletes the given buffer.
    +
    +
    +
    +

    VALUES AVAILABLE TO THE USER

    +     -    char *yytext holds the text of the current  token.   It
    +          may not be modified.
    +
    +     -    int yyleng holds the length of the current  token.   It
    +          may not be modified.
    +
    +     -    FILE *yyin is the file  which  by  default  flex  reads
    +          from.   It  may  be  redefined  but doing so only makes
    +          sense before scanning begins.  Changing it in the  mid-
    +          dle of scanning will have unexpected results since flex
    +          buffers its input.  Once scanning terminates because an
    +          end-of-file   has   been  seen,  void  yyrestart(  FILE
    +          *new_file ) may be called to  point  yyin  at  the  new
    +          input file.
    +
    +     -    FILE *yyout is the file to which ECHO actions are done.
    +          It can be reassigned by the user.
    +
    +     -    YY_CURRENT_BUFFER returns a YY_BUFFER_STATE  handle  to
    +          the current buffer.
    +
    +
    +
    +

    MACROS THE USER CAN REDEFINE

    +     -    YY_DECL controls how the scanning routine is  declared.
    +          By  default, it is "int yylex()", or, if prototypes are
    +          being used, "int yylex(void)".  This definition may  be
    +          changed  by  redefining the "YY_DECL" macro.  Note that
    +          if you give arguments to the scanning routine  using  a
    +          K&R-style/non-prototyped function declaration, you must
    +          terminate the definition with a semi-colon (;).
    +
    +     -    The nature of how the scanner gets  its  input  can  be
    +          controlled    by   redefining   the   YY_INPUT   macro.
    +          YY_INPUT's         calling         sequence          is
    +          "YY_INPUT(buf,result,max_size)".    Its  action  is  to
    +          place up to max_size characters in the character  array
    +          buf  and  return  in the integer variable result either
    +          the number of characters read or the  constant  YY_NULL
    +          (0  on  Unix  systems)  to  indicate  EOF.  The default
    +          YY_INPUT reads from the global file-pointer "yyin".   A
    +          sample  redefinition  of  YY_INPUT  (in the definitions
    +          section of the input file):
    +
    +              %{
    +              #undef YY_INPUT
    +              #define YY_INPUT(buf,result,max_size) \
    +                  { \
    +                  int c = getchar(); \
    +                  result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
    +                  }
    +              %}
    +
    +
    +     -    When the scanner  receives  an  end-of-file  indication
    +          from  YY_INPUT,  it  then checks the yywrap() function.
    +          If yywrap() returns false (zero), then  it  is  assumed
    +          that  the  function  has  gone ahead and set up yyin to
    +          point to another input file,  and  scanning  continues.
    +          If  it  returns  true (non-zero), then the scanner ter-
    +          minates, returning 0 to its caller.
    +
    +          The default yywrap() always returns 1.   Presently,  to
    +          redefine  it  you  must first "#undef yywrap", as it is
    +          currently implemented as a macro.  It  is  likely  that
    +          yywrap()  will  soon be defined to be a function rather
    +          than a macro.
    +
    +     -    YY_USER_ACTION can be redefined to  provide  an  action
    +          which  is  always  executed prior to the matched rule's
    +          action.
    +
    +     -    The macro YY_USER_INIT may be redefined to  provide  an
    +          action which is always executed before the first scan.
    +
    +     -    In the generated scanner, the actions are all  gathered
    +          in  one  large  switch  statement  and  separated using
    +          YY_BREAK, which may be redefined.  By  default,  it  is
    +          simply  a  "break", to separate each rule's action from
    +          the following rule's.
    +
    +
    +
    +

    FILES

    +     flex.skel
    +          skeleton scanner.
    +
    +     lex.yy.c
    +          generated scanner (called lexyy.c on some systems).
    +
    +     lex.backtrack
    +          backtracking information for -b flag (called lex.bck on
    +          some systems).
    +
    +     -lfl library with which to link the scanners.
    +
    +
    +
    +

    SEE ALSO

    +     flexdoc(1), lex(1), yacc(1), sed(1), awk(1).
    +
    +     M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator
    +
    +
    +
    +

    DIAGNOSTICS

    +     reject_used_but_not_detected undefined or
    +
    +     yymore_used_but_not_detected undefined -  These  errors  can
    +     occur  at compile time.  They indicate that the scanner uses
    +     REJECT or yymore() but that flex failed to notice the  fact,
    +     meaning that flex scanned the first two sections looking for
    +     occurrences of these actions and failed  to  find  any,  but
    +     somehow  you  snuck  some in (via a #include file, for exam-
    +     ple).  Make an explicit reference to the action in your flex
    +     input   file.    (Note  that  previously  flex  supported  a
    +     %used/%unused mechanism for dealing with this problem;  this
    +     feature  is  still supported but now deprecated, and will go
    +     away soon unless the author hears from people who can  argue
    +     compellingly that they need it.)
    +
    +     flex scanner jammed - a scanner compiled with -s has encoun-
    +     tered  an  input  string  which wasn't matched by any of its
    +     rules.
    +
    +     flex input buffer overflowed -  a  scanner  rule  matched  a
    +     string  long enough to overflow the scanner's internal input
    +     buffer  (16K   bytes   -   controlled   by   YY_BUF_MAX   in
    +     "flex.skel").
    +
    +     scanner  requires  -8  flag  -  Your  scanner  specification
    +     includes  recognizing  8-bit  characters  and  you  did  not
    +     specify the -8 flag (and your site has  not  installed  flex
    +     with -8 as the default).
    +
    +     fatal flex scanner internal error--end of  buffer  missed  -
    +     This  can  occur  in  an  scanner which is reentered after a
    +     long-jump has jumped out (or over) the scanner's  activation
    +     frame.  Before reentering the scanner, use:
    +         yyrestart( yyin );
    +
    +
    +     too many %t classes! - You managed to put every single char-
    +     acter  into  its  own %t class.  flex requires that at least
    +     one of the classes share characters.
    +
    +
    +
    +

    AUTHOR

    +     Vern Paxson, with the help of many ideas and  much  inspira-
    +     tion from Van Jacobson.  Original version by Jef Poskanzer.
    +
    +     See flexdoc(1) for additional credits  and  the  address  to
    +     send comments to.
    +
    +
    +
    +

    DEFICIENCIES / BUGS

    +     Some trailing context patterns cannot  be  properly  matched
    +     and  generate  warning  messages  ("Dangerous  trailing con-
    +     text").  These are patterns where the ending  of  the  first
    +     part  of  the rule matches the beginning of the second part,
    +     such as "zx*/xy*", where the 'x*' matches  the  'x'  at  the
    +     beginning  of  the  trailing  context.  (Note that the POSIX
    +     draft states that the text matched by such patterns is unde-
    +     fined.)
    +
    +     For some trailing context rules, parts  which  are  actually
    +     fixed-length  are  not  recognized  as  such, leading to the
    +     abovementioned performance loss.  In particular, parts using
    +     '|'   or  {n}  (such  as  "foo{3}")  are  always  considered
    +     variable-length.
    +
    +     Combining trailing context with the special '|'  action  can
    +     result  in fixed trailing context being turned into the more
    +     expensive variable trailing context.  For example, this hap-
    +     pens in the following example:
    +
    +         %%
    +         abc      |
    +         xyz/def
    +
    +
    +     Use of unput() invalidates yytext and yyleng.
    +
    +     Use of unput() to push back more text than was  matched  can
    +     result  in the pushed-back text matching a beginning-of-line
    +     ('^') rule even though it didn't come at  the  beginning  of
    +     the line (though this is rare!).
    +
    +     Pattern-matching  of  NUL's  is  substantially  slower  than
    +     matching other characters.
    +
    +     flex does not generate correct  #line  directives  for  code
    +     internal to the scanner; thus, bugs in flex.skel yield bogus
    +     line numbers.
    +
    +     Due to both buffering of input and  read-ahead,  you  cannot
    +     intermix  calls to <stdio.h> routines, such as, for example,
    +     getchar(), with flex rules and  expect  it  to  work.   Call
    +     input() instead.
    +
    +     The total table entries listed by the -v flag  excludes  the
    +     number  of  table  entries needed to determine what rule has
    +     been matched.  The number of entries is equal to the  number
    +     of  DFA states if the scanner does not use REJECT, and some-
    +     what greater than the number of states if it does.
    +
    +     REJECT cannot be used with the -f or -F options.
    +
    +     Some of the macros, such as  yywrap(),  may  in  the  future
    +     become  functions which live in the -lfl library.  This will
    +     doubtless break a lot of  code,  but  may  be  required  for
    +     POSIX-compliance.
    +
    +     The flex internal algorithms need documentation.
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +Man(1) output converted with +man2html +
    + + diff --git a/src/toolsComm/flex/flex.skel b/src/toolsComm/flex/flex.skel new file mode 100644 index 000000000..39edc27fc --- /dev/null +++ b/src/toolsComm/flex/flex.skel @@ -0,0 +1,750 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton version: + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ + +#define FLEX_SCANNER + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +#else /* ! __cplusplus */ + +#ifdef __GNUC__ +#include +void *malloc( size_t ); +void free( void* ); +#else +#include +#endif /* __GNUC__ */ + +#endif /* ! __cplusplus */ + + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#define yyterminate() return ( YY_NULL ) + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters + */ +#define YY_DECL int yylex ( void ) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +%% section 1 definitions go here + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ +%% code to fiddle yytext and yyleng for yymore() goes here + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters*/ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; + +YY_CHAR *yytext; +int yyleng; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +%% data tables for the DFA go here + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +void yy_load_buffer_state ( void ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); + +#define yy_new_buffer yy_create_buffer + +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +YY_DECL + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp, *yy_bp; + register int yy_act; + +%% user's declarations go here + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { +%% yymore()-related code goes here + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + +%% code to set up and find next match goes here + +yy_find_action: +%% code to find the action number goes here + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + +%% debug code goes here + + switch ( yy_act ) + { +%% actions go here + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { +%% code to do backtracking for compressed tables and set up yy_cp goes here + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + + { + register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state() + + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp; + +%% code to get the start state into yy_current_state goes here + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { +%% code to find the next state goes here + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) + { + register int yy_is_jam; +%% code to find the next state, and perhaps do backtracking, goes here + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) + { + register YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + register YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + register YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef __cplusplus +static int yyinput() +#else +static int input(void) +#endif + + { + int c; + YY_CHAR *yy_cp = yy_c_buf_p; + + *yy_cp = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = yytext + YY_MORE_ADJ; + return ( EOF ); + } + + YY_NEW_FILE; + +#ifdef __cplusplus + return ( yyinput() ); +#else + return ( input() ); +#endif + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( "unexpected last match in input()" ); +#endif + } + } + } + + c = *yy_c_buf_p; + yy_hold_char = *++yy_c_buf_p; + + return ( c ); + } + + +void yyrestart( FILE *input_file ) + { + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +void yy_load_buffer_state( void ) + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +void yy_delete_buffer( YY_BUFFER_STATE b ) + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } diff --git a/src/toolsComm/flex/flex.skel.static b/src/toolsComm/flex/flex.skel.static new file mode 100644 index 000000000..48a47e511 --- /dev/null +++ b/src/toolsComm/flex/flex.skel.static @@ -0,0 +1,752 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton version: + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ + +/* modified by Jim Kowalkowski to have everything declared static */ + +#define FLEX_SCANNER + +#include +#include + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ + +/* #define yyterminate() return ( YY_NULL ) replaced by jbk */ +static int yyterminate_internal( void ); +#define yyterminate() return yyterminate_internal() + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters - jbk added the static to YY_DECL + */ +#define YY_DECL static int yylex ( void ) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +%% section 1 definitions go here + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ +%% code to fiddle yytext and yyleng for yymore() goes here + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters */ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +/* jbk update +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; +*/ + +static YY_CHAR *yytext; /* jbk added static */ +static int yyleng; /* jbk added static */ + +static FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; /* jbk added static */ + +%% data tables for the DFA go here + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); + +/* jbk added static in front all these */ +static void yyrestart ( FILE *input_file ); +static void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +static void yy_load_buffer_state ( void ); +static YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +static void yy_delete_buffer ( YY_BUFFER_STATE b ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); + +#define yy_new_buffer yy_create_buffer + +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +YY_DECL + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp, *yy_bp; + register int yy_act; + +%% user's declarations go here + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { +%% yymore()-related code goes here + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + +%% code to set up and find next match goes here + +yy_find_action: +%% code to find the action number goes here + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + +%% debug code goes here + + switch ( yy_act ) + { +%% actions go here + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { +%% code to do backtracking for compressed tables and set up yy_cp goes here + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer( void ) + + { + register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move - YY_MORE_ADJ == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state( void ) + + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp; + +%% code to get the start state into yy_current_state goes here + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { +%% code to find the next state goes here + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) + { + register int yy_is_jam; +%% code to find the next state, and perhaps do backtracking, goes here + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) + { + register YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + register YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + register YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef __cplusplus +static int yyinput( void ) +#else +static int input( void ) +#endif + + { + int c; + YY_CHAR *yy_cp = yy_c_buf_p; + + *yy_cp = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = yytext + YY_MORE_ADJ; + return ( EOF ); + } + + YY_NEW_FILE; + +#ifdef __cplusplus + return ( yyinput() ); +#else + return ( input() ); +#endif + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( "unexpected last match in input()" ); +#endif + } + } + } + + c = *yy_c_buf_p; + yy_hold_char = *++yy_c_buf_p; + + return ( c ); + } + + +/* jbk added static in front of func */ +static void yyrestart( FILE *input_file ) + { + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, input_file ); + else + yy_current_buffer = yy_create_buffer( input_file, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + +/* jbk added static in front of func */ +static void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +/* jbk added static in front of func */ +static void yy_load_buffer_state( void ) + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +/* jbk added static in front of func */ +static YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +/* jbk added static in front of func */ +static void yy_delete_buffer( YY_BUFFER_STATE b ) + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +/* jbk added static in front of func */ +static void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } + +static int yyterminate_internal( void ) +{ + /* jbk fix - buffer created by yy_create_buffer needs to be freed */ + yy_delete_buffer(yy_current_buffer); + yy_current_buffer=NULL; + return YY_NULL; +} + diff --git a/src/toolsComm/flex/flexdef.h b/src/toolsComm/flex/flexdef.h new file mode 100644 index 000000000..f475c7a9c --- /dev/null +++ b/src/toolsComm/flex/flexdef.h @@ -0,0 +1,839 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* flexdef - definitions file for flex */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* @(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL) */ + +#ifndef INC_flexdef_H +#define INC_flexdef_H + +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#define NORETURN __attribute__((noreturn)) +#else +#define NORETURN +#endif + +/* always be prepared to generate an 8-bit scanner */ +#define FLEX_8_BIT_CHARS + +#ifdef FLEX_8_BIT_CHARS +#define CSIZE 256 +#define Char unsigned char +#else +#define Char char +#define CSIZE 128 +#endif + +/* size of input alphabet - should be size of ASCII set */ +#ifndef DEFAULT_CSIZE +#define DEFAULT_CSIZE 128 +#endif + + +/* maximum line length we'll have to deal with */ +#define MAXLINE BUFSIZ + +/* maximum size of file name */ +#define FILENAMESIZE 1024 + +#ifndef min +#define min(x,y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef max +#define max(x,y) ((x) > (y) ? (x) : (y)) +#endif + +#define true 1 +#define false 0 + + +#ifndef DEFAULT_SKELETON_FILE +#define DEFAULT_SKELETON_FILE "flex.skel" +#endif + +/* special chk[] values marking the slots taking by end-of-buffer and action + * numbers + */ +#define EOB_POSITION -1 +#define ACTION_POSITION -2 + +/* number of data items per line for -f output */ +#define NUMDATAITEMS 10 + +/* number of lines of data in -f output before inserting a blank line for + * readability. + */ +#define NUMDATALINES 10 + +/* transition_struct_out() definitions */ +#define TRANS_STRUCT_PRINT_LENGTH 15 + +/* returns true if an nfa state has an epsilon out-transition slot + * that can be used. This definition is currently not used. + */ +#define FREE_EPSILON(state) \ + (transchar[state] == SYM_EPSILON && \ + trans2[state] == NO_TRANSITION && \ + finalst[state] != state) + +/* returns true if an nfa state has an epsilon out-transition character + * and both slots are free + */ +#define SUPER_FREE_EPSILON(state) \ + (transchar[state] == SYM_EPSILON && \ + trans1[state] == NO_TRANSITION) \ + +/* maximum number of NFA states that can comprise a DFA state. It's real + * big because if there's a lot of rules, the initial state will have a + * huge epsilon closure. + */ +#define INITIAL_MAX_DFA_SIZE 750 +#define MAX_DFA_SIZE_INCREMENT 750 + + +/* a note on the following masks. They are used to mark accepting numbers + * as being special. As such, they implicitly limit the number of accepting + * numbers (i.e., rules) because if there are too many rules the rule numbers + * will overload the mask bits. Fortunately, this limit is \large/ (0x2000 == + * 8192) so unlikely to actually cause any problems. A check is made in + * new_rule() to ensure that this limit is not reached. + */ + +/* mask to mark a trailing context accepting number */ +#define YY_TRAILING_MASK 0x2000 + +/* mask to mark the accepting number of the "head" of a trailing context rule */ +#define YY_TRAILING_HEAD_MASK 0x4000 + +/* maximum number of rules, as outlined in the above note */ +#define MAX_RULE (YY_TRAILING_MASK - 1) + + +/* NIL must be 0. If not, its special meaning when making equivalence classes + * (it marks the representative of a given e.c.) will be unidentifiable + */ +#define NIL 0 + +#define JAM -1 /* to mark a missing DFA transition */ +#define NO_TRANSITION NIL +#define UNIQUE -1 /* marks a symbol as an e.c. representative */ +#define INFINITY -1 /* for x{5,} constructions */ + +#define INITIAL_MAX_CCLS 100 /* max number of unique character classes */ +#define MAX_CCLS_INCREMENT 100 + +/* size of table holding members of character classes */ +#define INITIAL_MAX_CCL_TBL_SIZE 500 +#define MAX_CCL_TBL_SIZE_INCREMENT 250 + +#define INITIAL_MAX_RULES 100 /* default maximum number of rules */ +#define MAX_RULES_INCREMENT 100 + +#define INITIAL_MNS 2000 /* default maximum number of nfa states */ +#define MNS_INCREMENT 1000 /* amount to bump above by if it's not enough */ + +#define INITIAL_MAX_DFAS 1000 /* default maximum number of dfa states */ +#define MAX_DFAS_INCREMENT 1000 + +#define JAMSTATE -32766 /* marks a reference to the state that always jams */ + +/* enough so that if it's subtracted from an NFA state number, the result + * is guaranteed to be negative + */ +#define MARKER_DIFFERENCE 32000 +#define MAXIMUM_MNS 31999 + +/* maximum number of nxt/chk pairs for non-templates */ +#define INITIAL_MAX_XPAIRS 2000 +#define MAX_XPAIRS_INCREMENT 2000 + +/* maximum number of nxt/chk pairs needed for templates */ +#define INITIAL_MAX_TEMPLATE_XPAIRS 2500 +#define MAX_TEMPLATE_XPAIRS_INCREMENT 2500 + +#define SYM_EPSILON (CSIZE + 1) /* to mark transitions on the symbol epsilon */ + +#define INITIAL_MAX_SCS 40 /* maximum number of start conditions */ +#define MAX_SCS_INCREMENT 40 /* amount to bump by if it's not enough */ + +#define ONE_STACK_SIZE 500 /* stack of states with only one out-transition */ +#define SAME_TRANS -1 /* transition is the same as "default" entry for state */ + +/* the following percentages are used to tune table compression: + + * the percentage the number of out-transitions a state must be of the + * number of equivalence classes in order to be considered for table + * compaction by using protos + */ +#define PROTO_SIZE_PERCENTAGE 15 + +/* the percentage the number of homogeneous out-transitions of a state + * must be of the number of total out-transitions of the state in order + * that the state's transition table is first compared with a potential + * template of the most common out-transition instead of with the first + * proto in the proto queue + */ +#define CHECK_COM_PERCENTAGE 50 + +/* the percentage the number of differences between a state's transition + * table and the proto it was first compared with must be of the total + * number of out-transitions of the state in order to keep the first + * proto as a good match and not search any further + */ +#define FIRST_MATCH_DIFF_PERCENTAGE 10 + +/* the percentage the number of differences between a state's transition + * table and the most similar proto must be of the state's total number + * of out-transitions to use the proto as an acceptable close match + */ +#define ACCEPTABLE_DIFF_PERCENTAGE 50 + +/* the percentage the number of homogeneous out-transitions of a state + * must be of the number of total out-transitions of the state in order + * to consider making a template from the state + */ +#define TEMPLATE_SAME_PERCENTAGE 60 + +/* the percentage the number of differences between a state's transition + * table and the most similar proto must be of the state's total number + * of out-transitions to create a new proto from the state + */ +#define NEW_PROTO_DIFF_PERCENTAGE 20 + +/* the percentage the total number of out-transitions of a state must be + * of the number of equivalence classes in order to consider trying to + * fit the transition table into "holes" inside the nxt/chk table. + */ +#define INTERIOR_FIT_PERCENTAGE 15 + +/* size of region set aside to cache the complete transition table of + * protos on the proto queue to enable quick comparisons + */ +#define PROT_SAVE_SIZE 2000 + +#define MSP 50 /* maximum number of saved protos (protos on the proto queue) */ + +/* maximum number of out-transitions a state can have that we'll rummage + * around through the interior of the internal fast table looking for a + * spot for it + */ +#define MAX_XTIONS_FULL_INTERIOR_FIT 4 + +/* maximum number of rules which will be reported as being associated + * with a DFA state + */ +#define MAX_ASSOC_RULES 100 + +/* number that, if used to subscript an array, has a good chance of producing + * an error; should be small enough to fit into a short + */ +#define BAD_SUBSCRIPT -32767 + +/* absolute value of largest number that can be stored in a short, with a + * bit of slop thrown in for general paranoia. + */ +#define MAX_SHORT 32766 + + +/* Declarations for global variables. */ + +/* variables for symbol tables: + * sctbl - start-condition symbol table + * ndtbl - name-definition symbol table + * ccltab - character class text symbol table + */ + +struct hash_entry + { + struct hash_entry *prev, *next; + char *name; + char *str_val; + int int_val; + } ; + +typedef struct hash_entry *hash_table[]; + +#define NAME_TABLE_HASH_SIZE 101 +#define START_COND_HASH_SIZE 101 +#define CCL_HASH_SIZE 101 + +extern struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE]; +extern struct hash_entry *sctbl[START_COND_HASH_SIZE]; +extern struct hash_entry *ccltab[CCL_HASH_SIZE]; + + +/* variables for flags: + * printstats - if true (-v), dump statistics + * syntaxerror - true if a syntax error has been found + * eofseen - true if we've seen an eof in the input file + * ddebug - if true (-d), make a "debug" scanner + * trace - if true (-T), trace processing + * spprdflt - if true (-s), suppress the default rule + * interactive - if true (-I), generate an interactive scanner + * caseins - if true (-i), generate a case-insensitive scanner + * useecs - if true (-Ce flag), use equivalence classes + * fulltbl - if true (-Cf flag), don't compress the DFA state table + * usemecs - if true (-Cm flag), use meta-equivalence classes + * fullspd - if true (-F flag), use Jacobson method of table representation + * gen_line_dirs - if true (i.e., no -L flag), generate #line directives + * performance_report - if true (i.e., -p flag), generate a report relating + * to scanner performance + * backtrack_report - if true (i.e., -b flag), generate "lex.backtrack" file + * listing backtracking states + * csize - size of character set for the scanner we're generating; + * 128 for 7-bit chars and 256 for 8-bit + * yymore_used - if true, yymore() is used in input rules + * reject - if true, generate backtracking tables for REJECT macro + * real_reject - if true, scanner really uses REJECT (as opposed to just + * having "reject" set for variable trailing context) + * continued_action - true if this rule's action is to "fall through" to + * the next rule's action (i.e., the '|' action) + * yymore_really_used - has a REALLY_xxx value indicating whether a + * %used or %notused was used with yymore() + * reject_really_used - same for REJECT + */ + +extern int printstats, syntaxerror, eofseen, ddebug, trace, spprdflt; +extern int interactive, caseins, useecs, fulltbl, usemecs; +extern int fullspd, gen_line_dirs, performance_report, backtrack_report, csize; +extern int yymore_used, reject, real_reject, continued_action; + +#define REALLY_NOT_DETERMINED 0 +#define REALLY_USED 1 +#define REALLY_NOT_USED 2 +extern int yymore_really_used, reject_really_used; + + +/* variables used in the flex input routines: + * datapos - characters on current output line + * dataline - number of contiguous lines of data in current data + * statement. Used to generate readable -f output + * linenum - current input line number + * skelfile - the skeleton file + * yyin - input file + * temp_action_file - temporary file to hold actions + * backtrack_file - file to summarize backtracking states to + * infilename - name of input file + * action_file_name - name of the temporary file + * input_files - array holding names of input files + * num_input_files - size of input_files array + * program_name - name with which program was invoked + */ + +extern int datapos, dataline, linenum; +extern FILE *skelfile, *yyin, *temp_action_file, *backtrack_file; +extern char *infilename; +extern char action_file_name[]; +extern char **input_files; +extern int num_input_files; +extern char *program_name; + + +/* variables for stack of states having only one out-transition: + * onestate - state number + * onesym - transition symbol + * onenext - target state + * onedef - default base entry + * onesp - stack pointer + */ + +extern int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE]; +extern int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp; + + +/* variables for nfa machine data: + * current_mns - current maximum on number of NFA states + * num_rules - number of the last accepting state; also is number of + * rules created so far + * current_max_rules - current maximum number of rules + * lastnfa - last nfa state number created + * firstst - physically the first state of a fragment + * lastst - last physical state of fragment + * finalst - last logical state of fragment + * transchar - transition character + * trans1 - transition state + * trans2 - 2nd transition state for epsilons + * accptnum - accepting number + * assoc_rule - rule associated with this NFA state (or 0 if none) + * state_type - a STATE_xxx type identifying whether the state is part + * of a normal rule, the leading state in a trailing context + * rule (i.e., the state which marks the transition from + * recognizing the text-to-be-matched to the beginning of + * the trailing context), or a subsequent state in a trailing + * context rule + * rule_type - a RULE_xxx type identifying whether this a a ho-hum + * normal rule or one which has variable head & trailing + * context + * rule_linenum - line number associated with rule + */ + +extern int current_mns, num_rules, current_max_rules, lastnfa; +extern int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2; +extern int *accptnum, *assoc_rule, *state_type, *rule_type, *rule_linenum; + +/* different types of states; values are useful as masks, as well, for + * routines like check_trailing_context() + */ +#define STATE_NORMAL 0x1 +#define STATE_TRAILING_CONTEXT 0x2 + +/* global holding current type of state we're making */ + +extern int current_state_type; + +/* different types of rules */ +#define RULE_NORMAL 0 +#define RULE_VARIABLE 1 + +/* true if the input rules include a rule with both variable-length head + * and trailing context, false otherwise + */ +extern int variable_trailing_context_rules; + + +/* variables for protos: + * numtemps - number of templates created + * numprots - number of protos created + * protprev - backlink to a more-recently used proto + * protnext - forward link to a less-recently used proto + * prottbl - base/def table entry for proto + * protcomst - common state of proto + * firstprot - number of the most recently used proto + * lastprot - number of the least recently used proto + * protsave contains the entire state array for protos + */ + +extern int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP]; +extern int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE]; + + +/* variables for managing equivalence classes: + * numecs - number of equivalence classes + * nextecm - forward link of Equivalence Class members + * ecgroup - class number or backward link of EC members + * nummecs - number of meta-equivalence classes (used to compress + * templates) + * tecfwd - forward link of meta-equivalence classes members + * tecbck - backward link of MEC's + * xlation - maps character codes to their translations, or nil if no %t table + * num_xlations - number of different xlation values + */ + +/* reserve enough room in the equivalence class arrays so that we + * can use the CSIZE'th element to hold equivalence class information + * for the NUL character. Later we'll move this information into + * the 0th element. + */ +extern int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs; + +/* meta-equivalence classes are indexed starting at 1, so it's possible + * that they will require positions from 1 .. CSIZE, i.e., CSIZE + 1 + * slots total (since the arrays are 0-based). nextecm[] and ecgroup[] + * don't require the extra position since they're indexed from 1 .. CSIZE - 1. + */ +extern int tecfwd[CSIZE + 1], tecbck[CSIZE + 1]; + +extern int *xlation; +extern int num_xlations; + + +/* variables for start conditions: + * lastsc - last start condition created + * current_max_scs - current limit on number of start conditions + * scset - set of rules active in start condition + * scbol - set of rules active only at the beginning of line in a s.c. + * scxclu - true if start condition is exclusive + * sceof - true if start condition has EOF rule + * scname - start condition name + * actvsc - stack of active start conditions for the current rule + */ + +extern int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc; +extern char **scname; + + +/* variables for dfa machine data: + * current_max_dfa_size - current maximum number of NFA states in DFA + * current_max_xpairs - current maximum number of non-template xtion pairs + * current_max_template_xpairs - current maximum number of template pairs + * current_max_dfas - current maximum number DFA states + * lastdfa - last dfa state number created + * nxt - state to enter upon reading character + * chk - check value to see if "nxt" applies + * tnxt - internal nxt table for templates + * base - offset into "nxt" for given state + * def - where to go if "chk" disallows "nxt" entry + * nultrans - NUL transition for each state + * NUL_ec - equivalence class of the NUL character + * tblend - last "nxt/chk" table entry being used + * firstfree - first empty entry in "nxt/chk" table + * dss - nfa state set for each dfa + * dfasiz - size of nfa state set for each dfa + * dfaacc - accepting set for each dfa state (or accepting number, if + * -r is not given) + * accsiz - size of accepting set for each dfa state + * dhash - dfa state hash value + * numas - number of DFA accepting states created; note that this + * is not necessarily the same value as num_rules, which is the analogous + * value for the NFA + * numsnpairs - number of state/nextstate transition pairs + * jambase - position in base/def where the default jam table starts + * jamstate - state number corresponding to "jam" state + * end_of_buffer_state - end-of-buffer dfa state number + */ + +extern int current_max_dfa_size, current_max_xpairs; +extern int current_max_template_xpairs, current_max_dfas; +extern int lastdfa, lasttemp, *nxt, *chk, *tnxt; +extern int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz; +extern union dfaacc_union + { + int *dfaacc_set; + int dfaacc_state; + } *dfaacc; +extern int *accsiz, *dhash, numas; +extern int numsnpairs, jambase, jamstate; +extern int end_of_buffer_state; + +/* variables for ccl information: + * lastccl - ccl index of the last created ccl + * current_maxccls - current limit on the maximum number of unique ccl's + * cclmap - maps a ccl index to its set pointer + * ccllen - gives the length of a ccl + * cclng - true for a given ccl if the ccl is negated + * cclreuse - counts how many times a ccl is re-used + * current_max_ccl_tbl_size - current limit on number of characters needed + * to represent the unique ccl's + * ccltbl - holds the characters in each ccl - indexed by cclmap + */ + +extern int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse; +extern int current_max_ccl_tbl_size; +extern Char *ccltbl; + + +/* variables for miscellaneous information: + * starttime - real-time when we started + * endtime - real-time when we ended + * nmstr - last NAME scanned by the scanner + * sectnum - section number currently being parsed + * nummt - number of empty nxt/chk table entries + * hshcol - number of hash collisions detected by snstods + * dfaeql - number of times a newly created dfa was equal to an old one + * numeps - number of epsilon NFA states created + * eps2 - number of epsilon states which have 2 out-transitions + * num_reallocs - number of times it was necessary to realloc() a group + * of arrays + * tmpuses - number of DFA states that chain to templates + * totnst - total number of NFA states used to make DFA states + * peakpairs - peak number of transition pairs we had to store internally + * numuniq - number of unique transitions + * numdup - number of duplicate transitions + * hshsave - number of hash collisions saved by checking number of states + * num_backtracking - number of DFA states requiring back-tracking + * bol_needed - whether scanner needs beginning-of-line recognition + */ + +extern char *starttime, *endtime, nmstr[MAXLINE]; +extern int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs; +extern int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave; +extern int num_backtracking, bol_needed; + +void *allocate_array(int size, int element_size); +void *reallocate_array(void *array, int size, int element_size); + +#define allocate_integer_array(size) \ + (int *) allocate_array( size, sizeof( int ) ) + +#define reallocate_integer_array(array,size) \ + (int *) reallocate_array( (void *) array, size, sizeof( int ) ) + +#define allocate_int_ptr_array(size) \ + (int **) allocate_array( size, sizeof( int * ) ) + +#define allocate_char_ptr_array(size) \ + (char **) allocate_array( size, sizeof( char * ) ) + +#define allocate_dfaacc_union(size) \ + (union dfaacc_union *) \ + allocate_array( size, sizeof( union dfaacc_union ) ) + +#define reallocate_int_ptr_array(array,size) \ + (int **) reallocate_array( (void *) array, size, sizeof( int * ) ) + +#define reallocate_char_ptr_array(array,size) \ + (char **) reallocate_array( (void *) array, size, sizeof( char * ) ) + +#define reallocate_dfaacc_union(array, size) \ + (union dfaacc_union *) \ + reallocate_array( (void *) array, size, sizeof( union dfaacc_union ) ) + +#define allocate_character_array(size) \ + (Char *) allocate_array( size, sizeof( Char ) ) + +#define reallocate_character_array(array,size) \ + (Char *) reallocate_array( (void *) array, size, sizeof( Char ) ) + +#if 0 /* JRW this might couse truuble... but not for IOC usage */ +/* used to communicate between scanner and parser. The type should really + * be YYSTYPE, but we can't easily get our hands on it. + */ +#ifdef __alpha /* inconsistency with parse.y, line 57... on Alpha */ +extern long yylval; +#else +extern int yylval; +#endif +#endif + + +/* external functions that are cross-referenced among the flex source files */ + + +/* from file ccl.c */ + +extern void ccladd (int, int); /* Add a single character to a ccl */ +extern int cclinit (void); /* make an empty ccl */ +extern void cclnegate (int); /* negate a ccl */ + +/* list the members of a set of characters in CCL form */ +extern void list_character_set (FILE*, int[]); + + +/* from file dfa.c */ + +/* increase the maximum number of dfas */ +extern void increase_max_dfas (void); + +extern void ntod (void); /* convert a ndfa to a dfa */ + + +/* from file ecs.c */ + +/* convert character classes to set of equivalence classes */ +extern void ccl2ecl (void); + +/* associate equivalence class numbers with class members */ +extern int cre8ecs (int[], int[], int); + +/* associate equivalence class numbers using %t table */ +extern int ecs_from_xlation (int[]); + +/* update equivalence classes based on character class transitions */ +extern void mkeccl (Char[], int, int[], int[], int, int); + +/* create equivalence class for single character */ +extern void mkechar (int, int[], int[]); + + +/* from file gen.c */ + +extern void make_tables (void); /* generate transition tables */ + + +/* from file main.c */ + +extern void flexend (int) NORETURN; + + +/* from file misc.c */ + +/* write out the actions from the temporary file to lex.yy.c */ +extern void action_out (void); + +/* true if a string is all lower case */ +extern int all_lower (Char *); + +/* true if a string is all upper case */ +extern int all_upper (Char *); + +/* bubble sort an integer array */ +extern void bubble (int [], int); + +/* shell sort a character array */ +extern void cshell (Char [], int, int); + +extern void dataend (void); /* finish up a block of data declarations */ + +/* report an error message and terminate */ +extern void flexerror (char[]) NORETURN; + +/* report a fatal error message and terminate */ +extern void flexfatal (char[]); + +/* report an error message formatted with one integer argument */ +extern void lerrif (char[], int); + +/* report an error message formatted with one string argument */ +extern void lerrsf (char[], char[]); + +/* spit out a "# line" statement */ +extern void line_directive_out (FILE*); + +/* generate a data statment for a two-dimensional array */ +extern void mk2data (int); + +extern void mkdata (int); /* generate a data statement */ + +/* return the integer represented by a string of digits */ +extern int myctoi (Char []); + +/* write out one section of the skeleton file */ +extern void skelout (void); + +/* output a yy_trans_info structure */ +extern void transition_struct_out (int, int); + + +/* from file nfa.c */ + +/* add an accepting state to a machine */ +extern void add_accept (int, int); + +/* make a given number of copies of a singleton machine */ +extern int copysingl (int, int); + +/* debugging routine to write out an nfa */ +extern void dumpnfa (int); + +/* finish up the processing for a rule */ +extern void finish_rule (int, int, int, int); + +/* connect two machines together */ +extern int link_machines (int, int); + +/* mark each "beginning" state in a machine as being a "normal" (i.e., + * not trailing context associated) state + */ +extern void mark_beginning_as_normal (int); + +/* make a machine that branches to two machines */ +extern int mkbranch (int, int); + +extern int mkclos (int); /* convert a machine into a closure */ +extern int mkopt (int); /* make a machine optional */ + +/* make a machine that matches either one of two machines */ +extern int mkor (int, int); + +/* convert a machine into a positive closure */ +extern int mkposcl (int); + +extern int mkrep (int, int, int); /* make a replicated machine */ + +/* create a state with a transition on a given symbol */ +extern int mkstate (int); + +extern void new_rule (void); /* initialize for a new rule */ + + +/* from file parse.y */ + +/* write out a message formatted with one string, pinpointing its location */ +extern void format_pinpoint_message (char[], char[]); + +/* write out a message, pinpointing its location */ +extern void pinpoint_message (char[]); + +extern void synerr (char []); /* report a syntax error */ +/* extern int yyparse ();*/ /* the YACC parser */ + + +/* from file scan.l */ + +extern int flexscan (); /* the Flex-generated scanner for flex */ + +/* open the given file (if NULL, stdin) for scanning */ +extern void set_input_file (char*); + +extern int yywrap (); /* wrapup a file in the lexical analyzer */ + + +/* from file sym.c */ + +/* save the text of a character class */ +extern void cclinstal (Char [], int); + +/* lookup the number associated with character class */ +extern int ccllookup (Char []); + +extern void ndinstal (char[], Char[]); /* install a name definition */ +extern void scinstal (char[], int); /* make a start condition */ + +/* lookup the number associated with a start condition */ +extern int sclookup (char[]); + + +/* from file tblcmp.c */ + +/* build table entries for dfa state */ +extern void bldtbl (int[], int, int, int, int); + +extern void cmptmps (void); /* compress template table entries */ +extern void inittbl (void); /* initialize transition tables */ +extern void mkdeftbl (void); /* make the default, "jam" table entries */ + +/* create table entries for a state (or state fragment) which has + * only one out-transition */ +extern void mk1tbl (int, int, int, int); + +/* place a state into full speed transition table */ +extern void place_state (int*, int, int); + +/* save states with only one out-transition to be processed later */ +extern void stack1 (int, int, int, int); + + +/* from file yylex.c */ + +extern int yylex (); + + +/* The Unix kernel calls used here */ + +extern int read (int, char*, int); +#ifndef _WIN32 +extern int unlink (char*); +#endif +extern int write (int, char*, int); + + +#endif /* INC_flexdef_H */ + diff --git a/src/toolsComm/flex/flexdoc.html b/src/toolsComm/flex/flexdoc.html new file mode 100644 index 000000000..0a3b59510 --- /dev/null +++ b/src/toolsComm/flex/flexdoc.html @@ -0,0 +1,1890 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ + + +
    +
    +
    +
    +

    NAME

    +     flex - fast lexical analyzer generator
    +
    +
    +
    +

    SYNOPSIS

    +     flex [-bcdfinpstvFILT8 -C[efmF] -Sskeleton] [filename ...]
    +
    +
    +
    +

    DESCRIPTION

    +     flex is a  tool  for  generating  scanners:  programs  which
    +     recognized  lexical  patterns in text.  flex reads the given
    +     input files, or its standard input  if  no  file  names  are
    +     given,  for  a  description  of  a scanner to generate.  The
    +     description is in the form of pairs of  regular  expressions
    +     and  C  code,  called  rules.  flex  generates as output a C
    +     source file, lex.yy.c, which defines a routine yylex(). This
    +     file is compiled and linked with the -lfl library to produce
    +     an executable.  When the executable is run, it analyzes  its
    +     input  for occurrences of the regular expressions.  Whenever
    +     it finds one, it executes the corresponding C code.
    +
    +
    +
    +

    SOME SIMPLE EXAMPLES

    +     First some simple examples to get the flavor of how one uses
    +     flex.  The  following  flex  input specifies a scanner which
    +     whenever it encounters the string "username" will replace it
    +     with the user's login name:
    +
    +         %%
    +         username    printf( "%s", getlogin() );
    +
    +     By default, any text not matched by a flex scanner is copied
    +     to  the output, so the net effect of this scanner is to copy
    +     its input file to its output with each occurrence of  "user-
    +     name"  expanded.   In  this  input,  there is just one rule.
    +     "username" is the pattern and the "printf"  is  the  action.
    +     The "%%" marks the beginning of the rules.
    +
    +     Here's another simple example:
    +
    +             int num_lines = 0, num_chars = 0;
    +
    +         %%
    +         \n    ++num_lines; ++num_chars;
    +         .     ++num_chars;
    +
    +         %%
    +         main()
    +             {
    +             yylex();
    +             printf( "# of lines = %d, # of chars = %d\n",
    +                     num_lines, num_chars );
    +             }
    +
    +     This scanner counts the number of characters and the  number
    +     of  lines in its input (it produces no output other than the
    +     final report on the counts).  The first  line  declares  two
    +     globals,  "num_lines"  and "num_chars", which are accessible
    +     both inside yylex() and in the main() routine declared after
    +     the  second  "%%".  There are two rules, one which matches a
    +     newline ("\n") and increments both the line  count  and  the
    +     character  count,  and one which matches any character other
    +     than a newline (indicated by the "." regular expression).
    +
    +     A somewhat more complicated example:
    +
    +         /* scanner for a toy Pascal-like language */
    +
    +         %{
    +         /* need this for the call to atof() below */
    +         #include <math.h>
    +         %}
    +
    +         DIGIT    [0-9]
    +         ID       [a-z][a-z0-9]*
    +
    +         %%
    +
    +         {DIGIT}+    {
    +                     printf( "An integer: %s (%d)\n", yytext,
    +                             atoi( yytext ) );
    +                     }
    +
    +         {DIGIT}+"."{DIGIT}*        {
    +                     printf( "A float: %s (%g)\n", yytext,
    +                             atof( yytext ) );
    +                     }
    +
    +         if|then|begin|end|procedure|function        {
    +                     printf( "A keyword: %s\n", yytext );
    +                     }
    +
    +         {ID}        printf( "An identifier: %s\n", yytext );
    +
    +         "+"|"-"|"*"|"/"   printf( "An operator: %s\n", yytext );
    +
    +         "{"[^}\n]*"}"     /* eat up one-line comments */
    +
    +         [ \t\n]+          /* eat up whitespace */
    +
    +         .           printf( "Unrecognized character: %s\n", yytext );
    +
    +         %%
    +
    +         main( argc, argv )
    +         int argc;
    +         char **argv;
    +             {
    +             ++argv, --argc;  /* skip over program name */
    +             if ( argc > 0 )
    +                     yyin = fopen( argv[0], "r" );
    +             else
    +                     yyin = stdin;
    +
    +             yylex();
    +             }
    +
    +     This is the beginnings of a simple scanner  for  a  language
    +     like  Pascal.   It  identifies different types of tokens and
    +     reports on what it has seen.
    +
    +     The details of this example will be explained in the follow-
    +     ing sections.
    +
    +
    +
    +

    FORMAT OF THE INPUT FILE

    +     The flex input file consists of three sections, separated by
    +     a line with just %% in it:
    +
    +         definitions
    +         %%
    +         rules
    +         %%
    +         user code
    +
    +     The definitions section contains declarations of simple name
    +     definitions  to  simplify  the  scanner  specification,  and
    +     declarations of start conditions, which are explained  in  a
    +     later section.
    +
    +     Name definitions have the form:
    +
    +         name definition
    +
    +     The "name" is a word beginning with a letter  or  an  under-
    +     score  ('_')  followed by zero or more letters, digits, '_',
    +     or '-' (dash).  The definition is  taken  to  begin  at  the
    +     first  non-white-space character following the name and con-
    +     tinuing to the end of the line.  The definition  can  subse-
    +     quently  be referred to using "{name}", which will expand to
    +     "(definition)".  For example,
    +
    +         DIGIT    [0-9]
    +         ID       [a-z][a-z0-9]*
    +
    +     defines "DIGIT" to be a regular expression which  matches  a
    +     single  digit,  and  "ID"  to  be a regular expression which
    +     matches a letter followed by zero-or-more letters-or-digits.
    +     A subsequent reference to
    +
    +         {DIGIT}+"."{DIGIT}*
    +
    +     is identical to
    +
    +         ([0-9])+"."([0-9])*
    +
    +     and matches one-or-more digits followed by a '.' followed by
    +     zero-or-more digits.
    +
    +     The rules section of the flex input  contains  a  series  of
    +     rules of the form:
    +
    +         pattern   action
    +
    +     where the pattern must be unindented  and  the  action  must
    +     begin on the same line.
    +
    +     See below for a further description of patterns and actions.
    +
    +     Finally, the user code section is simply copied to  lex.yy.c
    +     verbatim.   It  is used for companion routines which call or
    +     are called by the scanner.  The presence of this section  is
    +     optional;  if it is missing, the second %% in the input file
    +     may be skipped, too.
    +
    +     In the definitions and rules sections, any indented text  or
    +     text  enclosed in %{ and %} is copied verbatim to the output
    +     (with the %{}'s removed).  The %{}'s must appear  unindented
    +     on lines by themselves.
    +
    +     In the rules section, any indented  or  %{}  text  appearing
    +     before the first rule may be used to declare variables which
    +     are local to the scanning routine and  (after  the  declara-
    +     tions)  code  which  is to be executed whenever the scanning
    +     routine is entered.  Other indented or %{} text in the  rule
    +     section  is  still  copied to the output, but its meaning is
    +     not well-defined and it may well cause  compile-time  errors
    +     (this feature is present for POSIX compliance; see below for
    +     other such features).
    +
    +     In the definitions section, an unindented comment  (i.e.,  a
    +     line  beginning  with  "/*")  is also copied verbatim to the
    +     output up to the next "*/".  Also, any line in  the  defini-
    +     tions  section  beginning  with  '#' is ignored, though this
    +     style of comment is  deprecated  and  may  go  away  in  the
    +     future.
    +
    +
    +
    +

    PATTERNS

    +     The patterns in the input are written using an extended  set
    +     of regular expressions.  These are:
    +
    +         x          match the character 'x'
    +         .          any character except newline
    +         [xyz]      a "character class"; in this case, the pattern
    +                      matches either an 'x', a 'y', or a 'z'
    +         [abj-oZ]   a "character class" with a range in it; matches
    +                      an 'a', a 'b', any letter from 'j' through 'o',
    +                      or a 'Z'
    +         [^A-Z]     a "negated character class", i.e., any character
    +                      but those in the class.  In this case, any
    +                      character EXCEPT an uppercase letter.
    +         [^A-Z\n]   any character EXCEPT an uppercase letter or
    +                      a newline
    +         r*         zero or more r's, where r is any regular expression
    +         r+         one or more r's
    +         r?         zero or one r's (that is, "an optional r")
    +         r{2,5}     anywhere from two to five r's
    +         r{2,}      two or more r's
    +         r{4}       exactly 4 r's
    +         {name}     the expansion of the "name" definition
    +                    (see above)
    +         "[xyz]\"foo"
    +                    the literal string: [xyz]"foo
    +         \X         if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
    +                      then the ANSI-C interpretation of \x.
    +                      Otherwise, a literal 'X' (used to escape
    +                      operators such as '*')
    +         \123       the character with octal value 123
    +         \x2a       the character with hexadecimal value 2a
    +         (r)        match an r; parentheses are used to override
    +                      precedence (see below)
    +
    +
    +         rs         the regular expression r followed by the
    +                      regular expression s; called "concatenation"
    +
    +
    +         r|s        either an r or an s
    +
    +
    +         r/s        an r but only if it is followed by an s.  The
    +                      s is not part of the matched text.  This type
    +                      of pattern is called as "trailing context".
    +         ^r         an r, but only at the beginning of a line
    +         r$         an r, but only at the end of a line.  Equivalent
    +                      to "r/\n".
    +
    +
    +         <s>r       an r, but only in start condition s (see
    +                    below for discussion of start conditions)
    +         <s1,s2,s3>r
    +                    same, but in any of start conditions s1,
    +                    s2, or s3
    +
    +         <<EOF>>    an end-of-file
    +         <s1,s2><<EOF>>
    +                    an end-of-file when in start condition s1 or s2
    +
    +     The regular expressions listed above are  grouped  according
    +     to  precedence, from highest precedence at the top to lowest
    +     at the bottom.   Those  grouped  together  have  equal  pre-
    +     cedence.  For example,
    +
    +         foo|bar*
    +
    +     is the same as
    +
    +         (foo)|(ba(r*))
    +
    +     since the '*' operator has higher precedence than concatena-
    +     tion, and concatenation higher than alternation ('|').  This
    +     pattern therefore matches either the  string  "foo"  or  the
    +     string "ba" followed by zero-or-more r's.  To match "foo" or
    +     zero-or-more "bar"'s, use:
    +
    +         foo|(bar)*
    +
    +     and to match zero-or-more "foo"'s-or-"bar"'s:
    +
    +         (foo|bar)*
    +
    +
    +     Some notes on patterns:
    +
    +     -    A negated character class such as the example  "[^A-Z]"
    +          above   will   match  a  newline  unless  "\n"  (or  an
    +          equivalent escape sequence) is one  of  the  characters
    +          explicitly  present  in  the  negated  character  class
    +          (e.g., "[^A-Z\n]").  This is unlike how many other reg-
    +          ular  expression tools treat negated character classes,
    +          but unfortunately  the  inconsistency  is  historically
    +          entrenched.   Matching  newlines  means  that a pattern
    +          like [^"]* can match an entire input  (overflowing  the
    +          scanner's input buffer) unless there's another quote in
    +          the input.
    +
    +     -    A rule can have at most one instance of  trailing  con-
    +          text (the '/' operator or the '$' operator).  The start
    +          condition, '^', and "<<EOF>>" patterns can  only  occur
    +          at the beginning of a pattern, and, as well as with '/'
    +          and '$', cannot be grouped inside parentheses.   A  '^'
    +          which  does  not  occur at the beginning of a rule or a
    +          '$' which does not occur at the end of a rule loses its
    +          special  properties  and is treated as a normal charac-
    +          ter.
    +
    +          The following are illegal:
    +
    +              foo/bar$
    +              <sc1>foo<sc2>bar
    +
    +          Note  that  the  first  of  these,   can   be   written
    +          "foo/bar\n".
    +
    +          The following will result in '$' or '^'  being  treated
    +          as a normal character:
    +
    +              foo|(bar$)
    +              foo|^bar
    +
    +          If what's wanted is a  "foo"  or  a  bar-followed-by-a-
    +          newline,  the  following could be used (the special '|'
    +          action is explained below):
    +
    +              foo      |
    +              bar$     /* action goes here */
    +
    +          A similar trick will work for matching a foo or a  bar-
    +          at-the-beginning-of-a-line.
    +
    +
    +
    +

    HOW THE INPUT IS MATCHED

    +     When the generated scanner is run,  it  analyzes  its  input
    +     looking  for strings which match any of its patterns.  If it
    +     finds more than one match, it takes  the  one  matching  the
    +     most  text  (for  trailing  context rules, this includes the
    +     length of the trailing part, even though  it  will  then  be
    +     returned  to the input).  If it finds two or more matches of
    +     the same length, the rule listed first  in  the  flex  input
    +     file is chosen.
    +
    +     Once the match is determined, the text corresponding to  the
    +     match  (called  the  token)  is made available in the global
    +     character pointer yytext,  and  its  length  in  the  global
    +     integer yyleng. The action corresponding to the matched pat-
    +     tern is  then  executed  (a  more  detailed  description  of
    +     actions  follows),  and  then the remaining input is scanned
    +     for another match.
    +
    +     If no match is found, then the default rule is executed: the
    +     next character in the input is considered matched and copied
    +     to the standard output.  Thus, the simplest legal flex input
    +     is:
    +
    +         %%
    +
    +     which generates a scanner that simply copies its input  (one
    +     character at a time) to its output.
    +
    +
    +
    +

    ACTIONS

    +     Each pattern in a rule has a corresponding action, which can
    +     be any arbitrary C statement.  The pattern ends at the first
    +     non-escaped whitespace character; the remainder of the  line
    +     is  its  action.  If the action is empty, then when the pat-
    +     tern is matched the input token is  simply  discarded.   For
    +     example,  here  is  the  specification  for  a program which
    +     deletes all occurrences of "zap me" from its input:
    +
    +         %%
    +         "zap me"
    +
    +     (It will copy all other characters in the input to the  out-
    +     put since they will be matched by the default rule.)
    +
    +     Here is a program which compresses multiple blanks and  tabs
    +     down  to a single blank, and throws away whitespace found at
    +     the end of a line:
    +
    +         %%
    +         [ \t]+        putchar( ' ' );
    +         [ \t]+$       /* ignore this token */
    +
    +
    +     If the action contains a '{', then the action spans till the
    +     balancing  '}'  is  found, and the action may cross multiple
    +     lines.  flex knows about C strings and comments and won't be
    +     fooled  by braces found within them, but also allows actions
    +     to begin with %{ and will consider the action to be all  the
    +     text up to the next %} (regardless of ordinary braces inside
    +     the action).
    +
    +     An action consisting solely of a vertical  bar  ('|')  means
    +     "same  as  the  action for the next rule."  See below for an
    +     illustration.
    +
    +     Actions can  include  arbitrary  C  code,  including  return
    +     statements  to  return  a  value  to whatever routine called
    +     yylex(). Each time yylex() is called it continues processing
    +     tokens  from  where it last left off until it either reaches
    +     the end of the file or executes a return.  Once  it  reaches
    +     an end-of-file, however, then any subsequent call to yylex()
    +     will simply immediately return, unless yyrestart() is  first
    +     called (see below).
    +
    +     Actions are not allowed to modify yytext or yyleng.
    +
    +     There are a  number  of  special  directives  which  can  be
    +     included within an action:
    +
    +     -    ECHO copies yytext to the scanner's output.
    +
    +     -    BEGIN followed by the name of a start condition  places
    +          the  scanner  in the corresponding start condition (see
    +          below).
    +
    +     -    REJECT directs the scanner to proceed on to the "second
    +          best"  rule which matched the input (or a prefix of the
    +          input).  The rule is chosen as described above in  "How
    +          the  Input  is  Matched",  and yytext and yyleng set up
    +          appropriately.  It may either be one which  matched  as
    +          much  text as the originally chosen rule but came later
    +          in the flex input file, or one which matched less text.
    +          For example, the following will both count the words in
    +          the input  and  call  the  routine  special()  whenever
    +          "frob" is seen:
    +
    +                      int word_count = 0;
    +              %%
    +
    +              frob        special(); REJECT;
    +              [^ \t\n]+   ++word_count;
    +
    +          Without the REJECT, any "frob"'s in the input would not
    +          be  counted  as  words, since the scanner normally exe-
    +          cutes only one action per token.  Multiple REJECT's are
    +          allowed,  each  one finding the next best choice to the
    +          currently active rule.  For example, when the following
    +          scanner  scans the token "abcd", it will write "abcdab-
    +          caba" to the output:
    +
    +              %%
    +              a        |
    +              ab       |
    +              abc      |
    +              abcd     ECHO; REJECT;
    +              .|\n     /* eat up any unmatched character */
    +
    +          (The first three rules share the fourth's action  since
    +          they use the special '|' action.)  REJECT is a particu-
    +          larly expensive feature in terms  scanner  performance;
    +          if  it  is used in any of the scanner's actions it will
    +          slow down all of the scanner's matching.   Furthermore,
    +          REJECT  cannot  be  used with the -f or -F options (see
    +          below).
    +
    +          Note also that unlike the other special actions, REJECT
    +          is  a  branch;  code  immediately  following  it in the
    +          action will not be executed.
    +
    +     -    yymore() tells  the  scanner  that  the  next  time  it
    +          matches  a  rule,  the  corresponding  token  should be
    +          appended onto the current value of yytext  rather  than
    +          replacing  it.   For  example,  given  the input "mega-
    +          kludge" the following will write "mega-mega-kludge"  to
    +          the output:
    +
    +              %%
    +              mega-    ECHO; yymore();
    +              kludge   ECHO;
    +
    +          First "mega-" is matched  and  echoed  to  the  output.
    +          Then  "kludge"  is matched, but the previous "mega-" is
    +          still hanging around at the beginning of yytext so  the
    +          ECHO  for  the "kludge" rule will actually write "mega-
    +          kludge".  The presence of  yymore()  in  the  scanner's
    +          action  entails  a  minor  performance  penalty  in the
    +          scanner's matching speed.
    +
    +     -    yyless(n) returns all but the first n characters of the
    +          current token back to the input stream, where they will
    +          be rescanned when the scanner looks for the next match.
    +          yytext  and  yyleng  are  adjusted appropriately (e.g.,
    +          yyleng will now be equal to n ).  For example,  on  the
    +          input  "foobar"  the  following will write out "foobar-
    +          bar":
    +
    +              %%
    +              foobar    ECHO; yyless(3);
    +              [a-z]+    ECHO;
    +
    +          An argument of  0  to  yyless  will  cause  the  entire
    +          current  input  string  to  be  scanned  again.  Unless
    +          you've changed how the scanner will  subsequently  pro-
    +          cess  its  input  (using BEGIN, for example), this will
    +          result in an endless loop.
    +
    +     -    unput(c) puts the  character  c  back  onto  the  input
    +          stream.   It  will  be the next character scanned.  The
    +          following action will take the current token and  cause
    +          it to be rescanned enclosed in parentheses.
    +
    +              {
    +              int i;
    +              unput( ')' );
    +              for ( i = yyleng - 1; i >= 0; --i )
    +                  unput( yytext[i] );
    +              unput( '(' );
    +              }
    +
    +          Note that since each unput() puts the  given  character
    +          back at the beginning of the input stream, pushing back
    +          strings must be done back-to-front.
    +
    +     -    input() reads the next character from the input stream.
    +          For  example,  the  following  is  one  way to eat up C
    +          comments:
    +
    +              %%
    +              "/*"        {
    +                          register int c;
    +
    +                          for ( ; ; )
    +                              {
    +                              while ( (c = input()) != '*' &&
    +                                      c != EOF )
    +                                  ;    /* eat up text of comment */
    +
    +                              if ( c == '*' )
    +                                  {
    +                                  while ( (c = input()) == '*' )
    +                                      ;
    +                                  if ( c == '/' )
    +                                      break;    /* found the end */
    +                                  }
    +
    +                              if ( c == EOF )
    +                                  {
    +                                  error( "EOF in comment" );
    +                                  break;
    +                                  }
    +                              }
    +                          }
    +
    +          (Note that if the scanner is compiled using  C++,  then
    +          input()  is  instead referred to as yyinput(), in order
    +          to avoid a name clash with the C++ stream by  the  name
    +          of input.)
    +
    +     -    yyterminate() can be used in lieu of a return statement
    +          in  an action.  It terminates the scanner and returns a
    +          0 to the scanner's caller, indicating "all done".  Sub-
    +          sequent  calls  to  the scanner will immediately return
    +          unless preceded by a call to yyrestart()  (see  below).
    +          By  default,  yyterminate() is also called when an end-
    +          of-file is encountered.  It is a macro and may be rede-
    +          fined.
    +
    +
    +
    +

    THE GENERATED SCANNER

    +     The output of flex is the file lex.yy.c, which contains  the
    +     scanning  routine yylex(), a number of tables used by it for
    +     matching tokens, and a number of auxiliary routines and mac-
    +     ros.  By default, yylex() is declared as follows:
    +
    +         int yylex()
    +             {
    +             ... various definitions and the actions in here ...
    +             }
    +
    +     (If your environment supports function prototypes,  then  it
    +     will  be  "int  yylex(  void  )".)   This  definition may be
    +     changed by redefining the "YY_DECL" macro.  For example, you
    +     could use:
    +
    +         #undef YY_DECL
    +         #define YY_DECL float lexscan( a, b ) float a, b;
    +
    +     to give the scanning routine the name lexscan,  returning  a
    +     float, and taking two floats as arguments.  Note that if you
    +     give  arguments  to  the  scanning  routine  using  a   K&R-
    +     style/non-prototyped  function  declaration,  you  must ter-
    +     minate the definition with a semi-colon (;).
    +
    +     Whenever yylex() is called, it scans tokens from the  global
    +     input  file  yyin  (which  defaults to stdin).  It continues
    +     until it either reaches an end-of-file (at  which  point  it
    +     returns the value 0) or one of its actions executes a return
    +     statement.  In  the  former  case,  when  called  again  the
    +     scanner will immediately return unless yyrestart() is called
    +     to point yyin at the new input file.   (  yyrestart()  takes
    +     one  argument, a FILE * pointer.)  In the latter case (i.e.,
    +     when an action executes a return), the scanner may  then  be
    +     called again and it will resume scanning where it left off.
    +
    +     By default (and for purposes  of  efficiency),  the  scanner
    +     uses  block-reads  rather  than  simple getc() calls to read
    +     characters from yyin. The nature of how it  gets  its  input
    +     can   be   controlled  by  redefining  the  YY_INPUT  macro.
    +     YY_INPUT's           calling           sequence           is
    +     "YY_INPUT(buf,result,max_size)".   Its action is to place up
    +     to max_size characters in the character array buf and return
    +     in  the integer variable result either the number of charac-
    +     ters read or the constant YY_NULL (0  on  Unix  systems)  to
    +     indicate  EOF.   The  default YY_INPUT reads from the global
    +     file-pointer "yyin".
    +
    +     A sample redefinition of YY_INPUT (in the  definitions  sec-
    +     tion of the input file):
    +
    +         %{
    +         #undef YY_INPUT
    +         #define YY_INPUT(buf,result,max_size) \
    +             { \
    +             int c = getchar(); \
    +             result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
    +             }
    +         %}
    +
    +     This definition will change the input  processing  to  occur
    +     one character at a time.
    +
    +     You also can add in things like keeping track of  the  input
    +     line  number  this  way; but don't expect your scanner to go
    +     very fast.
    +
    +     When the scanner receives  an  end-of-file  indication  from
    +     YY_INPUT, it then checks the yywrap() function.  If yywrap()
    +     returns false (zero), then it is assumed that  the  function
    +     has  gone  ahead  and  set up yyin to point to another input
    +     file, and scanning continues.   If  it  returns  true  (non-
    +     zero),  then  the  scanner  terminates,  returning  0 to its
    +     caller.
    +
    +     The default yywrap() always returns 1.  Presently, to  rede-
    +     fine  it  you must first "#undef yywrap", as it is currently
    +     implemented as a macro.  As indicated by the hedging in  the
    +     previous  sentence,  it may be changed to a true function in
    +     the near future.
    +
    +     The scanner writes its  ECHO  output  to  the  yyout  global
    +     (default, stdout), which may be redefined by the user simply
    +     by assigning it to some other FILE pointer.
    +
    +
    +
    +

    START CONDITIONS

    +     flex  provides  a  mechanism  for  conditionally  activating
    +     rules.   Any rule whose pattern is prefixed with "<sc>" will
    +     only be active when the scanner is in  the  start  condition
    +     named "sc".  For example,
    +
    +         <STRING>[^"]*        { /* eat up the string body ... */
    +                     ...
    +                     }
    +
    +     will be active only when the  scanner  is  in  the  "STRING"
    +     start condition, and
    +
    +         <INITIAL,STRING,QUOTE>\.        { /* handle an escape ... */
    +                     ...
    +                     }
    +
    +     will be active only when  the  current  start  condition  is
    +     either "INITIAL", "STRING", or "QUOTE".
    +
    +     Start conditions are declared  in  the  definitions  (first)
    +     section  of  the input using unindented lines beginning with
    +     either %s or %x followed by a list  of  names.   The  former
    +     declares  inclusive  start  conditions, the latter exclusive
    +     start conditions.  A start condition is activated using  the
    +     BEGIN  action.   Until  the  next  BEGIN action is executed,
    +     rules with the given start  condition  will  be  active  and
    +     rules  with other start conditions will be inactive.  If the
    +     start condition is inclusive, then rules with no start  con-
    +     ditions  at  all  will  also be active.  If it is exclusive,
    +     then only rules qualified with the start condition  will  be
    +     active.   A  set  of  rules contingent on the same exclusive
    +     start condition describe a scanner which is  independent  of
    +     any  of the other rules in the flex input.  Because of this,
    +     exclusive start conditions make it easy  to  specify  "mini-
    +     scanners"  which scan portions of the input that are syntac-
    +     tically different from the rest (e.g., comments).
    +
    +     If the distinction between  inclusive  and  exclusive  start
    +     conditions  is still a little vague, here's a simple example
    +     illustrating the connection between the  two.   The  set  of
    +     rules:
    +
    +         %s example
    +         %%
    +         <example>foo           /* do something */
    +
    +     is equivalent to
    +
    +         %x example
    +         %%
    +         <INITIAL,example>foo   /* do something */
    +
    +
    +     The default rule (to ECHO any unmatched  character)  remains
    +     active in start conditions.
    +
    +     BEGIN(0) returns to the original state where only the  rules
    +     with no start conditions are active.  This state can also be
    +     referred   to   as   the   start-condition   "INITIAL",   so
    +     BEGIN(INITIAL)  is  equivalent to BEGIN(0). (The parentheses
    +     around the start condition name are  not  required  but  are
    +     considered good style.)
    +
    +     BEGIN actions can also be given  as  indented  code  at  the
    +     beginning  of the rules section.  For example, the following
    +     will cause the scanner to enter the "SPECIAL"  start  condi-
    +     tion  whenever  yylex()  is  called  and the global variable
    +     enter_special is true:
    +
    +                 int enter_special;
    +
    +         %x SPECIAL
    +         %%
    +                 if ( enter_special )
    +                     BEGIN(SPECIAL);
    +
    +         <SPECIAL>blahblahblah
    +         ...more rules follow...
    +
    +
    +
    +     To illustrate the  uses  of  start  conditions,  here  is  a
    +     scanner  which  provides  two different interpretations of a
    +     string like "123.456".  By default it will treat  it  as  as
    +     three  tokens,  the  integer  "123",  a  dot  ('.'), and the
    +     integer "456".  But if the string is preceded earlier in the
    +     line  by  the  string  "expect-floats" it will treat it as a
    +     single token, the floating-point number 123.456:
    +
    +         %{
    +         #include <math.h>
    +         %}
    +         %s expect
    +
    +         %%
    +         expect-floats        BEGIN(expect);
    +
    +         <expect>[0-9]+"."[0-9]+      {
    +                     printf( "found a float, = %f\n",
    +                             atof( yytext ) );
    +                     }
    +         <expect>\n           {
    +                     /* that's the end of the line, so
    +                      * we need another "expect-number"
    +                      * before we'll recognize any more
    +                      * numbers
    +                      */
    +                     BEGIN(INITIAL);
    +                     }
    +
    +         [0-9]+      {
    +                     printf( "found an integer, = %d\n",
    +                             atoi( yytext ) );
    +                     }
    +
    +         "."         printf( "found a dot\n" );
    +
    +     Here is a scanner which recognizes (and discards) C comments
    +     while maintaining a count of the current input line.
    +
    +         %x comment
    +         %%
    +                 int line_num = 1;
    +
    +         "/*"         BEGIN(comment);
    +
    +         <comment>[^*\n]*        /* eat anything that's not a '*' */
    +         <comment>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
    +         <comment>\n             ++line_num;
    +         <comment>"*"+"/"        BEGIN(INITIAL);
    +
    +     Note that start-conditions names are really  integer  values
    +     and  can  be  stored  as  such.   Thus,  the  above could be
    +     extended in the following fashion:
    +
    +         %x comment foo
    +         %%
    +                 int line_num = 1;
    +                 int comment_caller;
    +
    +         "/*"         {
    +                      comment_caller = INITIAL;
    +                      BEGIN(comment);
    +                      }
    +
    +         ...
    +
    +         <foo>"/*"    {
    +                      comment_caller = foo;
    +                      BEGIN(comment);
    +                      }
    +
    +         <comment>[^*\n]*        /* eat anything that's not a '*' */
    +         <comment>"*"+[^*/\n]*   /* eat up '*'s not followed by '/'s */
    +         <comment>\n             ++line_num;
    +         <comment>"*"+"/"        BEGIN(comment_caller);
    +
    +     One can then implement a "stack" of start  conditions  using
    +     an  array  of integers.  (It is likely that such stacks will
    +     become a full-fledged flex feature in  the  future.)   Note,
    +     though,  that  start  conditions do not have their own name-
    +     space; %s's and %x's declare names in the  same  fashion  as
    +     #define's.
    +
    +
    +
    +

    MULTIPLE INPUT BUFFERS

    +     Some scanners (such as those which support "include"  files)
    +     require   reading  from  several  input  streams.   As  flex
    +     scanners do a large amount of buffering, one cannot  control
    +     where  the  next input will be read from by simply writing a
    +     YY_INPUT  which  is  sensitive  to  the  scanning   context.
    +     YY_INPUT  is only called when the scanner reaches the end of
    +     its buffer, which may be a long time after scanning a state-
    +     ment such as an "include" which requires switching the input
    +     source.
    +
    +     To negotiate  these  sorts  of  problems,  flex  provides  a
    +     mechanism  for creating and switching between multiple input
    +     buffers.  An input buffer is created by using:
    +
    +         YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
    +
    +     which takes a FILE pointer and a size and creates  a  buffer
    +     associated with the given file and large enough to hold size
    +     characters (when in doubt, use YY_BUF_SIZE  for  the  size).
    +     It  returns  a  YY_BUFFER_STATE  handle,  which  may then be
    +     passed to other routines:
    +
    +         void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
    +
    +     switches the scanner's input  buffer  so  subsequent  tokens
    +     will  come  from new_buffer. Note that yy_switch_to_buffer()
    +     may be used by yywrap() to  sets  things  up  for  continued
    +     scanning, instead of opening a new file and pointing yyin at
    +     it.
    +
    +         void yy_delete_buffer( YY_BUFFER_STATE buffer )
    +
    +     is used to reclaim the storage associated with a buffer.
    +
    +     yy_new_buffer() is an alias for yy_create_buffer(), provided
    +     for  compatibility  with  the  C++ use of new and delete for
    +     creating and destroying dynamic objects.
    +
    +     Finally,   the    YY_CURRENT_BUFFER    macro    returns    a
    +     YY_BUFFER_STATE handle to the current buffer.
    +
    +     Here is an example of using these  features  for  writing  a
    +     scanner  which expands include files (the <<EOF>> feature is
    +     discussed below):
    +
    +         /* the "incl" state is used for picking up the name
    +          * of an include file
    +          */
    +         %x incl
    +
    +         %{
    +         #define MAX_INCLUDE_DEPTH 10
    +         YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
    +         int include_stack_ptr = 0;
    +         %}
    +
    +         %%
    +         include             BEGIN(incl);
    +
    +         [a-z]+              ECHO;
    +         [^a-z\n]*\n?        ECHO;
    +
    +         <incl>[ \t]*      /* eat the whitespace */
    +         <incl>[^ \t\n]+   { /* got the include file name */
    +                 if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
    +                     {
    +                     fprintf( stderr, "Includes nested too deeply" );
    +                     exit( 1 );
    +                     }
    +
    +                 include_stack[include_stack_ptr++] =
    +                     YY_CURRENT_BUFFER;
    +
    +                 yyin = fopen( yytext, "r" );
    +
    +                 if ( ! yyin )
    +                     error( ... );
    +
    +                 yy_switch_to_buffer(
    +                     yy_create_buffer( yyin, YY_BUF_SIZE ) );
    +
    +                 BEGIN(INITIAL);
    +                 }
    +
    +         <<EOF>> {
    +                 if ( --include_stack_ptr < 0 )
    +                     {
    +                     yyterminate();
    +                     }
    +
    +                 else
    +                     yy_switch_to_buffer(
    +                          include_stack[include_stack_ptr] );
    +                 }
    +
    +
    +
    +
    +

    END-OF-FILE RULES

    +     The special rule "<<EOF>>" indicates actions which are to be
    +     taken  when  an  end-of-file  is  encountered  and  yywrap()
    +     returns non-zero (i.e., indicates no further files  to  pro-
    +     cess).  The action must finish by doing one of four things:
    +
    +     -    the  special  YY_NEW_FILE  action,  if  yyin  has  been
    +          pointed at a new file to process;
    +
    +     -    a return statement;
    +
    +     -    the special yyterminate() action;
    +
    +     -    or,    switching    to    a    new     buffer     using
    +          yy_switch_to_buffer() as shown in the example above.
    +
    +     <<EOF>> rules may not be used with other patterns; they  may
    +     only  be  qualified  with a list of start conditions.  If an
    +     unqualified <<EOF>> rule is given, it applies to  all  start
    +     conditions  which  do  not already have <<EOF>> actions.  To
    +     specify an <<EOF>> rule for only the  initial  start  condi-
    +     tion, use
    +
    +         <INITIAL><<EOF>>
    +
    +
    +     These rules are useful for  catching  things  like  unclosed
    +     comments.  An example:
    +
    +         %x quote
    +         %%
    +
    +         ...other rules for dealing with quotes...
    +
    +         <quote><<EOF>>   {
    +                  error( "unterminated quote" );
    +                  yyterminate();
    +                  }
    +         <<EOF>>  {
    +                  if ( *++filelist )
    +                      {
    +                      yyin = fopen( *filelist, "r" );
    +                      YY_NEW_FILE;
    +                      }
    +                  else
    +                     yyterminate();
    +                  }
    +
    +
    +
    +
    +

    MISCELLANEOUS MACROS

    +     The macro YY_USER_ACTION can  be  redefined  to  provide  an
    +     action  which is always executed prior to the matched rule's
    +     action.  For example, it could be #define'd to call  a  rou-
    +     tine to convert yytext to lower-case.
    +
    +     The macro YY_USER_INIT may be redefined to provide an action
    +     which  is  always executed before the first scan (and before
    +     the scanner's internal initializations are done).  For exam-
    +     ple,  it  could  be used to call a routine to read in a data
    +     table or open a logging file.
    +
    +     In the generated scanner, the actions are  all  gathered  in
    +     one  large  switch  statement  and separated using YY_BREAK,
    +     which may be redefined.  By default, it is simply a "break",
    +     to  separate  each  rule's action from the following rule's.
    +     Redefining  YY_BREAK  allows,  for  example,  C++  users  to
    +     #define  YY_BREAK  to  do  nothing (while being very careful
    +     that every rule ends with a "break" or a "return"!) to avoid
    +     suffering  from unreachable statement warnings where because
    +     a rule's action ends with "return", the YY_BREAK is inacces-
    +     sible.
    +
    +
    +
    +

    INTERFACING WITH YACC

    +     One of the main uses of flex is as a companion to  the  yacc
    +     parser-generator.   yacc  parsers  expect  to call a routine
    +     named yylex() to find the next input token.  The routine  is
    +     supposed  to  return  the  type of the next token as well as
    +     putting any associated value in the global  yylval.  To  use
    +     flex  with  yacc,  one  specifies  the  -d option to yacc to
    +     instruct it to generate the file y.tab.h containing  defini-
    +     tions  of all the %tokens appearing in the yacc input.  This
    +     file is then included in the flex scanner.  For example,  if
    +     one of the tokens is "TOK_NUMBER", part of the scanner might
    +     look like:
    +
    +         %{
    +         #include "y.tab.h"
    +         %}
    +
    +         %%
    +
    +         [0-9]+        yylval = atoi( yytext ); return TOK_NUMBER;
    +
    +
    +
    +
    +

    TRANSLATION TABLE

    +     In the name of POSIX compliance, flex supports a translation
    +     table  for  mapping input characters into groups.  The table
    +     is specified in the first  section,  and  its  format  looks
    +     like:
    +
    +         %t
    +         1        abcd
    +         2        ABCDEFGHIJKLMNOPQRSTUVWXYZ
    +         52       0123456789
    +         6        \t\ \n
    +         %t
    +
    +     This example specifies that the characters  'a',  'b',  'c',
    +     and  'd'  are  to  all  be  lumped into group #1, upper-case
    +     letters in group #2, digits in group #52, tabs, blanks,  and
    +     newlines  into group #6, and no other characters will appear
    +     in the patterns.  The group numbers are actually disregarded
    +     by  flex;  %t  serves,  though, to lump characters together.
    +     Given the above table, for example, the pattern "a(AA)*5" is
    +     equivalent  to "d(ZQ)*0".  They both say, "match any charac-
    +     ter in group #1, followed by zero-or-more pairs  of  charac-
    +     ters from group #2, followed by a character from group #52."
    +     Thus %t provides a crude  way  for  introducing  equivalence
    +     classes into the scanner specification.
    +
    +     Note that  the  -i  option  (see  below)  coupled  with  the
    +     equivalence  classes which flex automatically generates take
    +     care of virtually all the instances when one might  consider
    +     using %t. But what the hell, it's there if you want it.
    +
    +
    +
    +

    OPTIONS

    +     flex has the following options:
    +
    +     -b   Generate  backtracking  information  to  lex.backtrack.
    +          This  is  a  list of scanner states which require back-
    +          tracking and the input characters on which they do  so.
    +          By adding rules one can remove backtracking states.  If
    +          all backtracking states are eliminated and -f or -F  is
    +          used, the generated scanner will run faster (see the -p
    +          flag).  Only users who wish to squeeze every last cycle
    +          out  of  their  scanners  need worry about this option.
    +          (See the section on PERFORMANCE CONSIDERATIONS below.)
    +
    +     -c   is a do-nothing, deprecated option included  for  POSIX
    +          compliance.
    +
    +          NOTE: in previous releases of flex -c specified  table-
    +          compression  options.   This functionality is now given
    +          by the -C flag.  To ease the the impact of this change,
    +          when  flex encounters -c, it currently issues a warning
    +          message and assumes that -C was  desired  instead.   In
    +          the future this "promotion" of -c to -C will go away in
    +          the name of full POSIX  compliance  (unless  the  POSIX
    +          meaning is removed first).
    +
    +     -d   makes the generated scanner run in debug  mode.   When-
    +          ever   a   pattern   is   recognized   and  the  global
    +          yy_flex_debug is non-zero (which is the  default),  the
    +          scanner will write to stderr a line of the form:
    +
    +              --accepting rule at line 53 ("the matched text")
    +
    +          The line number refers to the location of the  rule  in
    +          the  file defining the scanner (i.e., the file that was
    +          fed to flex).  Messages are  also  generated  when  the
    +          scanner  backtracks,  accepts the default rule, reaches
    +          the end of its input buffer (or encounters  a  NUL;  at
    +          this  point,  the  two  look  the  same  as  far as the
    +          scanner's concerned), or reaches an end-of-file.
    +
    +     -f   specifies (take your pick) full table or fast  scanner.
    +          No  table compression is done.  The result is large but
    +          fast.  This option is equivalent to -Cf (see below).
    +
    +     -i   instructs flex to generate a case-insensitive  scanner.
    +          The  case  of  letters given in the flex input patterns
    +          will be ignored,  and  tokens  in  the  input  will  be
    +          matched  regardless of case.  The matched text given in
    +          yytext will have the preserved case (i.e., it will  not
    +          be folded).
    +
    +     -n   is another do-nothing, deprecated option included  only
    +          for POSIX compliance.
    +
    +     -p   generates a performance report to stderr.   The  report
    +          consists  of  comments  regarding  features of the flex
    +          input file which will cause a loss  of  performance  in
    +          the resulting scanner.  Note that the use of REJECT and
    +          variable trailing context  (see  the  BUGS  section  in
    +          flex(1)) entails a substantial performance penalty; use
    +          of yymore(), the ^ operator, and  the  -I  flag  entail
    +          minor performance penalties.
    +
    +     -s   causes the default rule (that unmatched  scanner  input
    +          is  echoed to stdout) to be suppressed.  If the scanner
    +          encounters input that does not match any of its  rules,
    +          it  aborts  with  an  error.  This option is useful for
    +          finding holes in a scanner's rule set.
    +
    +     -t   instructs flex to write the  scanner  it  generates  to
    +          standard output instead of lex.yy.c.
    +
    +     -v   specifies that flex should write to stderr a summary of
    +          statistics regarding the scanner it generates.  Most of
    +          the statistics are meaningless to the casual flex user,
    +          but  the  first  line  identifies  the version of flex,
    +          which is useful for figuring out where you  stand  with
    +          respect  to  patches and new releases, and the next two
    +          lines give the date when the scanner was created and  a
    +          summary of the flags which were in effect.
    +
    +     -F   specifies that the fast  scanner  table  representation
    +          should  be  used.  This representation is about as fast
    +          as the full table representation  (-f),  and  for  some
    +          sets  of patterns will be considerably smaller (and for
    +          others, larger).  In general, if the pattern  set  con-
    +          tains  both  "keywords"  and  a catch-all, "identifier"
    +          rule, such as in the set:
    +
    +              "case"    return TOK_CASE;
    +              "switch"  return TOK_SWITCH;
    +              ...
    +              "default" return TOK_DEFAULT;
    +              [a-z]+    return TOK_ID;
    +
    +          then you're better off using the full table representa-
    +          tion.  If only the "identifier" rule is present and you
    +          then use a hash table or some such to detect  the  key-
    +          words, you're better off using -F.
    +
    +          This option is equivalent to -CF (see below).
    +
    +     -I   instructs flex  to  generate  an  interactive  scanner.
    +          Normally,  scanners generated by flex always look ahead
    +          one character before deciding  that  a  rule  has  been
    +          matched.   At  the cost of some scanning overhead, flex
    +          will generate a scanner which  only  looks  ahead  when
    +          needed.   Such  scanners are called interactive because
    +          if you want to write a scanner for an interactive  sys-
    +          tem such as a command shell, you will probably want the
    +          user's input to  be  terminated  with  a  newline,  and
    +          without  -I  the  user will have to type a character in
    +          addition to the newline in order to  have  the  newline
    +          recognized.  This leads to dreadful interactive perfor-
    +          mance.
    +
    +          If all this seems  to  confusing,  here's  the  general
    +          rule:  if  a  human  will  be  typing  in input to your
    +          scanner, use -I, otherwise don't;  if  you  don't  care
    +          about   squeezing  the  utmost  performance  from  your
    +          scanner and you don't  want  to  make  any  assumptions
    +          about the input to your scanner, use -I.
    +
    +          Note, -I cannot be used in  conjunction  with  full  or
    +          fast tables, i.e., the -f, -F, -Cf, or -CF flags.
    +
    +     -L   instructs  flex  not  to  generate  #line   directives.
    +          Without this option, flex peppers the generated scanner
    +          with #line directives so error messages in the  actions
    +          will  be correctly located with respect to the original
    +          flex input file, and not to the fairly meaningless line
    +          numbers  of  lex.yy.c.  (Unfortunately  flex  does  not
    +          presently generate the necessary directives to  "retar-
    +          get" the line numbers for those parts of lex.yy.c which
    +          it generated.  So if there is an error in the generated
    +          code, a meaningless line number is reported.)
    +
    +     -T   makes flex run in trace mode.  It will generate  a  lot
    +          of  messages to stdout concerning the form of the input
    +          and the resultant non-deterministic  and  deterministic
    +          finite  automata.   This  option  is  mostly for use in
    +          maintaining flex.
    +
    +     -8   instructs flex to generate an 8-bit scanner, i.e.,  one
    +          which  can  recognize 8-bit characters.  On some sites,
    +          flex is installed with this option as the default.   On
    +          others,  the default is 7-bit characters.  To see which
    +          is  the  case,  check  the  verbose  (-v)  output   for
    +          "equivalence  classes  created".  If the denominator of
    +          the number shown is 128, then by default flex  is  gen-
    +          erating  7-bit  characters.   If  it  is  256, then the
    +          default is 8-bit characters and  the  -8  flag  is  not
    +          required  (but  may  be a good idea to keep the scanner
    +          specification portable).  Feeding a 7-bit scanner 8-bit
    +          characters  will  result in infinite loops, bus errors,
    +          or other such fireworks, so  when  in  doubt,  use  the
    +          flag.  Note that if equivalence classes are used, 8-bit
    +          scanners take only slightly more table space than 7-bit
    +          scanners  (128  bytes,  to  be  exact);  if equivalence
    +          classes are not used, however, then the tables may grow
    +          up to twice their 7-bit size.
    +
    +     -C[efmF]
    +          controls the degree of table compression.
    +          -Ce directs  flex  to  construct  equivalence  classes,
    +          i.e.,  sets  of characters which have identical lexical
    +          properties (for example,  if  the  only  appearance  of
    +          digits  in  the  flex  input  is in the character class
    +          "[0-9]" then the digits '0', '1', ..., '9' will all  be
    +          put   in  the  same  equivalence  class).   Equivalence
    +          classes usually give dramatic reductions in  the  final
    +          table/object file sizes (typically a factor of 2-5) and
    +          are pretty cheap performance-wise  (one  array  look-up
    +          per character scanned).
    +
    +          -Cf specifies that the full scanner  tables  should  be
    +          generated - flex should not compress the tables by tak-
    +          ing advantages of similar transition functions for dif-
    +          ferent states.
    +
    +          -CF specifies that the alternate fast scanner represen-
    +          tation  (described  above  under the -F flag) should be
    +          used.
    +
    +          -Cm directs flex to construct meta-equivalence classes,
    +          which  are  sets of equivalence classes (or characters,
    +          if equivalence classes are not  being  used)  that  are
    +          commonly  used  together.  Meta-equivalence classes are
    +          often a big win when using compressed tables, but  they
    +          have  a  moderate  performance  impact (one or two "if"
    +          tests and one array look-up per character scanned).
    +
    +          A lone -C specifies that the scanner tables  should  be
    +          compressed  but  neither  equivalence classes nor meta-
    +          equivalence classes should be used.
    +
    +          The options -Cf or  -CF  and  -Cm  do  not  make  sense
    +          together - there is no opportunity for meta-equivalence
    +          classes if the table is not being  compressed.   Other-
    +          wise the options may be freely mixed.
    +
    +          The default setting is -Cem, which specifies that  flex
    +          should   generate   equivalence   classes   and   meta-
    +          equivalence classes.  This setting provides the highest
    +          degree   of  table  compression.   You  can  trade  off
    +          faster-executing scanners at the cost of larger  tables
    +          with the following generally being true:
    +
    +              slowest & smallest
    +                    -Cem
    +                    -Cm
    +                    -Ce
    +                    -C
    +                    -C{f,F}e
    +                    -C{f,F}
    +              fastest & largest
    +
    +          Note that scanners with the smallest tables are usually
    +          generated and compiled the quickest, so during develop-
    +          ment you will usually want to use the default,  maximal
    +          compression.
    +
    +          -Cfe is often a good compromise between speed and  size
    +          for production scanners.
    +
    +          -C options are not cumulative;  whenever  the  flag  is
    +          encountered, the previous -C settings are forgotten.
    +
    +     -Sskeleton_file
    +          overrides the default skeleton  file  from  which  flex
    +          constructs its scanners.  You'll never need this option
    +          unless you are doing flex maintenance or development.
    +
    +
    +
    +

    PERFORMANCE CONSIDERATIONS

    +     The main design goal of  flex  is  that  it  generate  high-
    +     performance  scanners.   It  has  been optimized for dealing
    +     well with large sets of rules.  Aside from  the  effects  of
    +     table compression on scanner speed outlined above, there are
    +     a  number  of  options/actions  which  degrade  performance.
    +     These are, from most expensive to least:
    +
    +         REJECT
    +
    +         pattern sets that require backtracking
    +         arbitrary trailing context
    +
    +         '^' beginning-of-line operator
    +         yymore()
    +
    +     with the first three all being quite expensive and the  last
    +     two being quite cheap.
    +
    +     REJECT should be avoided at all costs  when  performance  is
    +     important.  It is a particularly expensive option.
    +
    +     Getting rid of backtracking is messy and  often  may  be  an
    +     enormous amount of work for a complicated scanner.  In prin-
    +     cipal, one begins  by  using  the  -b  flag  to  generate  a
    +     lex.backtrack file.  For example, on the input
    +
    +         %%
    +         foo        return TOK_KEYWORD;
    +         foobar     return TOK_KEYWORD;
    +
    +     the file looks like:
    +
    +         State #6 is non-accepting -
    +          associated rule line numbers:
    +                2       3
    +
    +          out-transitions: [ o ]
    +          jam-transitions: EOF [ \001-n  p-\177 ]
    +
    +         State #8 is non-accepting -
    +          associated rule line numbers:
    +                3
    +          out-transitions: [ a ]
    +          jam-transitions: EOF [ \001-`  b-\177 ]
    +
    +         State #9 is non-accepting -
    +          associated rule line numbers:
    +                3
    +          out-transitions: [ r ]
    +          jam-transitions: EOF [ \001-q  s-\177 ]
    +
    +         Compressed tables always backtrack.
    +
    +     The first few lines tell us that there's a scanner state  in
    +     which  it  can  make  a  transition on an 'o' but not on any
    +     other character,  and  that  in  that  state  the  currently
    +     scanned text does not match any rule.  The state occurs when
    +     trying to match the rules found at lines  2  and  3  in  the
    +     input  file.  If the scanner is in that state and then reads
    +     something other than an 'o', it will have  to  backtrack  to
    +     find  a rule which is matched.  With a bit of headscratching
    +     one can see that this must be the state it's in when it  has
    +     seen  "fo".   When this has happened, if anything other than
    +     another 'o' is seen, the scanner will have  to  back  up  to
    +     simply match the 'f' (by the default rule).
    +
    +     The comment regarding State #8 indicates there's  a  problem
    +     when  "foob"  has  been  scanned.   Indeed, on any character
    +     other than a 'b', the scanner will have to back up to accept
    +     "foo".   Similarly,  the  comment for State #9 concerns when
    +     "fooba" has been scanned.
    +
    +     The final comment reminds us that there's no point going  to
    +     all  the  trouble  of  removing  backtracking from the rules
    +     unless we're using -f or -F, since  there's  no  performance
    +     gain doing so with compressed scanners.
    +
    +     The way to remove the backtracking is to add "error" rules:
    +
    +         %%
    +         foo         return TOK_KEYWORD;
    +         foobar      return TOK_KEYWORD;
    +
    +         fooba       |
    +         foob        |
    +         fo          {
    +                     /* false alarm, not really a keyword */
    +                     return TOK_ID;
    +                     }
    +
    +
    +     Eliminating backtracking among a list of keywords  can  also
    +     be done using a "catch-all" rule:
    +
    +         %%
    +         foo         return TOK_KEYWORD;
    +         foobar      return TOK_KEYWORD;
    +
    +         [a-z]+      return TOK_ID;
    +
    +     This is usually the best solution when appropriate.
    +
    +     Backtracking messages tend to cascade.  With  a  complicated
    +     set  of rules it's not uncommon to get hundreds of messages.
    +     If one can decipher them, though,  it  often  only  takes  a
    +     dozen or so rules to eliminate the backtracking (though it's
    +     easy to make a mistake and have an error  rule  accidentally
    +     match a valid token.  A possible future flex feature will be
    +     to automatically add rules to eliminate backtracking).
    +
    +     Variable trailing context (where both the leading and trail-
    +     ing  parts  do  not  have a fixed length) entails almost the
    +     same performance loss as  REJECT  (i.e.,  substantial).   So
    +     when possible a rule like:
    +
    +         %%
    +         mouse|rat/(cat|dog)   run();
    +
    +     is better written:
    +
    +         %%
    +         mouse/cat|dog         run();
    +         rat/cat|dog           run();
    +
    +     or as
    +
    +         %%
    +         mouse|rat/cat         run();
    +         mouse|rat/dog         run();
    +
    +     Note that here the special '|' action does not  provide  any
    +     savings,  and  can  even  make  things  worse  (see  BUGS in
    +     flex(1)).
    +
    +     Another area where the user can increase a scanner's perfor-
    +     mance  (and  one that's easier to implement) arises from the
    +     fact that the longer the  tokens  matched,  the  faster  the
    +     scanner will run.  This is because with long tokens the pro-
    +     cessing of most input characters takes place in the  (short)
    +     inner  scanning  loop, and does not often have to go through
    +     the additional work of setting up the  scanning  environment
    +     (e.g.,  yytext)  for  the  action.  Recall the scanner for C
    +     comments:
    +
    +         %x comment
    +         %%
    +                 int line_num = 1;
    +
    +         "/*"         BEGIN(comment);
    +
    +         <comment>[^*\n]*
    +         <comment>"*"+[^*/\n]*
    +         <comment>\n             ++line_num;
    +         <comment>"*"+"/"        BEGIN(INITIAL);
    +
    +     This could be sped up by writing it as:
    +
    +         %x comment
    +         %%
    +                 int line_num = 1;
    +
    +         "/*"         BEGIN(comment);
    +
    +         <comment>[^*\n]*
    +         <comment>[^*\n]*\n      ++line_num;
    +         <comment>"*"+[^*/\n]*
    +         <comment>"*"+[^*/\n]*\n ++line_num;
    +         <comment>"*"+"/"        BEGIN(INITIAL);
    +
    +     Now instead of each  newline  requiring  the  processing  of
    +     another  action,  recognizing  the newlines is "distributed"
    +     over the other rules to keep the matched  text  as  long  as
    +     possible.   Note  that  adding  rules does not slow down the
    +     scanner!  The speed of the scanner  is  independent  of  the
    +     number  of  rules or (modulo the considerations given at the
    +     beginning of this section) how  complicated  the  rules  are
    +     with regard to operators such as '*' and '|'.
    +
    +     A final example in speeding up a scanner: suppose  you  want
    +     to  scan through a file containing identifiers and keywords,
    +     one per line and with no other  extraneous  characters,  and
    +     recognize all the keywords.  A natural first approach is:
    +
    +         %%
    +         asm      |
    +         auto     |
    +         break    |
    +         ... etc ...
    +         volatile |
    +         while    /* it's a keyword */
    +
    +         .|\n     /* it's not a keyword */
    +
    +     To eliminate the back-tracking, introduce a catch-all rule:
    +
    +         %%
    +         asm      |
    +         auto     |
    +         break    |
    +         ... etc ...
    +         volatile |
    +         while    /* it's a keyword */
    +
    +         [a-z]+   |
    +         .|\n     /* it's not a keyword */
    +
    +     Now, if it's guaranteed that there's exactly  one  word  per
    +     line,  then  we  can reduce the total number of matches by a
    +     half by merging in the recognition of newlines with that  of
    +     the other tokens:
    +
    +         %%
    +         asm\n    |
    +         auto\n   |
    +         break\n  |
    +         ... etc ...
    +         volatile\n |
    +         while\n  /* it's a keyword */
    +
    +         [a-z]+\n |
    +         .|\n     /* it's not a keyword */
    +
    +     One has to be careful here,  as  we  have  now  reintroduced
    +     backtracking into the scanner.  In particular, while we know
    +     that there will never be any characters in the input  stream
    +     other  than letters or newlines, flex can't figure this out,
    +     and it will plan for possibly needing backtracking  when  it
    +     has  scanned a token like "auto" and then the next character
    +     is something other than a newline or a  letter.   Previously
    +     it  would  then  just match the "auto" rule and be done, but
    +     now it has no "auto" rule, only a "auto\n" rule.   To  elim-
    +     inate  the  possibility  of  backtracking,  we  could either
    +     duplicate all rules but without final newlines, or, since we
    +     never  expect to encounter such an input and therefore don't
    +     how it's classified, we can  introduce  one  more  catch-all
    +     rule, this one which doesn't include a newline:
    +
    +         %%
    +         asm\n    |
    +         auto\n   |
    +         break\n  |
    +         ... etc ...
    +         volatile\n |
    +         while\n  /* it's a keyword */
    +
    +         [a-z]+\n |
    +         [a-z]+   |
    +         .|\n     /* it's not a keyword */
    +
    +     Compiled with -Cf, this is about as fast as one  can  get  a
    +     flex scanner to go for this particular problem.
    +
    +     A final note:  flex is slow when  matching  NUL's,  particu-
    +     larly  when  a  token contains multiple NUL's.  It's best to
    +     write rules which match short amounts of text if it's  anti-
    +     cipated that the text will often include NUL's.
    +
    +
    +
    +

    INCOMPATIBILITIES WITH LEX AND POSIX

    +     flex is a rewrite of the Unix lex tool (the two  implementa-
    +     tions  do  not share any code, though), with some extensions
    +     and incompatibilities, both of which are of concern to those
    +     who  wish to write scanners acceptable to either implementa-
    +     tion.  At present, the POSIX lex draft is very close to  the
    +     original lex implementation, so some of these incompatibili-
    +     ties are also in conflict with the  POSIX  draft.   But  the
    +     intent  is  that except as noted below, flex as it presently
    +     stands will ultimately be POSIX conformant (i.e., that those
    +     areas  of  conflict with the POSIX draft will be resolved in
    +     flex's favor).  Please bear in mind that  all  the  comments
    +     which  follow are with regard to the POSIX draft standard of
    +     Summer 1989, and  not  the  final  document  (or  subsequent
    +     drafts); they are included so flex users can be aware of the
    +     standardization issues and those areas where flex may in the
    +     near  future  undergo  changes incompatible with its current
    +     definition.
    +
    +     flex is fully compatible with lex with the following  excep-
    +     tions:
    +
    +     -    The undocumented lex scanner internal variable yylineno
    +          is  not  supported.   It  is  difficult to support this
    +          option efficiently, since it requires  examining  every
    +          character  scanned  and reexamining the characters when
    +          the scanner backs up.  Things get more complicated when
    +          the  end  of  buffer  or  file  is  reached or a NUL is
    +          scanned (since the scan must then be restarted with the
    +          proper  line  number  count),  or  the  user  uses  the
    +          yyless(), unput(), or REJECT actions, or  the  multiple
    +          input buffer functions.
    +
    +          The fix is to add rules which, upon seeing  a  newline,
    +          increment  yylineno.   This is usually an easy process,
    +          though it can be a drag if some  of  the  patterns  can
    +          match multiple newlines along with other characters.
    +
    +          yylineno is not part of the POSIX draft.
    +
    +     -    The input() routine is not redefinable, though  it  may
    +          be  called  to  read  characters following whatever has
    +          been matched by a rule.  If input() encounters an  end-
    +          of-file  the  normal  yywrap()  processing  is done.  A
    +          ``real'' end-of-file is returned by input() as EOF.
    +
    +          Input is instead controlled by redefining the  YY_INPUT
    +          macro.
    +
    +          The flex restriction that input() cannot  be  redefined
    +          is in accordance with the POSIX draft, but YY_INPUT has
    +          not yet been accepted  into  the  draft  (and  probably
    +          won't;  it looks like the draft will simply not specify
    +          any way of controlling the scanner's input  other  than
    +          by making an initial assignment to yyin).
    +
    +     -    flex scanners do not use stdio for input.   Because  of
    +          this,  when  writing  an  interactive  scanner one must
    +          explicitly call fflush() on the stream associated  with
    +          the terminal after writing out a prompt.  With lex such
    +          writes are automatically flushed since lex scanners use
    +          getchar() for their input.  Also, when writing interac-
    +          tive scanners with flex, the -I flag must be used.
    +
    +     -    flex scanners are not as reentrant as lex scanners.  In
    +          particular,  if  you have an interactive scanner and an
    +          interrupt handler which long-jumps out of the  scanner,
    +          and  the  scanner is subsequently called again, you may
    +          get the following message:
    +
    +              fatal flex scanner internal error--end of buffer missed
    +
    +          To reenter the scanner, first use
    +
    +              yyrestart( yyin );
    +
    +
    +     -    output() is not supported.  Output from the ECHO  macro
    +          is done to the file-pointer yyout (default stdout).
    +
    +          The POSIX  draft  mentions  that  an  output()  routine
    +          exists  but  currently  gives  no details as to what it
    +          does.
    +
    +     -    lex does not support exclusive start  conditions  (%x),
    +          though they are in the current POSIX draft.
    +
    +     -    When definitions are expanded, flex  encloses  them  in
    +          parentheses.  With lex, the following:
    +
    +              NAME    [A-Z][A-Z0-9]*
    +              %%
    +              foo{NAME}?      printf( "Found it\n" );
    +              %%
    +
    +          will not match the string "foo" because when the  macro
    +          is  expanded  the rule is equivalent to "foo[A-Z][A-Z0-
    +          9]*?"  and the precedence is such that the '?' is asso-
    +          ciated  with  "[A-Z0-9]*".  With flex, the rule will be
    +          expanded to "foo([A-Z][A-Z0-9]*)?" and  so  the  string
    +          "foo" will match.  Note that because of this, the ^, $,
    +          <s>, /, and <<EOF>> operators cannot be used in a  flex
    +          definition.
    +
    +          The POSIX draft interpretation is the same as flex's.
    +
    +     -    To specify a character class which matches anything but
    +          a  left  bracket  (']'),  in lex one can use "[^]]" but
    +          with flex one must use "[^\]]".  The latter works  with
    +          lex, too.
    +
    +     -    The lex %r (generate a Ratfor scanner)  option  is  not
    +          supported.  It is not part of the POSIX draft.
    +
    +     -    If you are providing your  own  yywrap()  routine,  you
    +          must  include a "#undef yywrap" in the definitions sec-
    +          tion (section 1).  Note that the "#undef" will have  to
    +          be enclosed in %{}'s.
    +
    +          The POSIX draft specifies that yywrap() is  a  function
    +          and  this is very unlikely to change; so flex users are
    +          warned that yywrap() is likely to be changed to a func-
    +          tion in the near future.
    +
    +     -    After a call to unput(), yytext and  yyleng  are  unde-
    +          fined until the next token is matched.  This is not the
    +          case with lex or the present POSIX draft.
    +
    +     -    The precedence of the {} (numeric  range)  operator  is
    +          different.   lex  interprets  "abc{1,3}" as "match one,
    +          two, or  three  occurrences  of  'abc'",  whereas  flex
    +          interprets  it  as "match 'ab' followed by one, two, or
    +          three occurrences of 'c'".  The latter is in  agreement
    +          with the current POSIX draft.
    +
    +     -    The precedence of the ^  operator  is  different.   lex
    +          interprets  "^foo|bar"  as  "match  either 'foo' at the
    +          beginning of a line, or 'bar' anywhere",  whereas  flex
    +          interprets  it  as "match either 'foo' or 'bar' if they
    +          come at the beginning of a line".   The  latter  is  in
    +          agreement with the current POSIX draft.
    +
    +     -    To refer to yytext outside of the scanner source  file,
    +          the  correct  definition  with  flex  is  "extern  char
    +          *yytext" rather than "extern char yytext[]".   This  is
    +          contrary  to  the  current  POSIX  draft but a point on
    +          which flex will not be changing, as the array represen-
    +          tation  entails  a  serious performance penalty.  It is
    +          hoped that the POSIX draft will be emended  to  support
    +          the  flex  variety  of declaration (as this is a fairly
    +          painless change to require of lex users).
    +
    +     -    yyin is initialized by lex to be stdin;  flex,  on  the
    +          other  hand,  initializes yyin to NULL and then assigns
    +          it to stdin the first time the scanner is called,  pro-
    +          viding yyin has not already been assigned to a non-NULL
    +          value.  The difference is subtle, but the net effect is
    +          that  with  flex  scanners,  yyin does not have a valid
    +          value until the scanner has been called.
    +
    +     -    The special table-size declarations  such  as  %a  sup-
    +          ported  by  lex are not required by flex scanners; flex
    +          ignores them.
    +
    +     -    The name FLEX_SCANNER is #define'd so scanners  may  be
    +          written for use with either flex or lex.
    +
    +     The following flex features are not included in lex  or  the
    +     POSIX draft standard:
    +
    +         yyterminate()
    +         <<EOF>>
    +         YY_DECL
    +         #line directives
    +         %{}'s around actions
    +         yyrestart()
    +         comments beginning with '#' (deprecated)
    +         multiple actions on a line
    +
    +     This last feature refers to the fact that with flex you  can
    +     put  multiple actions on the same line, separated with semi-
    +     colons, while with lex, the following
    +
    +         foo    handle_foo(); ++num_foos_seen;
    +
    +     is (rather surprisingly) truncated to
    +
    +         foo    handle_foo();
    +
    +     flex does not truncate the action.   Actions  that  are  not
    +     enclosed  in  braces are simply terminated at the end of the
    +     line.
    +
    +
    +
    +

    DIAGNOSTICS

    +     reject_used_but_not_detected          undefined           or
    +     yymore_used_but_not_detected  undefined  -  These errors can
    +     occur at compile time.  They indicate that the scanner  uses
    +     REJECT  or yymore() but that flex failed to notice the fact,
    +     meaning that flex scanned the first two sections looking for
    +     occurrences  of  these  actions  and failed to find any, but
    +     somehow you snuck some in (via a #include  file,  for  exam-
    +     ple).  Make an explicit reference to the action in your flex
    +     input  file.   (Note  that  previously  flex   supported   a
    +     %used/%unused  mechanism for dealing with this problem; this
    +     feature is still supported but now deprecated, and  will  go
    +     away  soon unless the author hears from people who can argue
    +     compellingly that they need it.)
    +
    +     flex scanner jammed - a scanner compiled with -s has encoun-
    +     tered  an  input  string  which wasn't matched by any of its
    +     rules.
    +
    +     flex input buffer overflowed -  a  scanner  rule  matched  a
    +     string  long enough to overflow the scanner's internal input
    +     buffer (16K bytes by default - controlled by YY_BUF_SIZE  in
    +     "flex.skel".   Note  that  to  redefine this macro, you must
    +     first #undefine it).
    +
    +     scanner  requires  -8  flag  -  Your  scanner  specification
    +     includes  recognizing  8-bit  characters  and  you  did  not
    +     specify the -8 flag (and your site has  not  installed  flex
    +     with -8 as the default).
    +
    +     fatal flex scanner internal error--end of  buffer  missed  -
    +     This  can  occur  in  an  scanner which is reentered after a
    +     long-jump has jumped out (or over) the scanner's  activation
    +     frame.  Before reentering the scanner, use:
    +
    +         yyrestart( yyin );
    +
    +
    +     too many %t classes! - You managed to put every single char-
    +     acter  into  its  own %t class.  flex requires that at least
    +     one of the classes share characters.
    +
    +
    +
    +

    DEFICIENCIES / BUGS

    +     See flex(1).
    +
    +
    +
    +

    SEE ALSO

    +     flex(1), lex(1), yacc(1), sed(1), awk(1).
    +
    +     M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator
    +
    +
    +
    +

    AUTHOR

    +     Vern Paxson, with the help of many ideas and  much  inspira-
    +     tion  from Van Jacobson.  Original version by Jef Poskanzer.
    +     The fast table representation is a partial implementation of
    +     a  design done by Van Jacobson.  The implementation was done
    +     by Kevin Gong and Vern Paxson.
    +
    +     Thanks to the many flex beta-testers, feedbackers, and  con-
    +     tributors,  especially  Casey  Leedom, benson@odi.com, Keith
    +     Bostic, Frederic Brehm, Nick  Christopher,  Jason  Coughlin,
    +     Scott  David Daniels, Leo Eskin, Chris Faylor, Eric Goldman,
    +     Eric Hughes, Jeffrey R. Jones, Kevin B. Kenny,  Ronald  Lam-
    +     precht,  Greg  Lee, Craig Leres, Mohamed el Lozy, Jim Meyer-
    +     ing, Marc Nozell, Esmond Pitt, Jef Poskanzer,  Jim  Roskind,
    +     Dave  Tallman,  Frank Whaley, Ken Yap, and those whose names
    +     have slipped my marginal  mail-archiving  skills  but  whose
    +     contributions are appreciated all the same.
    +
    +     Thanks to Keith Bostic, John Gilmore, Craig Leres, Bob  Mul-
    +     cahy,  Rich Salz, and Richard Stallman for help with various
    +     distribution headaches.
    +
    +     Thanks to Esmond Pitt and Earle Horton for  8-bit  character
    +     support; to Benson Margulies and Fred Burke for C++ support;
    +     to Ove Ewerlid for the basics of support for NUL's;  and  to
    +     Eric Hughes for the basics of support for multiple buffers.
    +
    +     Work is being done on extending flex to generate scanners in
    +     which  the  state  machine is directly represented in C code
    +     rather than tables.  These scanners  may  well  be  substan-
    +     tially  faster  than those generated using -f or -F.  If you
    +     are working in this area and  are  interested  in  comparing
    +     notes and seeing whether redundant work can be avoided, con-
    +     tact Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE).
    +
    +     This work was primarily done when I was  at  the  Real  Time
    +     Systems  Group at the Lawrence Berkeley Laboratory in Berke-
    +     ley, CA.  Many  thanks  to  all  there  for  the  support  I
    +     received.
    +
    +     Send comments to:
    +
    +          Vern Paxson
    +          Computer Science Department
    +          4126 Upson Hall
    +          Cornell University
    +          Ithaca, NY 14853-7501
    +
    +          vern@cs.cornell.edu
    +          decvax!cornell!vern
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +Man(1) output converted with +man2html +
    + + diff --git a/src/toolsComm/flex/gen.c b/src/toolsComm/flex/gen.c new file mode 100644 index 000000000..3b22752f5 --- /dev/null +++ b/src/toolsComm/flex/gen.c @@ -0,0 +1,1324 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* gen - actual generation (writing) of flex scanners */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +void gen_next_state (int); +void genecs (void); +void indent_put2s (char [], char []); +void indent_puts (char []); + + +static int indent_level = 0; /* each level is 4 spaces */ + +#define indent_up() (++indent_level) +#define indent_down() (--indent_level) +#define set_indent(indent_val) indent_level = indent_val + +/* *everything* is done in terms of arrays starting at 1, so provide + * a null entry for the zero element of all C arrays + */ +static char C_short_decl[] = "static const short int %s[%d] =\n { 0,\n"; +static char C_long_decl[] = "static const long int %s[%d] =\n { 0,\n"; +static char C_state_decl[] = + "static const yy_state_type %s[%d] =\n { 0,\n"; + + +/* indent to the current level */ + +void do_indent(void) +{ + int i = indent_level * 4; + + while ( i >= 8 ) + { + putchar( '\t' ); + i -= 8; + } + + while ( i > 0 ) + { + putchar( ' ' ); + --i; + } + } + + +/* generate the code to keep backtracking information */ + +void gen_backtracking(void) +{ + if ( reject || num_backtracking == 0 ) + return; + + if ( fullspd ) + indent_puts( "if ( yy_current_state[-1].yy_nxt )" ); + else + indent_puts( "if ( yy_accept[yy_current_state] )" ); + + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_last_accepting_state = yy_current_state;" ); + indent_puts( "yy_last_accepting_cpos = yy_cp;" ); + indent_puts( "}" ); + indent_down(); + } + + +/* generate the code to perform the backtrack */ + +void gen_bt_action(void) +{ + if ( reject || num_backtracking == 0 ) + return; + + set_indent( 3 ); + + indent_puts( "case 0: /* must backtrack */" ); + indent_puts( "/* undo the effects of YY_DO_BEFORE_ACTION */" ); + indent_puts( "*yy_cp = yy_hold_char;" ); + + if ( fullspd || fulltbl ) + indent_puts( "yy_cp = yy_last_accepting_cpos + 1;" ); + else + /* backtracking info for compressed tables is taken \after/ + * yy_cp has been incremented for the next state + */ + indent_puts( "yy_cp = yy_last_accepting_cpos;" ); + + indent_puts( "yy_current_state = yy_last_accepting_state;" ); + indent_puts( "goto yy_find_action;" ); + putchar( '\n' ); + + set_indent( 0 ); + } + + +/* genctbl - generates full speed compressed transition table + * + * synopsis + * genctbl(); + */ + +void genctbl(void) +{ + int i; + int end_of_buffer_action = num_rules + 1; + + /* table of verify for transition and offset to next state */ + printf( "static const struct yy_trans_info yy_transition[%d] =\n", + tblend + numecs + 1 ); + printf( " {\n" ); + + /* We want the transition to be represented as the offset to the + * next state, not the actual state number, which is what it currently is. + * The offset is base[nxt[i]] - base[chk[i]]. That's just the + * difference between the starting points of the two involved states + * (to - from). + * + * first, though, we need to find some way to put in our end-of-buffer + * flags and states. We do this by making a state with absolutely no + * transitions. We put it at the end of the table. + */ + /* at this point, we're guaranteed that there's enough room in nxt[] + * and chk[] to hold tblend + numecs entries. We need just two slots. + * One for the action and one for the end-of-buffer transition. We + * now *assume* that we're guaranteed the only character we'll try to + * index this nxt/chk pair with is EOB, i.e., 0, so we don't have to + * make sure there's room for jam entries for other characters. + */ + + base[lastdfa + 1] = tblend + 2; + nxt[tblend + 1] = end_of_buffer_action; + chk[tblend + 1] = numecs + 1; + chk[tblend + 2] = 1; /* anything but EOB */ + nxt[tblend + 2] = 0; /* so that "make test" won't show arb. differences */ + + /* make sure every state has a end-of-buffer transition and an action # */ + for ( i = 0; i <= lastdfa; ++i ) + { + int anum = dfaacc[i].dfaacc_state; + + chk[base[i]] = EOB_POSITION; + chk[base[i] - 1] = ACTION_POSITION; + nxt[base[i] - 1] = anum; /* action number */ + } + + for ( i = 0; i <= tblend; ++i ) + { + if ( chk[i] == EOB_POSITION ) + transition_struct_out( 0, base[lastdfa + 1] - i ); + + else if ( chk[i] == ACTION_POSITION ) + transition_struct_out( 0, nxt[i] ); + + else if ( chk[i] > numecs || chk[i] == 0 ) + transition_struct_out( 0, 0 ); /* unused slot */ + + else /* verify, transition */ + transition_struct_out( chk[i], base[nxt[i]] - (i - chk[i]) ); + } + + + /* here's the final, end-of-buffer state */ + transition_struct_out( chk[tblend + 1], nxt[tblend + 1] ); + transition_struct_out( chk[tblend + 2], nxt[tblend + 2] ); + + printf( " };\n" ); + printf( "\n" ); + + /* table of pointers to start states */ + printf( "static const struct yy_trans_info *yy_start_state_list[%d] =\n", + lastsc * 2 + 1 ); + printf( " {\n" ); + + for ( i = 0; i <= lastsc * 2; ++i ) + printf( " &yy_transition[%d],\n", base[i] ); + + dataend(); + + if ( useecs ) + genecs(); + } + + +/* generate equivalence-class tables */ + +void genecs(void) +{ + int i, j; + static char C_char_decl[] = "static const %s %s[%d] =\n { 0,\n"; + int numrows; + Char clower(); + + if ( numecs < csize ) + printf( C_char_decl, "YY_CHAR", "yy_ec", csize ); + else + printf( C_char_decl, "short", "yy_ec", csize ); + + for ( i = 1; i < csize; ++i ) + { + if ( caseins && (i >= 'A') && (i <= 'Z') ) + ecgroup[i] = ecgroup[clower( i )]; + + ecgroup[i] = abs( ecgroup[i] ); + mkdata( ecgroup[i] ); + } + + dataend(); + + if ( trace ) + { + char *readable_form(); + + fputs( "\n\nEquivalence Classes:\n\n", stderr ); + + numrows = csize / 8; + + for ( j = 0; j < numrows; ++j ) + { + for ( i = j; i < csize; i = i + numrows ) + { + fprintf( stderr, "%4s = %-2d", readable_form( i ), ecgroup[i] ); + + putc( ' ', stderr ); + } + + putc( '\n', stderr ); + } + } + } + + +/* generate the code to find the action number */ + +void gen_find_action(void) +{ + if ( fullspd ) + indent_puts( "yy_act = yy_current_state[-1].yy_nxt;" ); + + else if ( fulltbl ) + indent_puts( "yy_act = yy_accept[yy_current_state];" ); + + else if ( reject ) + { + indent_puts( "yy_current_state = *--yy_state_ptr;" ); + indent_puts( "yy_lp = yy_accept[yy_current_state];" ); + + puts( "find_rule: /* we branch to this label when backtracking */" ); + + indent_puts( "for ( ; ; ) /* until we find what rule we matched */" ); + + indent_up(); + + indent_puts( "{" ); + + indent_puts( "if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_act = yy_acclist[yy_lp];" ); + + if ( variable_trailing_context_rules ) + { + indent_puts( "if ( yy_act & YY_TRAILING_HEAD_MASK ||" ); + indent_puts( " yy_looking_for_trail_begin )" ); + indent_up(); + indent_puts( "{" ); + + indent_puts( "if ( yy_act == yy_looking_for_trail_begin )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_looking_for_trail_begin = 0;" ); + indent_puts( "yy_act &= ~YY_TRAILING_HEAD_MASK;" ); + indent_puts( "break;" ); + indent_puts( "}" ); + indent_down(); + + indent_puts( "}" ); + indent_down(); + + indent_puts( "else if ( yy_act & YY_TRAILING_MASK )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( + "yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK;" ); + indent_puts( + "yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK;" ); + + if ( real_reject ) + { + /* remember matched text in case we back up due to REJECT */ + indent_puts( "yy_full_match = yy_cp;" ); + indent_puts( "yy_full_state = yy_state_ptr;" ); + indent_puts( "yy_full_lp = yy_lp;" ); + } + + indent_puts( "}" ); + indent_down(); + + indent_puts( "else" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_full_match = yy_cp;" ); + indent_puts( "yy_full_state = yy_state_ptr;" ); + indent_puts( "yy_full_lp = yy_lp;" ); + indent_puts( "break;" ); + indent_puts( "}" ); + indent_down(); + + indent_puts( "++yy_lp;" ); + indent_puts( "goto find_rule;" ); + } + + else + { + /* remember matched text in case we back up due to trailing context + * plus REJECT + */ + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_full_match = yy_cp;" ); + indent_puts( "break;" ); + indent_puts( "}" ); + indent_down(); + } + + indent_puts( "}" ); + indent_down(); + + indent_puts( "--yy_cp;" ); + + /* we could consolidate the following two lines with those at + * the beginning, but at the cost of complaints that we're + * branching inside a loop + */ + indent_puts( "yy_current_state = *--yy_state_ptr;" ); + indent_puts( "yy_lp = yy_accept[yy_current_state];" ); + + indent_puts( "}" ); + + indent_down(); + } + + else + /* compressed */ + indent_puts( "yy_act = yy_accept[yy_current_state];" ); + } + + +/* genftbl - generates full transition table + * + * synopsis + * genftbl(); + */ + +void genftbl(void) +{ + int i; + int end_of_buffer_action = num_rules + 1; + + printf( C_short_decl, "yy_accept", lastdfa + 1 ); + + + dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action; + + for ( i = 1; i <= lastdfa; ++i ) + { + int anum = dfaacc[i].dfaacc_state; + + mkdata( anum ); + + if ( trace && anum ) + fprintf( stderr, "state # %d accepts: [%d]\n", i, anum ); + } + + dataend(); + + if ( useecs ) + genecs(); + + /* don't have to dump the actual full table entries - they were created + * on-the-fly + */ + } + + +/* generate the code to find the next compressed-table state */ + +void gen_next_compressed_state(char *char_map) +{ + indent_put2s( "YY_CHAR yy_c = %s;", char_map ); + + /* save the backtracking info \before/ computing the next state + * because we always compute one more state than needed - we + * always proceed until we reach a jam state + */ + gen_backtracking(); + + indent_puts( + "while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_current_state = yy_def[yy_current_state];" ); + + if ( usemecs ) + { + /* we've arrange it so that templates are never chained + * to one another. This means we can afford make a + * very simple test to see if we need to convert to + * yy_c's meta-equivalence class without worrying + * about erroneously looking up the meta-equivalence + * class twice + */ + do_indent(); + /* lastdfa + 2 is the beginning of the templates */ + printf( "if ( yy_current_state >= %d )\n", lastdfa + 2 ); + + indent_up(); + indent_puts( "yy_c = yy_meta[(int)yy_c];" ); + indent_down(); + } + + indent_puts( "}" ); + indent_down(); + + indent_puts( + "yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];" ); + } + + +/* generate the code to find the next match */ + +void gen_next_match(void) +{ + /* NOTE - changes in here should be reflected in gen_next_state() and + * gen_NUL_trans() + */ + char *char_map = useecs ? "yy_ec[(int)*yy_cp]" : "*yy_cp"; + char *char_map_2 = useecs ? "yy_ec[*++yy_cp]" : "*++yy_cp"; + + if ( fulltbl ) + { + indent_put2s( + "while ( (yy_current_state = yy_nxt[yy_current_state][%s]) > 0 )", + char_map ); + + indent_up(); + + if ( num_backtracking > 0 ) + { + indent_puts( "{" ); + gen_backtracking(); + putchar( '\n' ); + } + + indent_puts( "++yy_cp;" ); + + if ( num_backtracking > 0 ) + indent_puts( "}" ); + + indent_down(); + + putchar( '\n' ); + indent_puts( "yy_current_state = -yy_current_state;" ); + } + + else if ( fullspd ) + { + indent_puts( "{" ); + indent_puts( "const struct yy_trans_info *yy_trans_info;\n" ); + indent_puts( "YY_CHAR yy_c;\n" ); + indent_put2s( "for ( yy_c = %s;", char_map ); + indent_puts( + " (yy_trans_info = &yy_current_state[yy_c])->yy_verify == yy_c;" ); + indent_put2s( " yy_c = %s )", char_map_2 ); + + indent_up(); + + if ( num_backtracking > 0 ) + indent_puts( "{" ); + + indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" ); + + if ( num_backtracking > 0 ) + { + putchar( '\n' ); + gen_backtracking(); + indent_puts( "}" ); + } + + indent_down(); + indent_puts( "}" ); + } + + else + { /* compressed */ + indent_puts( "do" ); + + indent_up(); + indent_puts( "{" ); + + gen_next_state( false ); + + indent_puts( "++yy_cp;" ); + + indent_puts( "}" ); + indent_down(); + + do_indent(); + + if ( interactive ) + printf( "while ( yy_base[yy_current_state] != %d );\n", jambase ); + else + printf( "while ( yy_current_state != %d );\n", jamstate ); + + if ( ! reject && ! interactive ) + { + /* do the guaranteed-needed backtrack to figure out the match */ + indent_puts( "yy_cp = yy_last_accepting_cpos;" ); + indent_puts( "yy_current_state = yy_last_accepting_state;" ); + } + } + } + + +/* generate the code to find the next state */ + +void gen_next_state(int worry_about_NULs) +{ /* NOTE - changes in here should be reflected in get_next_match() */ + char char_map[256]; + + if ( worry_about_NULs && ! nultrans ) + { + if ( useecs ) + (void) sprintf( char_map, "(*yy_cp ? yy_ec[(int)*yy_cp] : %d)", NUL_ec ); + else + (void) sprintf( char_map, "(*yy_cp ? *yy_cp : %d)", NUL_ec ); + } + + else + (void) strcpy( char_map, useecs ? "yy_ec[(int)*yy_cp]" : "*yy_cp" ); + + if ( worry_about_NULs && nultrans ) + { + if ( ! fulltbl && ! fullspd ) + /* compressed tables backtrack *before* they match */ + gen_backtracking(); + + indent_puts( "if ( *yy_cp )" ); + indent_up(); + indent_puts( "{" ); + } + + if ( fulltbl ) + indent_put2s( "yy_current_state = yy_nxt[yy_current_state][%s];", + char_map ); + + else if ( fullspd ) + indent_put2s( "yy_current_state += yy_current_state[%s].yy_nxt;", + char_map ); + + else + gen_next_compressed_state( char_map ); + + if ( worry_about_NULs && nultrans ) + { + indent_puts( "}" ); + indent_down(); + indent_puts( "else" ); + indent_up(); + indent_puts( "yy_current_state = yy_NUL_trans[yy_current_state];" ); + indent_down(); + } + + if ( fullspd || fulltbl ) + gen_backtracking(); + + if ( reject ) + indent_puts( "*yy_state_ptr++ = yy_current_state;" ); + } + + +/* generate the code to make a NUL transition */ + +void gen_NUL_trans(void) +{ /* NOTE - changes in here should be reflected in get_next_match() */ + int need_backtracking = (num_backtracking > 0 && ! reject); + + if ( need_backtracking ) + /* we'll need yy_cp lying around for the gen_backtracking() */ + indent_puts( "YY_CHAR *yy_cp = yy_c_buf_p;" ); + + putchar( '\n' ); + + if ( nultrans ) + { + indent_puts( "yy_current_state = yy_NUL_trans[yy_current_state];" ); + indent_puts( "yy_is_jam = (yy_current_state == 0);" ); + } + + else if ( fulltbl ) + { + do_indent(); + printf( "yy_current_state = yy_nxt[yy_current_state][%d];\n", + NUL_ec ); + indent_puts( "yy_is_jam = (yy_current_state <= 0);" ); + } + + else if ( fullspd ) + { + do_indent(); + printf( "int yy_c = %d;\n", NUL_ec ); + + indent_puts( + "const struct yy_trans_info *yy_trans_info;\n" ); + indent_puts( "yy_trans_info = &yy_current_state[yy_c];" ); + indent_puts( "yy_current_state += yy_trans_info->yy_nxt;" ); + + indent_puts( "yy_is_jam = (yy_trans_info->yy_verify != yy_c);" ); + } + + else + { + char NUL_ec_str[20]; + + (void) sprintf( NUL_ec_str, "%d", NUL_ec ); + gen_next_compressed_state( NUL_ec_str ); + + if ( reject ) + indent_puts( "*yy_state_ptr++ = yy_current_state;" ); + + do_indent(); + + if ( interactive ) + printf( "yy_is_jam = (yy_base[yy_current_state] == %d);\n", + jambase ); + else + printf( "yy_is_jam = (yy_current_state == %d);\n", jamstate ); + } + + /* if we've entered an accepting state, backtrack; note that + * compressed tables have *already* done such backtracking, so + * we needn't bother with it again + */ + if ( need_backtracking && (fullspd || fulltbl) ) + { + putchar( '\n' ); + indent_puts( "if ( ! yy_is_jam )" ); + indent_up(); + indent_puts( "{" ); + gen_backtracking(); + indent_puts( "}" ); + indent_down(); + } + } + + +/* generate the code to find the start state */ + +void gen_start_state(void) +{ + if ( fullspd ) + indent_put2s( "yy_current_state = yy_start_state_list[yy_start%s];", + bol_needed ? " + (yy_bp[-1] == '\\n' ? 1 : 0)" : "" ); + + else + { + indent_puts( "yy_current_state = yy_start;" ); + + if ( bol_needed ) + { + indent_puts( "if ( yy_bp[-1] == '\\n' )" ); + indent_up(); + indent_puts( "++yy_current_state;" ); + indent_down(); + } + + if ( reject ) + { + /* set up for storing up states */ + indent_puts( "yy_state_ptr = yy_state_buf;" ); + indent_puts( "*yy_state_ptr++ = yy_current_state;" ); + } + } + } + + +/* gentabs - generate data statements for the transition tables + * + * synopsis + * gentabs(); + */ + +void gentabs(void) +{ + int i, j, k, *accset, nacc, *acc_array, total_states; + int end_of_buffer_action = num_rules + 1; + + /* *everything* is done in terms of arrays starting at 1, so provide + * a null entry for the zero element of all C arrays + */ + static char C_char_decl[] = + "static const YY_CHAR %s[%d] =\n { 0,\n"; + + acc_array = allocate_integer_array( current_max_dfas ); + nummt = 0; + + /* the compressed table format jams by entering the "jam state", + * losing information about the previous state in the process. + * In order to recover the previous state, we effectively need + * to keep backtracking information. + */ + ++num_backtracking; + + if ( reject ) + { + /* write out accepting list and pointer list + * + * first we generate the "yy_acclist" array. In the process, we compute + * the indices that will go into the "yy_accept" array, and save the + * indices in the dfaacc array + */ + int EOB_accepting_list[2]; + + /* set up accepting structures for the End Of Buffer state */ + EOB_accepting_list[0] = 0; + EOB_accepting_list[1] = end_of_buffer_action; + accsiz[end_of_buffer_state] = 1; + dfaacc[end_of_buffer_state].dfaacc_set = EOB_accepting_list; + + printf( C_short_decl, "yy_acclist", max( numas, 1 ) + 1 ); + + j = 1; /* index into "yy_acclist" array */ + + for ( i = 1; i <= lastdfa; ++i ) + { + acc_array[i] = j; + + if ( accsiz[i] != 0 ) + { + accset = dfaacc[i].dfaacc_set; + nacc = accsiz[i]; + + if ( trace ) + fprintf( stderr, "state # %d accepts: ", i ); + + for ( k = 1; k <= nacc; ++k ) + { + int accnum = accset[k]; + + ++j; + + if ( variable_trailing_context_rules && + ! (accnum & YY_TRAILING_HEAD_MASK) && + accnum > 0 && accnum <= num_rules && + rule_type[accnum] == RULE_VARIABLE ) + { + /* special hack to flag accepting number as part + * of trailing context rule + */ + accnum |= YY_TRAILING_MASK; + } + + mkdata( accnum ); + + if ( trace ) + { + fprintf( stderr, "[%d]", accset[k] ); + + if ( k < nacc ) + fputs( ", ", stderr ); + else + putc( '\n', stderr ); + } + } + } + } + + /* add accepting number for the "jam" state */ + acc_array[i] = j; + + dataend(); + } + + else + { + dfaacc[end_of_buffer_state].dfaacc_state = end_of_buffer_action; + + for ( i = 1; i <= lastdfa; ++i ) + acc_array[i] = dfaacc[i].dfaacc_state; + + /* add accepting number for jam state */ + acc_array[i] = 0; + } + + /* spit out "yy_accept" array. If we're doing "reject", it'll be pointers + * into the "yy_acclist" array. Otherwise it's actual accepting numbers. + * In either case, we just dump the numbers. + */ + + /* "lastdfa + 2" is the size of "yy_accept"; includes room for C arrays + * beginning at 0 and for "jam" state + */ + k = lastdfa + 2; + + if ( reject ) + /* we put a "cap" on the table associating lists of accepting + * numbers with state numbers. This is needed because we tell + * where the end of an accepting list is by looking at where + * the list for the next state starts. + */ + ++k; + + printf( C_short_decl, "yy_accept", k ); + + for ( i = 1; i <= lastdfa; ++i ) + { + mkdata( acc_array[i] ); + + if ( ! reject && trace && acc_array[i] ) + fprintf( stderr, "state # %d accepts: [%d]\n", i, acc_array[i] ); + } + + /* add entry for "jam" state */ + mkdata( acc_array[i] ); + + if ( reject ) + /* add "cap" for the list */ + mkdata( acc_array[i] ); + + dataend(); + + if ( useecs ) + genecs(); + + if ( usemecs ) + { + /* write out meta-equivalence classes (used to index templates with) */ + + if ( trace ) + fputs( "\n\nMeta-Equivalence Classes:\n", stderr ); + + printf( C_char_decl, "yy_meta", numecs + 1 ); + + for ( i = 1; i <= numecs; ++i ) + { + if ( trace ) + fprintf( stderr, "%d = %d\n", i, abs( tecbck[i] ) ); + + mkdata( abs( tecbck[i] ) ); + } + + dataend(); + } + + total_states = lastdfa + numtemps; + + printf( tblend > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_base", total_states + 1 ); + + for ( i = 1; i <= lastdfa; ++i ) + { + int d = def[i]; + + if ( base[i] == JAMSTATE ) + base[i] = jambase; + + if ( d == JAMSTATE ) + def[i] = jamstate; + + else if ( d < 0 ) + { + /* template reference */ + ++tmpuses; + def[i] = lastdfa - d + 1; + } + + mkdata( base[i] ); + } + + /* generate jam state's base index */ + mkdata( base[i] ); + + for ( ++i /* skip jam state */; i <= total_states; ++i ) + { + mkdata( base[i] ); + def[i] = jamstate; + } + + dataend(); + + printf( tblend > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_def", total_states + 1 ); + + for ( i = 1; i <= total_states; ++i ) + mkdata( def[i] ); + + dataend(); + + printf( lastdfa > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_nxt", tblend + 1 ); + + for ( i = 1; i <= tblend; ++i ) + { + if ( nxt[i] == 0 || chk[i] == 0 ) + nxt[i] = jamstate; /* new state is the JAM state */ + + mkdata( nxt[i] ); + } + + dataend(); + + printf( lastdfa > MAX_SHORT ? C_long_decl : C_short_decl, + "yy_chk", tblend + 1 ); + + for ( i = 1; i <= tblend; ++i ) + { + if ( chk[i] == 0 ) + ++nummt; + + mkdata( chk[i] ); + } + + dataend(); + } + + +/* write out a formatted string (with a secondary string argument) at the + * current indentation level, adding a final newline + */ + +void indent_put2s(char *fmt, char *arg) +{ + do_indent(); + printf( fmt, arg ); + putchar( '\n' ); + } + + +/* write out a string at the current indentation level, adding a final + * newline + */ + +void indent_puts(char *str) +{ + do_indent(); + puts( str ); + } + + +/* make_tables - generate transition tables + * + * synopsis + * make_tables(); + * + * Generates transition tables and finishes generating output file + */ + +void make_tables(void) +{ + int i; + int did_eof_rule = false; + + skelout(); + + /* first, take care of YY_DO_BEFORE_ACTION depending on yymore being used */ + set_indent( 2 ); + + if ( yymore_used ) + { + indent_puts( "yytext -= yy_more_len; \\" ); + indent_puts( "yyleng = yy_cp - yytext; \\" ); + } + + else + indent_puts( "yyleng = yy_cp - yy_bp; \\" ); + + set_indent( 0 ); + + skelout(); + + + printf( "#define YY_END_OF_BUFFER %d\n", num_rules + 1 ); + + if ( fullspd ) + { /* need to define the transet type as a size large + * enough to hold the biggest offset + */ + int total_table_size = tblend + numecs + 1; + char *trans_offset_type = + total_table_size > MAX_SHORT ? "long" : "short"; + + set_indent( 0 ); + indent_puts( "struct yy_trans_info" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "short yy_verify;" ); + + /* in cases where its sister yy_verify *is* a "yes, there is a + * transition", yy_nxt is the offset (in records) to the next state. + * In most cases where there is no transition, the value of yy_nxt + * is irrelevant. If yy_nxt is the -1th record of a state, though, + * then yy_nxt is the action number for that state + */ + + indent_put2s( "%s yy_nxt;", trans_offset_type ); + indent_puts( "};" ); + indent_down(); + + indent_puts( "typedef const struct yy_trans_info *yy_state_type;" ); + } + + else + indent_puts( "typedef int yy_state_type;" ); + + if ( fullspd ) + genctbl(); + + else if ( fulltbl ) + genftbl(); + + else + gentabs(); + + if ( num_backtracking > 0 ) + { + indent_puts( "static yy_state_type yy_last_accepting_state;" ); + indent_puts( "static YY_CHAR *yy_last_accepting_cpos;\n" ); + } + + if ( nultrans ) + { + printf( C_state_decl, "yy_NUL_trans", lastdfa + 1 ); + + for ( i = 1; i <= lastdfa; ++i ) + { + if ( fullspd ) + { + if ( nultrans ) + printf( " &yy_transition[%d],\n", base[i] ); + else + printf( " 0,\n" ); + } + + else + mkdata( nultrans[i] ); + } + + dataend(); + } + + if ( ddebug ) + { /* spit out table mapping rules to line numbers */ + indent_puts( "extern int yy_flex_debug;" ); + indent_puts( "int yy_flex_debug = 1;\n" ); + + printf( C_short_decl, "yy_rule_linenum", num_rules ); + for ( i = 1; i < num_rules; ++i ) + mkdata( rule_linenum[i] ); + dataend(); + } + + if ( reject ) + { + /* declare state buffer variables */ + puts( + "static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr;" ); + puts( "static YY_CHAR *yy_full_match;" ); + puts( "static int yy_lp;" ); + + if ( variable_trailing_context_rules ) + { + puts( "static int yy_looking_for_trail_begin = 0;" ); + puts( "static int yy_full_lp;" ); + puts( "static int *yy_full_state;" ); + printf( "#define YY_TRAILING_MASK 0x%x\n", YY_TRAILING_MASK ); + printf( "#define YY_TRAILING_HEAD_MASK 0x%x\n", + YY_TRAILING_HEAD_MASK ); + } + + puts( "#define REJECT \\" ); + puts( "{ \\" ); + puts( + "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \\" ); + puts( + "yy_cp = yy_full_match; /* restore poss. backed-over text */ \\" ); + + if ( variable_trailing_context_rules ) + { + puts( "yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \\" ); + puts( + "yy_state_ptr = yy_full_state; /* restore orig. state */ \\" ); + puts( + "yy_current_state = *yy_state_ptr; /* restore curr. state */ \\" ); + } + + puts( "++yy_lp; \\" ); + puts( "goto find_rule; \\" ); + puts( "}" ); + } + + else + { + puts( "/* the intent behind this definition is that it'll catch" ); + puts( " * any uses of REJECT which flex missed" ); + puts( " */" ); + puts( "#define REJECT reject_used_but_not_detected" ); + } + + if ( yymore_used ) + { + indent_puts( "static int yy_more_flag = 0;" ); + indent_puts( "static int yy_doing_yy_more = 0;" ); + indent_puts( "static int yy_more_len = 0;" ); + indent_puts( + "#define yymore() { yy_more_flag = 1; }" ); + indent_puts( + "#define YY_MORE_ADJ (yy_doing_yy_more ? yy_more_len : 0)" ); + } + + else + { + indent_puts( "#define yymore() yymore_used_but_not_detected" ); + indent_puts( "#define YY_MORE_ADJ 0" ); + } + + skelout(); + + if ( ferror( temp_action_file ) ) + flexfatal( "error occurred when writing temporary action file" ); + + else if ( fclose( temp_action_file ) ) + flexfatal( "error occurred when closing temporary action file" ); + + temp_action_file = fopen( action_file_name, "r" ); + + if ( temp_action_file == NULL ) + flexfatal( "could not re-open temporary action file" ); + + /* copy prolog from action_file to output file */ + action_out(); + + skelout(); + + set_indent( 2 ); + + if ( yymore_used ) + { + indent_puts( "yy_more_len = 0;" ); + indent_puts( "yy_doing_yy_more = yy_more_flag;" ); + indent_puts( "if ( yy_doing_yy_more )" ); + indent_up(); + indent_puts( "{" ); + indent_puts( "yy_more_len = yyleng;" ); + indent_puts( "yy_more_flag = 0;" ); + indent_puts( "}" ); + indent_down(); + } + + skelout(); + + gen_start_state(); + + /* note, don't use any indentation */ + puts( "yy_match:" ); + gen_next_match(); + + skelout(); + set_indent( 2 ); + gen_find_action(); + + skelout(); + if ( ddebug ) + { + indent_puts( "if ( yy_flex_debug )" ); + indent_up(); + + indent_puts( "{" ); + indent_puts( "if ( yy_act == 0 )" ); + indent_up(); + indent_puts( "fprintf( stderr, \"--scanner backtracking\\n\" );" ); + indent_down(); + + do_indent(); + printf( "else if ( yy_act < %d )\n", num_rules ); + indent_up(); + indent_puts( + "fprintf( stderr, \"--accepting rule at line %d (\\\"%s\\\")\\n\"," ); + indent_puts( " yy_rule_linenum[yy_act], yytext );" ); + indent_down(); + + do_indent(); + printf( "else if ( yy_act == %d )\n", num_rules ); + indent_up(); + indent_puts( + "fprintf( stderr, \"--accepting default rule (\\\"%s\\\")\\n\"," ); + indent_puts( " yytext );" ); + indent_down(); + + do_indent(); + printf( "else if ( yy_act == %d )\n", num_rules + 1 ); + indent_up(); + indent_puts( "fprintf( stderr, \"--(end of buffer or a NUL)\\n\" );" ); + indent_down(); + + do_indent(); + printf( "else\n" ); + indent_up(); + indent_puts( "fprintf( stderr, \"--EOF\\n\" );" ); + indent_down(); + + indent_puts( "}" ); + indent_down(); + } + + /* copy actions from action_file to output file */ + skelout(); + indent_up(); + gen_bt_action(); + action_out(); + + /* generate cases for any missing EOF rules */ + for ( i = 1; i <= lastsc; ++i ) + if ( ! sceof[i] ) + { + do_indent(); + printf( "case YY_STATE_EOF(%s):\n", scname[i] ); + did_eof_rule = true; + } + + if ( did_eof_rule ) + { + indent_up(); + indent_puts( "yyterminate();" ); + indent_down(); + } + + + /* generate code for handling NUL's, if needed */ + + /* first, deal with backtracking and setting up yy_cp if the scanner + * finds that it should JAM on the NUL + */ + skelout(); + set_indent( 7 ); + + if ( fullspd || fulltbl ) + indent_puts( "yy_cp = yy_c_buf_p;" ); + + else + { /* compressed table */ + if ( ! reject && ! interactive ) + { + /* do the guaranteed-needed backtrack to figure out the match */ + indent_puts( "yy_cp = yy_last_accepting_cpos;" ); + indent_puts( "yy_current_state = yy_last_accepting_state;" ); + } + } + + + /* generate code for yy_get_previous_state() */ + set_indent( 1 ); + skelout(); + + if ( bol_needed ) + indent_puts( "YY_CHAR *yy_bp = yytext;\n" ); + + gen_start_state(); + + set_indent( 2 ); + skelout(); + gen_next_state( true ); + + set_indent( 1 ); + skelout(); + gen_NUL_trans(); + + skelout(); + + /* copy remainder of input to output */ + + line_directive_out( stdout ); + (void) flexscan(); /* copy remainder of input to output */ + } diff --git a/src/toolsComm/flex/libmain.c b/src/toolsComm/flex/libmain.c new file mode 100644 index 000000000..a5d303885 --- /dev/null +++ b/src/toolsComm/flex/libmain.c @@ -0,0 +1,18 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* libmain - flex run-time support library "main" function */ + +/* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +extern int yylex(); + +int main(int argc, char *argv[]) +{ + return yylex(); +} diff --git a/src/toolsComm/flex/main.c b/src/toolsComm/flex/main.c new file mode 100644 index 000000000..13fa1c9f8 --- /dev/null +++ b/src/toolsComm/flex/main.c @@ -0,0 +1,754 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* flex - tool to generate fast lexical analyzers */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define ENQUOTE(path) #path + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +/* +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" +*/ + +#include "epicsStdio.h" + +static char flex_version[] = "2.3"; + + +/* declare functions that have forward references */ + +void flexinit (int, char**); +void readin (void); +void set_up_initial_allocations (void); + + +/* these globals are all defined and commented in flexdef.h */ +int printstats, syntaxerror, eofseen, ddebug, trace, spprdflt; +int interactive, caseins, useecs, fulltbl, usemecs; +int fullspd, gen_line_dirs, performance_report, backtrack_report, csize; +int yymore_used, reject, real_reject, continued_action; +int yymore_really_used, reject_really_used; +int datapos, dataline, linenum; +FILE *skelfile = NULL; +char *infilename = NULL; +int onestate[ONE_STACK_SIZE], onesym[ONE_STACK_SIZE]; +int onenext[ONE_STACK_SIZE], onedef[ONE_STACK_SIZE], onesp; +int current_mns, num_rules, current_max_rules, lastnfa; +int *firstst, *lastst, *finalst, *transchar, *trans1, *trans2; +int *accptnum, *assoc_rule, *state_type, *rule_type, *rule_linenum; +int current_state_type; +int variable_trailing_context_rules; +int numtemps, numprots, protprev[MSP], protnext[MSP], prottbl[MSP]; +int protcomst[MSP], firstprot, lastprot, protsave[PROT_SAVE_SIZE]; +int numecs, nextecm[CSIZE + 1], ecgroup[CSIZE + 1], nummecs, tecfwd[CSIZE + 1]; +int tecbck[CSIZE + 1]; +int *xlation = (int *) 0; +int num_xlations; +int lastsc, current_max_scs, *scset, *scbol, *scxclu, *sceof, *actvsc; +char **scname; +int current_max_dfa_size, current_max_xpairs; +int current_max_template_xpairs, current_max_dfas; +int lastdfa, *nxt, *chk, *tnxt; +int *base, *def, *nultrans, NUL_ec, tblend, firstfree, **dss, *dfasiz; +union dfaacc_union *dfaacc; +int *accsiz, *dhash, numas; +int numsnpairs, jambase, jamstate; +int lastccl, current_maxccls, *cclmap, *ccllen, *cclng, cclreuse; +int current_max_ccl_tbl_size; +Char *ccltbl; +char *starttime, *endtime, nmstr[MAXLINE]; +int sectnum, nummt, hshcol, dfaeql, numeps, eps2, num_reallocs; +int tmpuses, totnst, peakpairs, numuniq, numdup, hshsave; +int num_backtracking, bol_needed; +FILE *temp_action_file; +FILE *backtrack_file; +int end_of_buffer_state; +char action_file_name[256>L_tmpnam?256:L_tmpnam]; +char **input_files; +int num_input_files; +char *program_name; + +#ifndef SHORT_FILE_NAMES +static char *outfile = "lex.yy.c"; +#else +static char *outfile = "lexyy.c"; +#endif +static int outfile_created = 0; +static int use_stdout; +static char *skelname = NULL; + + +int main(int argc, char *argv[]) +{ + flexinit( argc, argv ); + + readin(); + + if ( syntaxerror ) + flexend( 1 ); + + if ( yymore_really_used == REALLY_USED ) + yymore_used = true; + else if ( yymore_really_used == REALLY_NOT_USED ) + yymore_used = false; + + if ( reject_really_used == REALLY_USED ) + reject = true; + else if ( reject_really_used == REALLY_NOT_USED ) + reject = false; + + if ( performance_report ) + { + if ( interactive ) + fprintf( stderr, + "-I (interactive) entails a minor performance penalty\n" ); + + if ( yymore_used ) + fprintf( stderr, "yymore() entails a minor performance penalty\n" ); + + if ( reject ) + fprintf( stderr, "REJECT entails a large performance penalty\n" ); + + if ( variable_trailing_context_rules ) + fprintf( stderr, +"Variable trailing context rules entail a large performance penalty\n" ); + } + + if ( reject ) + real_reject = true; + + if ( variable_trailing_context_rules ) + reject = true; + + if ( (fulltbl || fullspd) && reject ) + { + if ( real_reject ) + flexerror( "REJECT cannot be used with -f or -F" ); + else + flexerror( + "variable trailing context rules cannot be used with -f or -F" ); + } + + ntod(); + + /* generate the C state transition tables from the DFA */ + make_tables(); + + /* note, flexend does not return. It exits with its argument as status. */ + + flexend( 0 ); + + /*NOTREACHED*/ +} + + +/* flexend - terminate flex + * + * synopsis + * int status; + * flexend( status ); + * + * status is exit status. + * + * note + * This routine does not return. + */ + +void flexend(int status) +{ + int tblsiz; + char *flex_gettime(); + + if ( skelfile != NULL ) + { + if ( ferror( skelfile ) ) + flexfatal( "error occurred when writing skeleton file" ); + + else if ( fclose( skelfile ) ) + flexfatal( "error occurred when closing skeleton file" ); + } + + if ( temp_action_file ) + { + if ( ferror( temp_action_file ) ) + flexfatal( "error occurred when writing temporary action file" ); + + else if ( fclose( temp_action_file ) ) + flexfatal( "error occurred when closing temporary action file" ); + + else if ( unlink( action_file_name ) ) + flexfatal( "error occurred when deleting temporary action file" ); + } + + if ( status != 0 && outfile_created ) + { + if ( ferror( stdout ) ) + flexfatal( "error occurred when writing output file" ); + + else if ( fclose( stdout ) ) + flexfatal( "error occurred when closing output file" ); + + else if ( unlink( outfile ) ) + flexfatal( "error occurred when deleting output file" ); + } + + if ( backtrack_report && backtrack_file ) + { + if ( num_backtracking == 0 ) + fprintf( backtrack_file, "No backtracking.\n" ); + else if ( fullspd || fulltbl ) + fprintf( backtrack_file, + "%d backtracking (non-accepting) states.\n", + num_backtracking ); + else + fprintf( backtrack_file, "Compressed tables always backtrack.\n" ); + + if ( ferror( backtrack_file ) ) + flexfatal( "error occurred when writing backtracking file" ); + + else if ( fclose( backtrack_file ) ) + flexfatal( "error occurred when closing backtracking file" ); + } + + if ( printstats ) + { + endtime = flex_gettime(); + + fprintf( stderr, "%s version %s usage statistics:\n", program_name, + flex_version ); + fprintf( stderr, " started at %s, finished at %s\n", + starttime, endtime ); + + fprintf( stderr, " scanner options: -" ); + + if ( backtrack_report ) + putc( 'b', stderr ); + if ( ddebug ) + putc( 'd', stderr ); + if ( interactive ) + putc( 'I', stderr ); + if ( caseins ) + putc( 'i', stderr ); + if ( ! gen_line_dirs ) + putc( 'L', stderr ); + if ( performance_report ) + putc( 'p', stderr ); + if ( spprdflt ) + putc( 's', stderr ); + if ( use_stdout ) + putc( 't', stderr ); + if ( trace ) + putc( 'T', stderr ); + if ( printstats ) + putc( 'v', stderr ); /* always true! */ + if ( csize == 256 ) + putc( '8', stderr ); + + fprintf( stderr, " -C" ); + + if ( fulltbl ) + putc( 'f', stderr ); + if ( fullspd ) + putc( 'F', stderr ); + if ( useecs ) + putc( 'e', stderr ); + if ( usemecs ) + putc( 'm', stderr ); + + if ( strcmp( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) ) ) + fprintf( stderr, " -S%s", skelname ); + + putc( '\n', stderr ); + + fprintf( stderr, " %d/%d NFA states\n", lastnfa, current_mns ); + fprintf( stderr, " %d/%d DFA states (%d words)\n", lastdfa, + current_max_dfas, totnst ); + fprintf( stderr, + " %d rules\n", num_rules - 1 /* - 1 for def. rule */ ); + + if ( num_backtracking == 0 ) + fprintf( stderr, " No backtracking\n" ); + else if ( fullspd || fulltbl ) + fprintf( stderr, " %d backtracking (non-accepting) states\n", + num_backtracking ); + else + fprintf( stderr, " compressed tables always backtrack\n" ); + + if ( bol_needed ) + fprintf( stderr, " Beginning-of-line patterns used\n" ); + + fprintf( stderr, " %d/%d start conditions\n", lastsc, + current_max_scs ); + fprintf( stderr, " %d epsilon states, %d double epsilon states\n", + numeps, eps2 ); + + if ( lastccl == 0 ) + fprintf( stderr, " no character classes\n" ); + else + fprintf( stderr, + " %d/%d character classes needed %d/%d words of storage, %d reused\n", + lastccl, current_maxccls, + cclmap[lastccl] + ccllen[lastccl], + current_max_ccl_tbl_size, cclreuse ); + + fprintf( stderr, " %d state/nextstate pairs created\n", numsnpairs ); + fprintf( stderr, " %d/%d unique/duplicate transitions\n", + numuniq, numdup ); + + if ( fulltbl ) + { + tblsiz = lastdfa * numecs; + fprintf( stderr, " %d table entries\n", tblsiz ); + } + + else + { + tblsiz = 2 * (lastdfa + numtemps) + 2 * tblend; + + fprintf( stderr, " %d/%d base-def entries created\n", + lastdfa + numtemps, current_max_dfas ); + fprintf( stderr, " %d/%d (peak %d) nxt-chk entries created\n", + tblend, current_max_xpairs, peakpairs ); + fprintf( stderr, + " %d/%d (peak %d) template nxt-chk entries created\n", + numtemps * nummecs, current_max_template_xpairs, + numtemps * numecs ); + fprintf( stderr, " %d empty table entries\n", nummt ); + fprintf( stderr, " %d protos created\n", numprots ); + fprintf( stderr, " %d templates created, %d uses\n", + numtemps, tmpuses ); + } + + if ( useecs ) + { + tblsiz = tblsiz + csize; + fprintf( stderr, " %d/%d equivalence classes created\n", + numecs, csize ); + } + + if ( usemecs ) + { + tblsiz = tblsiz + numecs; + fprintf( stderr, " %d/%d meta-equivalence classes created\n", + nummecs, csize ); + } + + fprintf( stderr, " %d (%d saved) hash collisions, %d DFAs equal\n", + hshcol, hshsave, dfaeql ); + fprintf( stderr, " %d sets of reallocations needed\n", num_reallocs ); + fprintf( stderr, " %d total table entries needed\n", tblsiz ); + } + + exit( status ); +} + + +/* flexinit - initialize flex + * + * synopsis + * int argc; + * char **argv; + * flexinit( argc, argv ); + */ + +void flexinit(int argc, char **argv) +{ + int i, sawcmpflag; + char *arg, *flex_gettime(), *mktemp(); + + printstats = syntaxerror = trace = spprdflt = interactive = caseins = false; + backtrack_report = performance_report = ddebug = fulltbl = fullspd = false; + yymore_used = continued_action = reject = false; + yymore_really_used = reject_really_used = false; + gen_line_dirs = usemecs = useecs = true; + + sawcmpflag = false; + use_stdout = false; + + csize = DEFAULT_CSIZE; + + program_name = argv[0]; + + /* read flags */ + for ( --argc, ++argv; argc ; --argc, ++argv ) + { + if ( argv[0][0] != '-' || argv[0][1] == '\0' ) + break; + + arg = argv[0]; + + for ( i = 1; arg[i] != '\0'; ++i ) + switch ( arg[i] ) + { + case 'b': + backtrack_report = true; + break; + + case 'c': + fprintf( stderr, + "%s: Assuming use of deprecated -c flag is really intended to be -C\n", + program_name ); + + /* fall through */ + + case 'C': + if ( i != 1 ) + flexerror( "-C flag must be given separately" ); + + if ( ! sawcmpflag ) + { + useecs = false; + usemecs = false; + fulltbl = false; + sawcmpflag = true; + } + + for ( ++i; arg[i] != '\0'; ++i ) + switch ( arg[i] ) + { + case 'e': + useecs = true; + break; + + case 'F': + fullspd = true; + break; + + case 'f': + fulltbl = true; + break; + + case 'm': + usemecs = true; + break; + + default: + lerrif( "unknown -C option '%c'", + (int) arg[i] ); + break; + } + + goto get_next_arg; + + case 'd': + ddebug = true; + break; + + case 'f': + useecs = usemecs = false; + fulltbl = true; + break; + + case 'F': + useecs = usemecs = false; + fullspd = true; + break; + + case 'I': + interactive = true; + break; + + case 'i': + caseins = true; + break; + + case 'L': + gen_line_dirs = false; + break; + + case 'n': + /* stupid do-nothing deprecated option */ + break; + + case 'p': + performance_report = true; + break; + + case 'S': + if ( i != 1 ) + flexerror( "-S flag must be given separately" ); + + skelname = arg + i + 1; + goto get_next_arg; + + case 's': + spprdflt = true; + break; + + case 't': + use_stdout = true; + break; + + case 'T': + trace = true; + break; + + case 'v': + printstats = true; + break; + + case '8': + csize = CSIZE; + break; + + default: + lerrif( "unknown flag '%c'", (int) arg[i] ); + break; + } + +get_next_arg: /* used by -C and -S flags in lieu of a "continue 2" control */ + ; + } + + if ( (fulltbl || fullspd) && usemecs ) + flexerror( "full table and -Cm don't make sense together" ); + + if ( (fulltbl || fullspd) && interactive ) + flexerror( "full table and -I are (currently) incompatible" ); + + if ( fulltbl && fullspd ) + flexerror( "full table and -F are mutually exclusive" ); + + if ( ! skelname ) + { + static char skeleton_name_storage[400]; + + skelname = skeleton_name_storage; + (void) strcpy( skelname, ENQUOTE(DEFAULT_SKELETON_FILE) ); + } + + if ( ! use_stdout ) + { + FILE *prev_stdout = freopen( outfile, "w", stdout ); + + if ( prev_stdout == NULL ) + lerrsf( "could not create %s", outfile ); + + outfile_created = 1; + } + + num_input_files = argc; + input_files = argv; + set_input_file( num_input_files > 0 ? input_files[0] : NULL ); + + if ( backtrack_report ) + { +#ifndef SHORT_FILE_NAMES + backtrack_file = fopen( "lex.backtrack", "w" ); +#else + backtrack_file = fopen( "lex.bck", "w" ); +#endif + + if ( backtrack_file == NULL ) + flexerror( "could not create lex.backtrack" ); + } + + else + backtrack_file = NULL; + + + lastccl = 0; + lastsc = 0; + + /* initialize the statistics */ + starttime = flex_gettime(); + + if ( (skelfile = fopen( skelname, "r" )) == NULL ) + lerrsf( "can't open skeleton file %s", skelname ); + + epicsTempName ( action_file_name, sizeof ( action_file_name ) ); + if ( action_file_name[0] == '\0' ) + { + lerrsf( "can't create temporary file name", "" ); + } + + if ( ( temp_action_file = fopen ( action_file_name, "w" ) ) == NULL ) + lerrsf( "can't open temporary action file %s", action_file_name ); + + lastdfa = lastnfa = num_rules = numas = numsnpairs = tmpuses = 0; + numecs = numeps = eps2 = num_reallocs = hshcol = dfaeql = totnst = 0; + numuniq = numdup = hshsave = eofseen = datapos = dataline = 0; + num_backtracking = onesp = numprots = 0; + variable_trailing_context_rules = bol_needed = false; + + linenum = sectnum = 1; + firstprot = NIL; + + /* used in mkprot() so that the first proto goes in slot 1 + * of the proto queue + */ + lastprot = 1; + + if ( useecs ) + { /* set up doubly-linked equivalence classes */ + /* We loop all the way up to csize, since ecgroup[csize] is the + * position used for NUL characters + */ + ecgroup[1] = NIL; + + for ( i = 2; i <= csize; ++i ) + { + ecgroup[i] = i - 1; + nextecm[i - 1] = i; + } + + nextecm[csize] = NIL; + } + + else + { /* put everything in its own equivalence class */ + for ( i = 1; i <= csize; ++i ) + { + ecgroup[i] = i; + nextecm[i] = BAD_SUBSCRIPT; /* to catch errors */ + } + } + + set_up_initial_allocations(); +} + + +/* readin - read in the rules section of the input file(s) + * + * synopsis + * readin(); + */ + +void readin(void) +{ + skelout(); + + if ( ddebug ) + puts( "#define FLEX_DEBUG" ); + + if ( csize == 256 ) + puts( "#define YY_CHAR unsigned char" ); + else + puts( "#define YY_CHAR char" ); + + line_directive_out( stdout ); + + if ( yyparse() ) + { + pinpoint_message( "fatal parse error" ); + flexend( 1 ); + } + + if ( xlation ) + { + numecs = ecs_from_xlation( ecgroup ); + useecs = true; + } + + else if ( useecs ) + numecs = cre8ecs( nextecm, ecgroup, csize ); + + else + numecs = csize; + + /* now map the equivalence class for NUL to its expected place */ + ecgroup[0] = ecgroup[csize]; + NUL_ec = abs( ecgroup[0] ); + + if ( useecs ) + ccl2ecl(); +} + + + +/* set_up_initial_allocations - allocate memory for internal tables */ + +void set_up_initial_allocations(void) +{ + current_mns = INITIAL_MNS; + firstst = allocate_integer_array( current_mns ); + lastst = allocate_integer_array( current_mns ); + finalst = allocate_integer_array( current_mns ); + transchar = allocate_integer_array( current_mns ); + trans1 = allocate_integer_array( current_mns ); + trans2 = allocate_integer_array( current_mns ); + accptnum = allocate_integer_array( current_mns ); + assoc_rule = allocate_integer_array( current_mns ); + state_type = allocate_integer_array( current_mns ); + + current_max_rules = INITIAL_MAX_RULES; + rule_type = allocate_integer_array( current_max_rules ); + rule_linenum = allocate_integer_array( current_max_rules ); + + current_max_scs = INITIAL_MAX_SCS; + scset = allocate_integer_array( current_max_scs ); + scbol = allocate_integer_array( current_max_scs ); + scxclu = allocate_integer_array( current_max_scs ); + sceof = allocate_integer_array( current_max_scs ); + scname = allocate_char_ptr_array( current_max_scs ); + actvsc = allocate_integer_array( current_max_scs ); + + current_maxccls = INITIAL_MAX_CCLS; + cclmap = allocate_integer_array( current_maxccls ); + ccllen = allocate_integer_array( current_maxccls ); + cclng = allocate_integer_array( current_maxccls ); + + current_max_ccl_tbl_size = INITIAL_MAX_CCL_TBL_SIZE; + ccltbl = allocate_character_array( current_max_ccl_tbl_size ); + + current_max_dfa_size = INITIAL_MAX_DFA_SIZE; + + current_max_xpairs = INITIAL_MAX_XPAIRS; + nxt = allocate_integer_array( current_max_xpairs ); + chk = allocate_integer_array( current_max_xpairs ); + + current_max_template_xpairs = INITIAL_MAX_TEMPLATE_XPAIRS; + tnxt = allocate_integer_array( current_max_template_xpairs ); + + current_max_dfas = INITIAL_MAX_DFAS; + base = allocate_integer_array( current_max_dfas ); + def = allocate_integer_array( current_max_dfas ); + dfasiz = allocate_integer_array( current_max_dfas ); + accsiz = allocate_integer_array( current_max_dfas ); + dhash = allocate_integer_array( current_max_dfas ); + dss = allocate_int_ptr_array( current_max_dfas ); + dfaacc = allocate_dfaacc_union( current_max_dfas ); + + nultrans = (int *) 0; +} diff --git a/src/toolsComm/flex/misc.c b/src/toolsComm/flex/misc.c new file mode 100644 index 000000000..a80a63e1e --- /dev/null +++ b/src/toolsComm/flex/misc.c @@ -0,0 +1,776 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* misc - miscellaneous flex routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include +#include "flexdef.h" +#include + +/* ANSI C does not guarantee that isascii() is defined */ +#ifndef isascii +#define isascii(c) ((c) <= 0177) +#endif + + + +/* declare functions that have forward references */ + +void dataflush (void); +int otoi (Char []); + + +/* action_out - write the actions from the temporary file to lex.yy.c + * + * synopsis + * action_out(); + * + * Copies the action file up to %% (or end-of-file) to lex.yy.c + */ + +void action_out(void) +{ + char buf[MAXLINE]; + + while ( fgets( buf, MAXLINE, temp_action_file ) != NULL ) + if ( buf[0] == '%' && buf[1] == '%' ) + break; + else + fputs( buf, stdout ); + } + + +/* allocate_array - allocate memory for an integer array of the given size */ + +void *allocate_array(int size, int element_size) +{ + void *mem; + + /* on 16-bit int machines (e.g., 80286) we might be trying to + * allocate more than a signed int can hold, and that won't + * work. Cheap test: + */ + if ( element_size * size <= 0 ) + flexfatal( "request for < 1 byte in allocate_array()" ); + + mem = (void *) malloc( (unsigned) (element_size * size) ); + + if ( mem == NULL ) + flexfatal( "memory allocation failed in allocate_array()" ); + + return ( mem ); + } + + +/* all_lower - true if a string is all lower-case + * + * synopsis: + * Char *str; + * int all_lower(); + * true/false = all_lower( str ); + */ + +int all_lower(Char *str) +{ + while ( *str ) + { + if ( ! isascii( *str ) || ! islower( *str ) ) + return ( 0 ); + ++str; + } + + return ( 1 ); + } + + +/* all_upper - true if a string is all upper-case + * + * synopsis: + * Char *str; + * int all_upper(); + * true/false = all_upper( str ); + */ + +int all_upper(Char *str) +{ + while ( *str ) + { + if ( ! isascii( *str ) || ! isupper( (char) *str ) ) + return ( 0 ); + ++str; + } + + return ( 1 ); + } + + +/* bubble - bubble sort an integer array in increasing order + * + * synopsis + * int v[n], n; + * bubble( v, n ); + * + * description + * sorts the first n elements of array v and replaces them in + * increasing order. + * + * passed + * v - the array to be sorted + * n - the number of elements of 'v' to be sorted */ + +void bubble(int v[], int n) +{ + int i, j, k; + + for ( i = n; i > 1; --i ) + for ( j = 1; j < i; ++j ) + if ( v[j] > v[j + 1] ) /* compare */ + { + k = v[j]; /* exchange */ + v[j] = v[j + 1]; + v[j + 1] = k; + } + } + + +/* clower - replace upper-case letter to lower-case + * + * synopsis: + * Char clower(); + * int c; + * c = clower( c ); + */ + +Char clower(int c) +{ + return ( (isascii( c ) && isupper( c )) ? tolower( c ) : c ); + } + + +/* copy_string - returns a dynamically allocated copy of a string + * + * synopsis + * char *str, *copy, *copy_string(); + * copy = copy_string( str ); + */ + +char *copy_string(char *str) +{ + char *c; + char *copy; + + /* find length */ + for ( c = str; *c; ++c ) + ; + + copy = malloc( (unsigned) ((c - str + 1) * sizeof( char )) ); + + if ( copy == NULL ) + flexfatal( "dynamic memory failure in copy_string()" ); + + for ( c = copy; (*c++ = *str++); ) + ; + + return ( copy ); + } + + +/* copy_unsigned_string - + * returns a dynamically allocated copy of a (potentially) unsigned string + * + * synopsis + * Char *str, *copy, *copy_unsigned_string(); + * copy = copy_unsigned_string( str ); + */ + +Char *copy_unsigned_string(Char *str) +{ + Char *c; + Char *copy; + + /* find length */ + for ( c = str; *c; ++c ) + ; + + copy = (Char *) malloc( (unsigned) ((c - str + 1) * sizeof( Char )) ); + + if ( copy == NULL ) + flexfatal( "dynamic memory failure in copy_unsigned_string()" ); + + for ( c = copy; (*c++ = *str++); ) + ; + + return ( copy ); + } + + +/* cshell - shell sort a character array in increasing order + * + * synopsis + * + * Char v[n]; + * int n, special_case_0; + * cshell( v, n, special_case_0 ); + * + * description + * does a shell sort of the first n elements of array v. + * If special_case_0 is true, then any element equal to 0 + * is instead assumed to have infinite weight. + * + * passed + * v - array to be sorted + * n - number of elements of v to be sorted + */ + +void cshell(Char *v, int n, int special_case_0) +{ + int gap, i, j, jg; + Char k; + + for ( gap = n / 2; gap > 0; gap = gap / 2 ) + for ( i = gap; i < n; ++i ) + for ( j = i - gap; j >= 0; j = j - gap ) + { + jg = j + gap; + + if ( special_case_0 ) + { + if ( v[jg] == 0 ) + break; + + else if ( v[j] != 0 && v[j] <= v[jg] ) + break; + } + + else if ( v[j] <= v[jg] ) + break; + + k = v[j]; + v[j] = v[jg]; + v[jg] = k; + } + } + + +/* dataend - finish up a block of data declarations + * + * synopsis + * dataend(); + */ + +void dataend(void) +{ + if ( datapos > 0 ) + dataflush(); + + /* add terminator for initialization */ + puts( " } ;\n" ); + + dataline = 0; + datapos = 0; + } + + + +/* dataflush - flush generated data statements + * + * synopsis + * dataflush(); + */ + +void dataflush(void) +{ + putchar( '\n' ); + + if ( ++dataline >= NUMDATALINES ) + { + /* put out a blank line so that the table is grouped into + * large blocks that enable the user to find elements easily + */ + putchar( '\n' ); + dataline = 0; + } + + /* reset the number of characters written on the current line */ + datapos = 0; + } + + +/* flexerror - report an error message and terminate + * + * synopsis + * char msg[]; + * flexerror( msg ); + */ + +void flexerror(char *msg) +{ + fprintf( stderr, "%s: %s\n", program_name, msg ); + + flexend( 1 ); + } + + +/* flexfatal - report a fatal error message and terminate + * + * synopsis + * char msg[]; + * flexfatal( msg ); + */ + +void flexfatal(char *msg) +{ + fprintf( stderr, "%s: fatal internal error, %s\n", program_name, msg ); + flexend( 1 ); + } + + +/* flex_gettime - return current time + * + * synopsis + * char *flex_gettime(), *time_str; + * time_str = flex_gettime(); + * + * note + * the routine name has the "flex_" prefix because of name clashes + * with Turbo-C + */ + +/* include sys/types.h to use time_t and make lint happy */ + +#ifndef MS_DOS +#include +#endif + +#ifdef MS_DOS +#include +typedef long time_t; +#endif + +char *flex_gettime(void) +{ + time_t t, time(time_t *); + char *result, *ctime(const time_t *), *copy_string(char *str); + + t = time( NULL ); + + result = copy_string( ctime( &t ) ); + + /* get rid of trailing newline */ + result[24] = '\0'; + + return ( result ); + } + + +/* lerrif - report an error message formatted with one integer argument + * + * synopsis + * char msg[]; + * int arg; + * lerrif( msg, arg ); + */ + +void lerrif(char *msg, int arg) +{ + char errmsg[MAXLINE]; + (void) sprintf( errmsg, msg, arg ); + flexerror( errmsg ); + } + + +/* lerrsf - report an error message formatted with one string argument + * + * synopsis + * char msg[], arg[]; + * lerrsf( msg, arg ); + */ + +void lerrsf(char *msg, char *arg) +{ + char errmsg[MAXLINE]; + + (void) sprintf( errmsg, msg, arg ); + flexerror( errmsg ); + } + + +/* htoi - convert a hexadecimal digit string to an integer value + * + * synopsis: + * int val, htoi(); + * Char str[]; + * val = htoi( str ); + */ + +int htoi(unsigned char *str) +{ + int result; + + (void) sscanf( (char *) str, "%x", &result ); + + return ( result ); + } + + +/* is_hex_digit - returns true if a character is a valid hex digit, false + * otherwise + * + * synopsis: + * int true_or_false, is_hex_digit(); + * int ch; + * val = is_hex_digit( ch ); + */ + +int is_hex_digit(int ch) +{ + if ( isdigit( ch ) ) + return ( 1 ); + + switch ( clower( ch ) ) + { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return ( 1 ); + + default: + return ( 0 ); + } + } + + +/* line_directive_out - spit out a "# line" statement */ + +void line_directive_out(FILE *output_file_name) +{ + if ( infilename && gen_line_dirs ) + fprintf( output_file_name, "# line %d \"%s\"\n", linenum, infilename ); + } + + +/* mk2data - generate a data statement for a two-dimensional array + * + * synopsis + * int value; + * mk2data( value ); + * + * generates a data statement initializing the current 2-D array to "value" + */ +void mk2data(int value) +{ + if ( datapos >= NUMDATAITEMS ) + { + putchar( ',' ); + dataflush(); + } + + if ( datapos == 0 ) + /* indent */ + fputs( " ", stdout ); + + else + putchar( ',' ); + + ++datapos; + + printf( "%5d", value ); + } + + +/* mkdata - generate a data statement + * + * synopsis + * int value; + * mkdata( value ); + * + * generates a data statement initializing the current array element to + * "value" + */ +void mkdata(int value) +{ + if ( datapos >= NUMDATAITEMS ) + { + putchar( ',' ); + dataflush(); + } + + if ( datapos == 0 ) + /* indent */ + fputs( " ", stdout ); + + else + putchar( ',' ); + + ++datapos; + + printf( "%5d", value ); + } + + +/* myctoi - return the integer represented by a string of digits + * + * synopsis + * Char array[]; + * int val, myctoi(); + * val = myctoi( array ); + * + */ + +int myctoi(Char *array) +{ + int val = 0; + + (void) sscanf( (char *) array, "%d", &val ); + + return ( val ); + } + + +/* myesc - return character corresponding to escape sequence + * + * synopsis + * Char array[], c, myesc(); + * c = myesc( array ); + * + */ + +Char myesc(Char *array) +{ + Char c, esc_char; + int sptr; + + switch ( array[1] ) + { + case 'a': return ( '\a' ); + case 'b': return ( '\b' ); + case 'f': return ( '\f' ); + case 'n': return ( '\n' ); + case 'r': return ( '\r' ); + case 't': return ( '\t' ); + case 'v': return ( '\v' ); + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { /* \ */ + sptr = 1; + + while ( isascii( array[sptr] ) && isdigit( array[sptr] ) ) + /* don't increment inside loop control because if + * isdigit() is a macro it might expand into multiple + * increments ... + */ + ++sptr; + + c = array[sptr]; + array[sptr] = '\0'; + + esc_char = otoi( array + 1 ); + + array[sptr] = c; + + return ( esc_char ); + } + + case 'x': + { /* \x */ + int sptr = 2; + + while ( isascii( array[sptr] ) && is_hex_digit( array[sptr] ) ) + /* don't increment inside loop control because if + * isdigit() is a macro it might expand into multiple + * increments ... + */ + ++sptr; + + c = array[sptr]; + array[sptr] = '\0'; + + esc_char = htoi( array + 2 ); + + array[sptr] = c; + + return ( esc_char ); + } + + default: + return ( array[1] ); + } + } + + +/* otoi - convert an octal digit string to an integer value + * + * synopsis: + * int val, otoi(); + * Char str[]; + * val = otoi( str ); + */ + +int otoi(Char *str) +{ + int result; + + (void) sscanf( (char *) str, "%o", &result ); + + return ( result ); + } + + +/* readable_form - return the the human-readable form of a character + * + * synopsis: + * int c; + * char *readable_form(); + * = readable_form( c ); + * + * The returned string is in static storage. + */ + +char *readable_form(int c) +{ + static char rform[10]; + + if ( (c >= 0 && c < 32) || c >= 127 ) + { + switch ( c ) + { + case '\n': return ( "\\n" ); + case '\t': return ( "\\t" ); + case '\f': return ( "\\f" ); + case '\r': return ( "\\r" ); + case '\b': return ( "\\b" ); + + default: + (void) sprintf( rform, "\\%.3o", c ); + return ( rform ); + } + } + + else if ( c == ' ' ) + return ( "' '" ); + + else + { + rform[0] = c; + rform[1] = '\0'; + + return ( rform ); + } + } + + +/* reallocate_array - increase the size of a dynamic array */ + +void *reallocate_array(void *array, int size, int element_size) +{ + void *new_array; + + /* same worry as in allocate_array(): */ + if ( size * element_size <= 0 ) + flexfatal( "attempt to increase array size by less than 1 byte" ); + + new_array = + (void *) realloc( (char *)array, (unsigned) (size * element_size )); + + if ( new_array == NULL ) + flexfatal( "attempt to increase array size failed" ); + + return ( new_array ); + } + + +/* skelout - write out one section of the skeleton file + * + * synopsis + * skelout(); + * + * DESCRIPTION + * Copies from skelfile to stdout until a line beginning with "%%" or + * EOF is found. + */ +void skelout(void) +{ + char buf[MAXLINE]; + + while ( fgets( buf, MAXLINE, skelfile ) != NULL ) + if ( buf[0] == '%' && buf[1] == '%' ) + break; + else + fputs( buf, stdout ); + } + + +/* transition_struct_out - output a yy_trans_info structure + * + * synopsis + * int element_v, element_n; + * transition_struct_out( element_v, element_n ); + * + * outputs the yy_trans_info structure with the two elements, element_v and + * element_n. Formats the output with spaces and carriage returns. + */ + +void transition_struct_out(int element_v, int element_n) +{ + printf( "%7d, %5d,", element_v, element_n ); + + datapos += TRANS_STRUCT_PRINT_LENGTH; + + if ( datapos >= 75 ) + { + putchar( '\n' ); + + if ( ++dataline % 10 == 0 ) + putchar( '\n' ); + + datapos = 0; + } + } diff --git a/src/toolsComm/flex/nfa.c b/src/toolsComm/flex/nfa.c new file mode 100644 index 000000000..67dd2d2ad --- /dev/null +++ b/src/toolsComm/flex/nfa.c @@ -0,0 +1,694 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* nfa - NFA construction routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +int dupmachine (int); +void mkxtion (int, int); + + +/* add_accept - add an accepting state to a machine + * + * synopsis + * + * add_accept( mach, accepting_number ); + * + * accepting_number becomes mach's accepting number. + */ + +void add_accept(int mach, int accepting_number) +{ + /* hang the accepting number off an epsilon state. if it is associated + * with a state that has a non-epsilon out-transition, then the state + * will accept BEFORE it makes that transition, i.e., one character + * too soon + */ + + if ( transchar[finalst[mach]] == SYM_EPSILON ) + accptnum[finalst[mach]] = accepting_number; + + else + { + int astate = mkstate( SYM_EPSILON ); + accptnum[astate] = accepting_number; + mach = link_machines( mach, astate ); + } + } + + +/* copysingl - make a given number of copies of a singleton machine + * + * synopsis + * + * newsng = copysingl( singl, num ); + * + * newsng - a new singleton composed of num copies of singl + * singl - a singleton machine + * num - the number of copies of singl to be present in newsng + */ + +int copysingl(int singl, int num) +{ + int copy, i; + + copy = mkstate( SYM_EPSILON ); + + for ( i = 1; i <= num; ++i ) + copy = link_machines( copy, dupmachine( singl ) ); + + return ( copy ); + } + + +/* dumpnfa - debugging routine to write out an nfa + * + * synopsis + * int state1; + * dumpnfa( state1 ); + */ + +void dumpnfa(int state1) +{ + int sym, tsp1, tsp2, anum, ns; + + fprintf( stderr, "\n\n********** beginning dump of nfa with start state %d\n", + state1 ); + + /* we probably should loop starting at firstst[state1] and going to + * lastst[state1], but they're not maintained properly when we "or" + * all of the rules together. So we use our knowledge that the machine + * starts at state 1 and ends at lastnfa. + */ + + /* for ( ns = firstst[state1]; ns <= lastst[state1]; ++ns ) */ + for ( ns = 1; ns <= lastnfa; ++ns ) + { + fprintf( stderr, "state # %4d\t", ns ); + + sym = transchar[ns]; + tsp1 = trans1[ns]; + tsp2 = trans2[ns]; + anum = accptnum[ns]; + + fprintf( stderr, "%3d: %4d, %4d", sym, tsp1, tsp2 ); + + if ( anum != NIL ) + fprintf( stderr, " [%d]", anum ); + + fprintf( stderr, "\n" ); + } + + fprintf( stderr, "********** end of dump\n" ); + } + + +/* dupmachine - make a duplicate of a given machine + * + * synopsis + * + * copy = dupmachine( mach ); + * + * copy - holds duplicate of mach + * mach - machine to be duplicated + * + * note that the copy of mach is NOT an exact duplicate; rather, all the + * transition states values are adjusted so that the copy is self-contained, + * as the original should have been. + * + * also note that the original MUST be contiguous, with its low and high + * states accessible by the arrays firstst and lastst + */ + +int dupmachine(int mach) +{ + int i, init, state_offset; + int state = 0; + int last = lastst[mach]; + + for ( i = firstst[mach]; i <= last; ++i ) + { + state = mkstate( transchar[i] ); + + if ( trans1[i] != NO_TRANSITION ) + { + mkxtion( finalst[state], trans1[i] + state - i ); + + if ( transchar[i] == SYM_EPSILON && trans2[i] != NO_TRANSITION ) + mkxtion( finalst[state], trans2[i] + state - i ); + } + + accptnum[state] = accptnum[i]; + } + + if ( state == 0 ) + flexfatal( "empty machine in dupmachine()" ); + + state_offset = state - i + 1; + + init = mach + state_offset; + firstst[init] = firstst[mach] + state_offset; + finalst[init] = finalst[mach] + state_offset; + lastst[init] = lastst[mach] + state_offset; + + return ( init ); + } + + +/* finish_rule - finish up the processing for a rule + * + * synopsis + * + * finish_rule( mach, variable_trail_rule, headcnt, trailcnt ); + * + * An accepting number is added to the given machine. If variable_trail_rule + * is true then the rule has trailing context and both the head and trail + * are variable size. Otherwise if headcnt or trailcnt is non-zero then + * the machine recognizes a pattern with trailing context and headcnt is + * the number of characters in the matched part of the pattern, or zero + * if the matched part has variable length. trailcnt is the number of + * trailing context characters in the pattern, or zero if the trailing + * context has variable length. + */ + +void finish_rule(int mach, int variable_trail_rule, int headcnt, int trailcnt) +{ + add_accept( mach, num_rules ); + + /* we did this in new_rule(), but it often gets the wrong + * number because we do it before we start parsing the current rule + */ + rule_linenum[num_rules] = linenum; + + /* if this is a continued action, then the line-number has + * already been updated, giving us the wrong number + */ + if ( continued_action ) + --rule_linenum[num_rules]; + + fprintf( temp_action_file, "case %d:\n", num_rules ); + + if ( variable_trail_rule ) + { + rule_type[num_rules] = RULE_VARIABLE; + + if ( performance_report ) + fprintf( stderr, "Variable trailing context rule at line %d\n", + rule_linenum[num_rules] ); + + variable_trailing_context_rules = true; + } + + else + { + rule_type[num_rules] = RULE_NORMAL; + + if ( headcnt > 0 || trailcnt > 0 ) + { + /* do trailing context magic to not match the trailing characters */ + char *scanner_cp = "yy_c_buf_p = yy_cp"; + char *scanner_bp = "yy_bp"; + + fprintf( temp_action_file, + "*yy_cp = yy_hold_char; /* undo effects of setting up yytext */\n" ); + + if ( headcnt > 0 ) + fprintf( temp_action_file, "%s = %s + %d;\n", + scanner_cp, scanner_bp, headcnt ); + + else + fprintf( temp_action_file, + "%s -= %d;\n", scanner_cp, trailcnt ); + + fprintf( temp_action_file, + "YY_DO_BEFORE_ACTION; /* set up yytext again */\n" ); + } + } + + line_directive_out( temp_action_file ); + } + + +/* link_machines - connect two machines together + * + * synopsis + * + * new = link_machines( first, last ); + * + * new - a machine constructed by connecting first to last + * first - the machine whose successor is to be last + * last - the machine whose predecessor is to be first + * + * note: this routine concatenates the machine first with the machine + * last to produce a machine new which will pattern-match first first + * and then last, and will fail if either of the sub-patterns fails. + * FIRST is set to new by the operation. last is unmolested. + */ + +int link_machines(int first, int last) +{ + if ( first == NIL ) + return ( last ); + + else if ( last == NIL ) + return ( first ); + + else + { + mkxtion( finalst[first], last ); + finalst[first] = finalst[last]; + lastst[first] = max( lastst[first], lastst[last] ); + firstst[first] = min( firstst[first], firstst[last] ); + + return ( first ); + } + } + + +/* mark_beginning_as_normal - mark each "beginning" state in a machine + * as being a "normal" (i.e., not trailing context- + * associated) states + * + * synopsis + * + * mark_beginning_as_normal( mach ) + * + * mach - machine to mark + * + * The "beginning" states are the epsilon closure of the first state + */ + +void mark_beginning_as_normal(int mach) +{ + switch ( state_type[mach] ) + { + case STATE_NORMAL: + /* oh, we've already visited here */ + return; + + case STATE_TRAILING_CONTEXT: + state_type[mach] = STATE_NORMAL; + + if ( transchar[mach] == SYM_EPSILON ) + { + if ( trans1[mach] != NO_TRANSITION ) + mark_beginning_as_normal( trans1[mach] ); + + if ( trans2[mach] != NO_TRANSITION ) + mark_beginning_as_normal( trans2[mach] ); + } + break; + + default: + flexerror( "bad state type in mark_beginning_as_normal()" ); + break; + } + } + + +/* mkbranch - make a machine that branches to two machines + * + * synopsis + * + * branch = mkbranch( first, second ); + * + * branch - a machine which matches either first's pattern or second's + * first, second - machines whose patterns are to be or'ed (the | operator) + * + * note that first and second are NEITHER destroyed by the operation. Also, + * the resulting machine CANNOT be used with any other "mk" operation except + * more mkbranch's. Compare with mkor() + */ + +int mkbranch(int first, int second) +{ + int eps; + + if ( first == NO_TRANSITION ) + return ( second ); + + else if ( second == NO_TRANSITION ) + return ( first ); + + eps = mkstate( SYM_EPSILON ); + + mkxtion( eps, first ); + mkxtion( eps, second ); + + return ( eps ); + } + + +/* mkclos - convert a machine into a closure + * + * synopsis + * new = mkclos( state ); + * + * new - a new state which matches the closure of "state" + */ + +int mkclos(int state) +{ + return ( mkopt( mkposcl( state ) ) ); + } + + +/* mkopt - make a machine optional + * + * synopsis + * + * new = mkopt( mach ); + * + * new - a machine which optionally matches whatever mach matched + * mach - the machine to make optional + * + * notes: + * 1. mach must be the last machine created + * 2. mach is destroyed by the call + */ + +int mkopt(int mach) +{ + int eps; + + if ( ! SUPER_FREE_EPSILON(finalst[mach]) ) + { + eps = mkstate( SYM_EPSILON ); + mach = link_machines( mach, eps ); + } + + /* can't skimp on the following if FREE_EPSILON(mach) is true because + * some state interior to "mach" might point back to the beginning + * for a closure + */ + eps = mkstate( SYM_EPSILON ); + mach = link_machines( eps, mach ); + + mkxtion( mach, finalst[mach] ); + + return ( mach ); + } + + +/* mkor - make a machine that matches either one of two machines + * + * synopsis + * + * new = mkor( first, second ); + * + * new - a machine which matches either first's pattern or second's + * first, second - machines whose patterns are to be or'ed (the | operator) + * + * note that first and second are both destroyed by the operation + * the code is rather convoluted because an attempt is made to minimize + * the number of epsilon states needed + */ + +int mkor(int first, int second) +{ + int eps, orend; + + if ( first == NIL ) + return ( second ); + + else if ( second == NIL ) + return ( first ); + + else + { + /* see comment in mkopt() about why we can't use the first state + * of "first" or "second" if they satisfy "FREE_EPSILON" + */ + eps = mkstate( SYM_EPSILON ); + + first = link_machines( eps, first ); + + mkxtion( first, second ); + + if ( SUPER_FREE_EPSILON(finalst[first]) && + accptnum[finalst[first]] == NIL ) + { + orend = finalst[first]; + mkxtion( finalst[second], orend ); + } + + else if ( SUPER_FREE_EPSILON(finalst[second]) && + accptnum[finalst[second]] == NIL ) + { + orend = finalst[second]; + mkxtion( finalst[first], orend ); + } + + else + { + eps = mkstate( SYM_EPSILON ); + + first = link_machines( first, eps ); + orend = finalst[first]; + + mkxtion( finalst[second], orend ); + } + } + + finalst[first] = orend; + return ( first ); + } + + +/* mkposcl - convert a machine into a positive closure + * + * synopsis + * new = mkposcl( state ); + * + * new - a machine matching the positive closure of "state" + */ + +int mkposcl(int state) +{ + int eps; + + if ( SUPER_FREE_EPSILON(finalst[state]) ) + { + mkxtion( finalst[state], state ); + return ( state ); + } + + else + { + eps = mkstate( SYM_EPSILON ); + mkxtion( eps, state ); + return ( link_machines( state, eps ) ); + } + } + + +/* mkrep - make a replicated machine + * + * synopsis + * new = mkrep( mach, lb, ub ); + * + * new - a machine that matches whatever "mach" matched from "lb" + * number of times to "ub" number of times + * + * note + * if "ub" is INFINITY then "new" matches "lb" or more occurrences of "mach" + */ + +int mkrep(int mach, int lb, int ub) +{ + int base_mach, tail, copy, i; + + base_mach = copysingl( mach, lb - 1 ); + + if ( ub == INFINITY ) + { + copy = dupmachine( mach ); + mach = link_machines( mach, + link_machines( base_mach, mkclos( copy ) ) ); + } + + else + { + tail = mkstate( SYM_EPSILON ); + + for ( i = lb; i < ub; ++i ) + { + copy = dupmachine( mach ); + tail = mkopt( link_machines( copy, tail ) ); + } + + mach = link_machines( mach, link_machines( base_mach, tail ) ); + } + + return ( mach ); + } + + +/* mkstate - create a state with a transition on a given symbol + * + * synopsis + * + * state = mkstate( sym ); + * + * state - a new state matching sym + * sym - the symbol the new state is to have an out-transition on + * + * note that this routine makes new states in ascending order through the + * state array (and increments LASTNFA accordingly). The routine DUPMACHINE + * relies on machines being made in ascending order and that they are + * CONTIGUOUS. Change it and you will have to rewrite DUPMACHINE (kludge + * that it admittedly is) + */ + +int mkstate(int sym) +{ + if ( ++lastnfa >= current_mns ) + { + if ( (current_mns += MNS_INCREMENT) >= MAXIMUM_MNS ) + lerrif( "input rules are too complicated (>= %d NFA states)", + current_mns ); + + ++num_reallocs; + + firstst = reallocate_integer_array( firstst, current_mns ); + lastst = reallocate_integer_array( lastst, current_mns ); + finalst = reallocate_integer_array( finalst, current_mns ); + transchar = reallocate_integer_array( transchar, current_mns ); + trans1 = reallocate_integer_array( trans1, current_mns ); + trans2 = reallocate_integer_array( trans2, current_mns ); + accptnum = reallocate_integer_array( accptnum, current_mns ); + assoc_rule = reallocate_integer_array( assoc_rule, current_mns ); + state_type = reallocate_integer_array( state_type, current_mns ); + } + + firstst[lastnfa] = lastnfa; + finalst[lastnfa] = lastnfa; + lastst[lastnfa] = lastnfa; + transchar[lastnfa] = sym; + trans1[lastnfa] = NO_TRANSITION; + trans2[lastnfa] = NO_TRANSITION; + accptnum[lastnfa] = NIL; + assoc_rule[lastnfa] = num_rules; + state_type[lastnfa] = current_state_type; + + /* fix up equivalence classes base on this transition. Note that any + * character which has its own transition gets its own equivalence class. + * Thus only characters which are only in character classes have a chance + * at being in the same equivalence class. E.g. "a|b" puts 'a' and 'b' + * into two different equivalence classes. "[ab]" puts them in the same + * equivalence class (barring other differences elsewhere in the input). + */ + + if ( sym < 0 ) + { + /* we don't have to update the equivalence classes since that was + * already done when the ccl was created for the first time + */ + } + + else if ( sym == SYM_EPSILON ) + ++numeps; + + else + { + if ( useecs ) + /* map NUL's to csize */ + mkechar( sym ? sym : csize, nextecm, ecgroup ); + } + + return ( lastnfa ); + } + + +/* mkxtion - make a transition from one state to another + * + * synopsis + * + * mkxtion( statefrom, stateto ); + * + * statefrom - the state from which the transition is to be made + * stateto - the state to which the transition is to be made + */ + +void mkxtion(int statefrom, int stateto) +{ + if ( trans1[statefrom] == NO_TRANSITION ) + trans1[statefrom] = stateto; + + else if ( (transchar[statefrom] != SYM_EPSILON) || + (trans2[statefrom] != NO_TRANSITION) ) + flexfatal( "found too many transitions in mkxtion()" ); + + else + { /* second out-transition for an epsilon state */ + ++eps2; + trans2[statefrom] = stateto; + } + } + +/* new_rule - initialize for a new rule + * + * synopsis + * + * new_rule(); + * + * the global num_rules is incremented and the any corresponding dynamic + * arrays (such as rule_type[]) are grown as needed. + */ + +void new_rule(void) +{ + if ( ++num_rules >= current_max_rules ) + { + ++num_reallocs; + current_max_rules += MAX_RULES_INCREMENT; + rule_type = reallocate_integer_array( rule_type, current_max_rules ); + rule_linenum = + reallocate_integer_array( rule_linenum, current_max_rules ); + } + + if ( num_rules > MAX_RULE ) + lerrif( "too many rules (> %d)!", MAX_RULE ); + + rule_linenum[num_rules] = linenum; + } diff --git a/src/toolsComm/flex/parse.y b/src/toolsComm/flex/parse.y new file mode 100644 index 000000000..bf4ea28fb --- /dev/null +++ b/src/toolsComm/flex/parse.y @@ -0,0 +1,707 @@ + +/* parse.y - parser for flex input */ + +%token CHAR NUMBER SECTEND SCDECL XSCDECL WHITESPACE NAME PREVCCL EOF_OP + +%{ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + +int pat, scnum, eps, headcnt, trailcnt, anyccl, lastchar, i, actvp, rulelen; +int trlcontxt, xcluflg, cclsorted, varlength, variable_trail_rule; +Char clower(); + +static int madeany = false; /* whether we've made the '.' character class */ +int previous_continued_action; /* whether the previous rule's action was '|' */ + +%} + +%% +goal : initlex sect1 sect1end sect2 initforrule + { /* add default rule */ + int def_rule; + + pat = cclinit(); + cclnegate( pat ); + + def_rule = mkstate( -pat ); + + finish_rule( def_rule, false, 0, 0 ); + + for ( i = 1; i <= lastsc; ++i ) + scset[i] = mkbranch( scset[i], def_rule ); + + if ( spprdflt ) + fputs( "YY_FATAL_ERROR( \"flex scanner jammed\" )", + temp_action_file ); + else + fputs( "ECHO", temp_action_file ); + + fputs( ";\n\tYY_BREAK\n", temp_action_file ); + } + ; + +initlex : + { + /* initialize for processing rules */ + + /* create default DFA start condition */ + scinstal( "INITIAL", false ); + } + ; + +sect1 : sect1 startconddecl WHITESPACE namelist1 '\n' + | + | error '\n' + { synerr( "unknown error processing section 1" ); } + ; + +sect1end : SECTEND + ; + +startconddecl : SCDECL + { + /* these productions are separate from the s1object + * rule because the semantics must be done before + * we parse the remainder of an s1object + */ + + xcluflg = false; + } + + | XSCDECL + { xcluflg = true; } + ; + +namelist1 : namelist1 WHITESPACE NAME + { scinstal( nmstr, xcluflg ); } + + | NAME + { scinstal( nmstr, xcluflg ); } + + | error + { synerr( "bad start condition list" ); } + ; + +sect2 : sect2 initforrule flexrule '\n' + | + ; + +initforrule : + { + /* initialize for a parse of one rule */ + trlcontxt = variable_trail_rule = varlength = false; + trailcnt = headcnt = rulelen = 0; + current_state_type = STATE_NORMAL; + previous_continued_action = continued_action; + new_rule(); + } + ; + +flexrule : scon '^' rule + { + pat = $3; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + for ( i = 1; i <= actvp; ++i ) + scbol[actvsc[i]] = + mkbranch( scbol[actvsc[i]], pat ); + + if ( ! bol_needed ) + { + bol_needed = true; + + if ( performance_report ) + pinpoint_message( + "'^' operator results in sub-optimal performance" ); + } + } + + | scon rule + { + pat = $2; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + for ( i = 1; i <= actvp; ++i ) + scset[actvsc[i]] = + mkbranch( scset[actvsc[i]], pat ); + } + + | '^' rule + { + pat = $2; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + /* add to all non-exclusive start conditions, + * including the default (0) start condition + */ + + for ( i = 1; i <= lastsc; ++i ) + if ( ! scxclu[i] ) + scbol[i] = mkbranch( scbol[i], pat ); + + if ( ! bol_needed ) + { + bol_needed = true; + + if ( performance_report ) + pinpoint_message( + "'^' operator results in sub-optimal performance" ); + } + } + + | rule + { + pat = $1; + finish_rule( pat, variable_trail_rule, + headcnt, trailcnt ); + + for ( i = 1; i <= lastsc; ++i ) + if ( ! scxclu[i] ) + scset[i] = mkbranch( scset[i], pat ); + } + + | scon EOF_OP + { build_eof_action(); } + + | EOF_OP + { + /* this EOF applies to all start conditions + * which don't already have EOF actions + */ + actvp = 0; + + for ( i = 1; i <= lastsc; ++i ) + if ( ! sceof[i] ) + actvsc[++actvp] = i; + + if ( actvp == 0 ) + pinpoint_message( + "warning - all start conditions already have <> rules" ); + + else + build_eof_action(); + } + + | error + { synerr( "unrecognized rule" ); } + ; + +scon : '<' namelist2 '>' + ; + +namelist2 : namelist2 ',' NAME + { + if ( (scnum = sclookup( nmstr )) == 0 ) + format_pinpoint_message( + "undeclared start condition %s", nmstr ); + + else + actvsc[++actvp] = scnum; + } + + | NAME + { + if ( (scnum = sclookup( nmstr )) == 0 ) + format_pinpoint_message( + "undeclared start condition %s", nmstr ); + else + actvsc[actvp = 1] = scnum; + } + + | error + { synerr( "bad start condition list" ); } + ; + +rule : re2 re + { + if ( transchar[lastst[$2]] != SYM_EPSILON ) + /* provide final transition \now/ so it + * will be marked as a trailing context + * state + */ + $2 = link_machines( $2, mkstate( SYM_EPSILON ) ); + + mark_beginning_as_normal( $2 ); + current_state_type = STATE_NORMAL; + + if ( previous_continued_action ) + { + /* we need to treat this as variable trailing + * context so that the backup does not happen + * in the action but before the action switch + * statement. If the backup happens in the + * action, then the rules "falling into" this + * one's action will *also* do the backup, + * erroneously. + */ + if ( ! varlength || headcnt != 0 ) + { + fprintf( stderr, + "%s: warning - trailing context rule at line %d made variable because\n", + program_name, linenum ); + fprintf( stderr, + " of preceding '|' action\n" ); + } + + /* mark as variable */ + varlength = true; + headcnt = 0; + } + + if ( varlength && headcnt == 0 ) + { /* variable trailing context rule */ + /* mark the first part of the rule as the accepting + * "head" part of a trailing context rule + */ + /* by the way, we didn't do this at the beginning + * of this production because back then + * current_state_type was set up for a trail + * rule, and add_accept() can create a new + * state ... + */ + add_accept( $1, num_rules | YY_TRAILING_HEAD_MASK ); + variable_trail_rule = true; + } + + else + trailcnt = rulelen; + + $$ = link_machines( $1, $2 ); + } + + | re2 re '$' + { synerr( "trailing context used twice" ); } + + | re '$' + { + if ( trlcontxt ) + { + synerr( "trailing context used twice" ); + $$ = mkstate( SYM_EPSILON ); + } + + else if ( previous_continued_action ) + { + /* see the comment in the rule for "re2 re" + * above + */ + if ( ! varlength || headcnt != 0 ) + { + fprintf( stderr, + "%s: warning - trailing context rule at line %d made variable because\n", + program_name, linenum ); + fprintf( stderr, + " of preceding '|' action\n" ); + } + + /* mark as variable */ + varlength = true; + headcnt = 0; + } + + trlcontxt = true; + + if ( ! varlength ) + headcnt = rulelen; + + ++rulelen; + trailcnt = 1; + + eps = mkstate( SYM_EPSILON ); + $$ = link_machines( $1, + link_machines( eps, mkstate( '\n' ) ) ); + } + + | re + { + $$ = $1; + + if ( trlcontxt ) + { + if ( varlength && headcnt == 0 ) + /* both head and trail are variable-length */ + variable_trail_rule = true; + else + trailcnt = rulelen; + } + } + ; + + +re : re '|' series + { + varlength = true; + $$ = mkor( $1, $3 ); + } + + | series + { $$ = $1; } + ; + + +re2 : re '/' + { + /* this rule is written separately so + * the reduction will occur before the trailing + * series is parsed + */ + + if ( trlcontxt ) + synerr( "trailing context used twice" ); + else + trlcontxt = true; + + if ( varlength ) + /* we hope the trailing context is fixed-length */ + varlength = false; + else + headcnt = rulelen; + + rulelen = 0; + + current_state_type = STATE_TRAILING_CONTEXT; + $$ = $1; + } + ; + +series : series singleton + { + /* this is where concatenation of adjacent patterns + * gets done + */ + $$ = link_machines( $1, $2 ); + } + + | singleton + { $$ = $1; } + ; + +singleton : singleton '*' + { + varlength = true; + + $$ = mkclos( $1 ); + } + + | singleton '+' + { + varlength = true; + + $$ = mkposcl( $1 ); + } + + | singleton '?' + { + varlength = true; + + $$ = mkopt( $1 ); + } + + | singleton '{' NUMBER ',' NUMBER '}' + { + varlength = true; + + if ( $3 > $5 || $3 < 0 ) + { + synerr( "bad iteration values" ); + $$ = $1; + } + else + { + if ( $3 == 0 ) + $$ = mkopt( mkrep( $1, $3, $5 ) ); + else + $$ = mkrep( $1, $3, $5 ); + } + } + + | singleton '{' NUMBER ',' '}' + { + varlength = true; + + if ( $3 <= 0 ) + { + synerr( "iteration value must be positive" ); + $$ = $1; + } + + else + $$ = mkrep( $1, $3, INFINITY ); + } + + | singleton '{' NUMBER '}' + { + /* the singleton could be something like "(foo)", + * in which case we have no idea what its length + * is, so we punt here. + */ + varlength = true; + + if ( $3 <= 0 ) + { + synerr( "iteration value must be positive" ); + $$ = $1; + } + + else + $$ = link_machines( $1, copysingl( $1, $3 - 1 ) ); + } + + | '.' + { + if ( ! madeany ) + { + /* create the '.' character class */ + anyccl = cclinit(); + ccladd( anyccl, '\n' ); + cclnegate( anyccl ); + + if ( useecs ) + mkeccl( ccltbl + cclmap[anyccl], + ccllen[anyccl], nextecm, + ecgroup, csize, csize ); + + madeany = true; + } + + ++rulelen; + + $$ = mkstate( -anyccl ); + } + + | fullccl + { + if ( ! cclsorted ) + /* sort characters for fast searching. We use a + * shell sort since this list could be large. + */ + cshell( ccltbl + cclmap[$1], ccllen[$1], true ); + + if ( useecs ) + mkeccl( ccltbl + cclmap[$1], ccllen[$1], + nextecm, ecgroup, csize, csize ); + + ++rulelen; + + $$ = mkstate( -$1 ); + } + + | PREVCCL + { + ++rulelen; + + $$ = mkstate( -$1 ); + } + + | '"' string '"' + { $$ = $2; } + + | '(' re ')' + { $$ = $2; } + + | CHAR + { + ++rulelen; + + if ( caseins && $1 >= 'A' && $1 <= 'Z' ) + $1 = clower( $1 ); + + $$ = mkstate( $1 ); + } + ; + +fullccl : '[' ccl ']' + { $$ = $2; } + + | '[' '^' ccl ']' + { + /* *Sigh* - to be compatible Unix lex, negated ccls + * match newlines + */ +#ifdef NOTDEF + ccladd( $3, '\n' ); /* negated ccls don't match '\n' */ + cclsorted = false; /* because we added the newline */ +#endif + cclnegate( $3 ); + $$ = $3; + } + ; + +ccl : ccl CHAR '-' CHAR + { + if ( $2 > $4 ) + synerr( "negative range in character class" ); + + else + { + if ( caseins ) + { + if ( $2 >= 'A' && $2 <= 'Z' ) + $2 = clower( $2 ); + if ( $4 >= 'A' && $4 <= 'Z' ) + $4 = clower( $4 ); + } + + for ( i = $2; i <= $4; ++i ) + ccladd( $1, i ); + + /* keep track if this ccl is staying in alphabetical + * order + */ + cclsorted = cclsorted && ($2 > lastchar); + lastchar = $4; + } + + $$ = $1; + } + + | ccl CHAR + { + if ( caseins ) + if ( $2 >= 'A' && $2 <= 'Z' ) + $2 = clower( $2 ); + + ccladd( $1, $2 ); + cclsorted = cclsorted && ($2 > lastchar); + lastchar = $2; + $$ = $1; + } + + | + { + cclsorted = true; + lastchar = 0; + $$ = cclinit(); + } + ; + +string : string CHAR + { + if ( caseins ) + if ( $2 >= 'A' && $2 <= 'Z' ) + $2 = clower( $2 ); + + ++rulelen; + + $$ = link_machines( $1, mkstate( $2 ) ); + } + + | + { $$ = mkstate( SYM_EPSILON ); } + ; + +%% + + +/* build_eof_action - build the "<>" action for the active start + * conditions + */ + +void build_eof_action() + + { + int i; + + for ( i = 1; i <= actvp; ++i ) + { + if ( sceof[actvsc[i]] ) + format_pinpoint_message( + "multiple <> rules for start condition %s", + scname[actvsc[i]] ); + + else + { + sceof[actvsc[i]] = true; + fprintf( temp_action_file, "case YY_STATE_EOF(%s):\n", + scname[actvsc[i]] ); + } + } + + line_directive_out( temp_action_file ); + } + + +/* synerr - report a syntax error */ + +void synerr( str ) +char str[]; + + { + syntaxerror = true; + pinpoint_message( str ); + } + + +/* format_pinpoint_message - write out a message formatted with one string, + * pinpointing its location + */ + +void format_pinpoint_message( msg, arg ) +char msg[], arg[]; + + { + char errmsg[MAXLINE]; + + (void) sprintf( errmsg, msg, arg ); + pinpoint_message( errmsg ); + } + + +/* pinpoint_message - write out a message, pinpointing its location */ + +void pinpoint_message( str ) +char str[]; + + { + fprintf( stderr, "\"%s\", line %d: %s\n", infilename, linenum, str ); + } + + +/* yyerror - eat up an error message from the parser; + * currently, messages are ignore + */ + +void yyerror( msg ) +char msg[]; + + { + } + +#include "scan.c" +#include "yylex.c" +#include "main.c" + diff --git a/src/toolsComm/flex/scan.c b/src/toolsComm/flex/scan.c new file mode 100644 index 000000000..b50bdbf78 --- /dev/null +++ b/src/toolsComm/flex/scan.c @@ -0,0 +1,2330 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton version: + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ + +#define FLEX_SCANNER + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +#else /* ! __cplusplus */ +#ifdef __GNUC__ +#include +void *malloc( size_t ); +void free( void* ); +#else +#include +#endif /* __GNUC__ */ + +#endif /* ! __cplusplus */ + + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#define yyterminate() return ( YY_NULL ) + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters + */ +#define YY_DECL int yylex ( void ) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +#define YY_CHAR unsigned char +# line 1 "scan.l" +#define INITIAL 0 +/* scan.l - scanner for flex input */ +# line 5 "scan.l" +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#undef yywrap + +#define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) +#define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); + +#undef YY_DECL +#define YY_DECL \ + int flexscan() + +#define RETURNCHAR \ + yylval = yytext[0]; \ + return ( CHAR ); + +#define RETURNNAME \ + (void) strcpy( nmstr, (char *) yytext ); \ + return ( NAME ); + +#define PUT_BACK_STRING(str, start) \ + for ( i = strlen( (char *) (str) ) - 1; i >= start; --i ) \ + unput((str)[i]) + +#define CHECK_REJECT(str) \ + if ( all_upper( str ) ) \ + reject = true; + +#define CHECK_YYMORE(str) \ + if ( all_lower( str ) ) \ + yymore_used = true; +#define SECT2 1 +#define SECT2PROLOG 2 +#define SECT3 3 +#define CODEBLOCK 4 +#define PICKUPDEF 5 +#define SC 6 +#define CARETISBOL 7 +#define NUM 8 +#define QUOTE 9 +#define FIRSTCCL 10 +#define CCL 11 +#define ACTION 12 +#define RECOVER 13 +#define BRACEERROR 14 +#define C_COMMENT 15 +#define ACTION_COMMENT 16 +#define ACTION_STRING 17 +#define PERCENT_BRACE_ACTION 18 +#define USED_LIST 19 +#define CODEBLOCK_2 20 +#define XLATION 21 +# line 76 "scan.l" + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ + yyleng = yy_cp - yy_bp; \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters*/ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; + +YY_CHAR *yytext; +int yyleng; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +#define YY_END_OF_BUFFER 121 +typedef int yy_state_type; +static const short int yy_acclist[482] = + { 0, + 16444,16444, 119, 119, 121, 19, 120, 7, 19, 120, + 18, 120, 19, 120, 19, 120, 16, 19, 120, 1, + 7, 19, 120, 17, 18, 120, 19, 120, 19, 120, + 19, 120, 19, 120, 15, 16, 19, 120, 67, 120, + 59, 67, 120,16444, 8252, 68, 120, 67, 120, 53, + 67, 120, 67, 120, 66, 67, 120, 51, 67, 120, + 67, 120, 67, 120, 67, 120, 50, 59, 67, 120, + 16444, 49, 8252, 61, 68, 120, 67, 120, 67, 120, + 52, 67, 120, 120, 47, 120, 120, 119, 120, 119, + 120, 119, 120, 28, 120, 29, 120, 28, 120, 28, + + 120, 28, 120, 28, 120, 28, 120, 31, 120, 30, + 120, 32, 120, 120, 73, 120, 120, 69, 73, 120, + 70, 73, 120, 72, 73, 120, 74, 120, 88, 120, + 89, 120, 88, 120, 86, 88, 120, 85, 88, 120, + 87, 88, 120, 75, 120, 77, 120, 120, 76, 120, + 75, 120, 81, 120, 80, 81, 120, 81, 120, 81, + 120, 83, 120, 83, 120, 83, 120, 84, 120, 99, + 105, 120, 104, 120, 105, 120, 103, 105, 120, 105, + 120, 105, 120, 100, 105, 120, 100, 105, 120, 100, + 105, 120, 97, 105, 120, 98, 105, 120, 120, 33, + + 120, 120, 91, 120, 120, 90, 120, 22, 120, 24, + 120, 120, 23, 120, 107, 110, 120, 109, 120, 110, + 120, 108, 110, 120, 111, 115, 120, 113, 120, 115, + 120, 114, 115, 120, 115, 120, 95, 120, 95, 120, + 96, 120, 95, 120, 95, 120, 95, 120, 95, 120, + 95, 120, 38, 120, 35, 120, 34, 120, 120, 38, + 120, 38, 120, 44, 120, 42, 44, 120, 45, 120, + 44, 120, 44, 120, 44, 120, 41, 44, 120, 41, + 42, 44, 120, 41, 44, 120, 41, 44, 120, 40, + 41, 44, 120, 41, 44, 120, 7, 18, 16, 1, + + 7, 17, 18, 2, 14, 8, 14, 12, 4, 5, + 3, 15, 16, 59,16444, 8252, 8252, 68, 56, 117, + 117, 117, 55, 54, 55, 50, 59,16444, 49, 8252, + 61, 49, 8252, 61, 68, 63, 50, 47, 46, 119, + 119, 119, 28, 29, 28, 28, 28, 28, 31, 30, + 32, 71, 72, 89, 85, 77, 118, 118, 118, 78, + 79, 82, 99, 104, 102, 101, 100, 100, 100, 33, + 91, 22, 24, 20, 107, 109, 106, 111, 113, 112, + 95, 95, 95, 96, 92, 95, 95, 95, 95, 38, + 35, 34, 38, 38, 42, 45, 43, 43, 43, 42, + + 40, 13, 14, 8, 8, 14, 12, 4, 5, 6, + 57, 58, 64, 117, 117, 55, 55, 65, 63, 28, + 28, 28, 25, 118, 118, 100, 100, 21, 92, 95, + 92, 95, 95, 38, 38, 39, 43, 43, 11, 4, + 11, 13, 5, 117, 28, 28, 118, 100, 100, 95, + 95, 38, 38, 43, 9, 28, 28, 100, 100, 95, + 95, 38, 38, 26, 28, 27, 28, 93, 100, 94, + 100, 93, 95, 94, 95, 36, 38, 37, 38, 10, + 62 + } ; + +static const short int yy_accept[392] = + { 0, + 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 6, 8, 11, 13, 15, + 17, 20, 24, 27, 29, 31, 33, 35, 39, 41, + 45, 48, 50, 53, 55, 58, 61, 63, 65, 67, + 72, 77, 79, 81, 84, 85, 87, 88, 90, 92, + 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, + 114, 115, 117, 118, 121, 124, 127, 129, 131, 133, + + 135, 138, 141, 144, 146, 148, 149, 151, 153, 155, + 158, 160, 162, 164, 166, 168, 170, 173, 175, 177, + 180, 182, 184, 187, 190, 193, 196, 199, 200, 202, + 203, 205, 206, 208, 210, 212, 213, 215, 218, 220, + 222, 225, 228, 230, 232, 235, 237, 239, 241, 243, + 245, 247, 249, 251, 253, 255, 257, 259, 260, 262, + 264, 266, 269, 271, 273, 275, 277, 280, 284, 287, + 290, 294, 297, 298, 299, 299, 299, 300, 302, 304, + 304, 304, 305, 305, 306, 308, 308, 309, 310, 310, + 310, 311, 311, 312, 314, 316, 317, 317, 317, 317, + + 319, 320, 320, 320, 320, 321, 322, 323, 324, 325, + 326, 329, 332, 332, 336, 337, 338, 338, 339, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 365, 366, 366, 367, + 368, 369, 370, 370, 371, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, + 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, + 397, 397, 398, 399, 400, 401, 402, 402, 403, 403, + 404, 405, 407, 407, 408, 409, 409, 409, 409, 410, + + 410, 411, 411, 412, 412, 413, 413, 413, 414, 414, + 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, + 424, 424, 424, 425, 426, 427, 428, 428, 429, 429, + 431, 432, 433, 434, 435, 436, 436, 437, 437, 438, + 439, 439, 440, 440, 441, 441, 443, 443, 443, 443, + 444, 444, 444, 444, 445, 446, 447, 448, 449, 450, + 451, 452, 453, 454, 455, 455, 456, 456, 456, 456, + 457, 458, 459, 460, 461, 462, 463, 464, 464, 464, + 466, 468, 470, 472, 474, 476, 478, 480, 481, 482, + 482 + + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 4, 5, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 6, 1, 7, 8, 9, 10, 1, 11, 12, + 12, 13, 12, 14, 15, 12, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 1, 1, 18, + 1, 19, 12, 1, 25, 26, 27, 28, 29, 30, + 24, 24, 24, 31, 32, 24, 33, 34, 35, 32, + 24, 36, 37, 38, 39, 24, 24, 40, 41, 24, + 20, 21, 22, 23, 24, 1, 25, 26, 27, 28, + + 29, 30, 24, 24, 24, 31, 32, 24, 33, 34, + 35, 32, 24, 36, 37, 38, 39, 24, 24, 40, + 41, 24, 42, 43, 44, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[45] = + { 0, + 1, 2, 3, 2, 4, 2, 5, 1, 1, 1, + 6, 1, 7, 1, 8, 6, 9, 1, 1, 1, + 10, 11, 1, 12, 13, 13, 13, 13, 13, 13, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 6, 1, 14 + } ; + +static const short int yy_base[454] = + { 0, + 0, 44, 87, 129, 92, 99, 106, 107, 172, 1520, + 111, 116, 216, 0, 1506, 1464, 123, 258, 141, 148, + 259, 262, 266, 286, 308, 0, 120, 151, 261, 350, + 155, 265, 281, 287, 351, 354, 394, 0, 437, 477, + 0, 0, 517, 537, 1475, 1567, 293, 1567, 1471, 1394, + 0, 360, 1567, 1416, 154, 549, 1405, 0, 1567, 590, + 1567, 1402, 1567, 365, 1567, 1386, 1382, 84, 633, 676, + 1567, 1396, 350, 1567, 158, 0, 161, 301, 1567, 371, + 0, 1567, 1395, 0, 1365, 1352, 1341, 0, 375, 1567, + 1381, 1567, 1567, 1567, 1347, 0, 1567, 1567, 1567, 1360, + + 1567, 1341, 1567, 1567, 1567, 1354, 1567, 427, 1567, 1567, + 428, 1341, 1567, 0, 429, 1567, 0, 1567, 1343, 1567, + 282, 1330, 0, 1313, 1291, 1567, 1567, 375, 1567, 379, + 1567, 1326, 1567, 0, 1567, 1325, 1293, 0, 1567, 1305, + 1279, 0, 1567, 1291, 1567, 0, 0, 381, 1567, 1283, + 1241, 0, 1255, 1242, 0, 384, 1567, 1274, 1241, 1228, + 1567, 445, 1567, 1253, 1217, 431, 1567, 448, 1239, 1203, + 1219, 436, 453, 1567, 1232, 458, 0, 482, 1567, 1218, + 467, 1567, 472, 487, 488, 1210, 493, 0, 498, 239, + 0, 493, 1567, 0, 0, 1567, 1212, 1169, 502, 1567, + + 1567, 1181, 487, 489, 1567, 1185, 0, 0, 1567, 705, + 0, 1567, 1198, 1567, 0, 1567, 507, 0, 511, 1567, + 512, 1567, 521, 0, 1567, 0, 1143, 1140, 749, 0, + 526, 1567, 1567, 0, 1567, 1155, 1567, 1567, 1129, 0, + 1567, 1567, 1567, 0, 1567, 514, 1567, 1140, 1567, 0, + 1108, 1099, 528, 1567, 531, 1567, 0, 1567, 541, 0, + 1567, 1567, 0, 1567, 1567, 0, 546, 1087, 1567, 793, + 0, 1095, 1092, 0, 547, 1567, 1062, 1059, 558, 1567, + 563, 1567, 842, 0, 573, 841, 599, 1567, 854, 604, + 0, 605, 552, 610, 0, 615, 817, 826, 0, 558, + + 1567, 567, 1567, 568, 1567, 577, 819, 1567, 605, 821, + 836, 0, 0, 0, 1567, 0, 816, 809, 0, 1567, + 594, 610, 824, 0, 811, 804, 620, 1567, 625, 0, + 0, 767, 713, 718, 693, 685, 1567, 724, 709, 0, + 626, 1567, 633, 0, 690, 1567, 680, 688, 696, 0, + 693, 680, 835, 1567, 694, 682, 1567, 690, 664, 657, + 596, 597, 562, 1567, 516, 1567, 518, 677, 682, 436, + 437, 241, 238, 132, 136, 105, 98, 78, 79, 0, + 0, 0, 0, 0, 0, 0, 0, 1567, 1567, 1567, + 865, 879, 893, 907, 921, 935, 949, 963, 977, 991, + + 1005, 1019, 1033, 1047, 1061, 1075, 1082, 1095, 1109, 1115, + 1128, 1142, 1156, 1170, 1184, 1198, 1205, 1218, 1225, 1238, + 1252, 1266, 1280, 1291, 1298, 1311, 1325, 1339, 1353, 1367, + 1381, 1388, 1401, 1415, 1429, 693, 695, 1443, 1457, 360, + 1471, 1484, 380, 1498, 700, 1512, 1519, 1525, 701, 1538, + 702, 1552, 703 + } ; + +static const short int yy_def[454] = + { 0, + 390, 390, 391, 391, 392, 392, 393, 393, 390, 9, + 394, 394, 390, 13, 395, 395, 396, 396, 397, 397, + 398, 398, 399, 399, 390, 25, 400, 400, 395, 395, + 401, 401, 402, 402, 403, 403, 390, 37, 404, 404, + 37, 37, 405, 406, 390, 390, 390, 390, 390, 390, + 407, 390, 390, 390, 408, 409, 390, 410, 390, 390, + 390, 390, 390, 390, 390, 390, 411, 412, 390, 390, + 390, 390, 390, 390, 413, 414, 413, 415, 390, 415, + 416, 390, 390, 417, 417, 417, 416, 418, 390, 390, + 390, 390, 390, 390, 390, 419, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 412, 390, 390, + 420, 421, 390, 422, 412, 390, 423, 390, 390, 390, + 424, 390, 425, 425, 425, 390, 390, 426, 390, 426, + 390, 390, 390, 427, 390, 390, 390, 428, 390, 390, + 390, 429, 390, 390, 390, 430, 431, 431, 390, 390, + 431, 432, 432, 432, 433, 390, 390, 390, 433, 433, + 390, 390, 390, 390, 390, 434, 390, 390, 390, 390, + 390, 434, 390, 390, 390, 390, 407, 390, 390, 390, + 408, 390, 408, 390, 435, 390, 390, 436, 390, 390, + 437, 438, 390, 410, 60, 390, 390, 390, 439, 390, + + 390, 390, 411, 411, 390, 390, 440, 441, 390, 441, + 70, 390, 390, 390, 442, 390, 413, 414, 413, 390, + 415, 390, 415, 416, 390, 417, 417, 417, 390, 418, + 390, 390, 390, 419, 390, 390, 390, 390, 390, 443, + 390, 390, 390, 423, 390, 424, 390, 424, 390, 425, + 425, 425, 426, 390, 426, 390, 427, 390, 444, 428, + 390, 390, 429, 390, 390, 431, 431, 431, 390, 390, + 432, 432, 432, 433, 390, 390, 433, 433, 390, 390, + 390, 390, 390, 445, 390, 390, 390, 390, 390, 390, + 435, 435, 446, 390, 447, 446, 390, 390, 448, 438, + + 390, 438, 390, 439, 390, 439, 390, 390, 411, 411, + 390, 449, 441, 210, 390, 442, 417, 417, 229, 390, + 450, 450, 390, 451, 425, 425, 444, 390, 444, 270, + 452, 432, 432, 433, 433, 390, 390, 390, 390, 453, + 446, 390, 446, 447, 446, 390, 446, 390, 390, 448, + 390, 411, 310, 390, 417, 417, 390, 425, 425, 432, + 432, 433, 433, 390, 390, 390, 390, 411, 411, 417, + 417, 425, 425, 432, 432, 433, 433, 390, 390, 417, + 417, 425, 425, 432, 432, 433, 433, 390, 390, 0, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390 + } ; + +static const short int yy_nxt[1612] = + { 0, + 46, 47, 48, 47, 49, 47, 46, 46, 46, 50, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 46, 46, 46, 46, 52, 53, 52, 54, 52, + 46, 55, 46, 56, 46, 46, 46, 46, 46, 57, + 46, 46, 46, 46, 46, 46, 46, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 58, 58, 46, 46, 46, 60, 61, + 60, 62, 60, 63, 76, 64, 77, 389, 65, 65, + + 206, 76, 65, 77, 66, 388, 67, 68, 79, 79, + 80, 80, 89, 90, 89, 91, 89, 89, 90, 89, + 91, 89, 129, 207, 130, 99, 387, 100, 69, 65, + 70, 71, 70, 72, 70, 63, 101, 64, 73, 102, + 65, 65, 386, 105, 65, 106, 66, 107, 67, 68, + 105, 74, 106, 129, 107, 130, 182, 135, 183, 136, + 218, 108, 219, 218, 385, 219, 103, 137, 108, 384, + 69, 65, 81, 81, 82, 81, 83, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 84, 84, 84, 84, 84, + + 84, 84, 84, 84, 84, 84, 84, 85, 84, 84, + 84, 84, 86, 81, 81, 81, 92, 92, 93, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 94, + 92, 92, 92, 92, 95, 92, 92, 92, 92, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 92, 92, 92, + 99, 93, 100, 131, 93, 132, 383, 135, 93, 136, + 93, 101, 297, 110, 102, 298, 110, 137, 382, 111, + 114, 112, 111, 139, 112, 140, 115, 116, 93, 139, + 93, 140, 247, 141, 173, 174, 173, 175, 173, 141, + + 114, 103, 248, 222, 133, 223, 115, 116, 117, 117, + 118, 117, 119, 117, 120, 117, 117, 117, 121, 117, + 117, 117, 117, 122, 117, 117, 117, 117, 117, 117, + 117, 123, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 123, 123, 124, 123, 123, 123, 123, 125, 126, + 117, 127, 131, 143, 132, 144, 143, 145, 144, 215, + 145, 178, 179, 178, 180, 178, 201, 201, 312, 201, + 201, 146, 312, 222, 146, 223, 231, 254, 231, 255, + 231, 254, 267, 255, 267, 275, 267, 275, 324, 275, + 268, 216, 324, 133, 147, 148, 149, 148, 150, 148, + + 147, 147, 147, 151, 147, 147, 147, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 147, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 153, + 152, 152, 152, 152, 154, 147, 147, 147, 156, 157, + 156, 158, 156, 206, 239, 206, 279, 283, 279, 285, + 279, 285, 283, 285, 173, 174, 173, 175, 173, 287, + 288, 287, 289, 287, 286, 381, 207, 240, 207, 182, + 284, 183, 159, 380, 182, 284, 183, 160, 156, 157, + 156, 158, 156, 178, 179, 178, 180, 178, 290, 292, + 290, 292, 290, 292, 294, 301, 294, 302, 294, 287, + + 288, 287, 289, 296, 305, 309, 306, 204, 308, 218, + 203, 219, 159, 218, 222, 219, 223, 160, 162, 163, + 162, 164, 162, 222, 247, 223, 165, 231, 310, 231, + 254, 231, 255, 254, 248, 255, 379, 166, 168, 163, + 168, 169, 168, 328, 378, 329, 170, 267, 275, 267, + 275, 267, 275, 171, 342, 268, 343, 172, 185, 279, + 301, 279, 302, 279, 336, 337, 336, 338, 336, 301, + 305, 302, 306, 186, 285, 187, 285, 186, 285, 305, + 186, 306, 186, 186, 187, 188, 189, 190, 191, 286, + 192, 195, 196, 195, 197, 195, 320, 377, 321, 198, + + 287, 288, 287, 289, 287, 290, 292, 290, 292, 290, + 292, 294, 320, 294, 321, 294, 345, 346, 345, 347, + 345, 352, 328, 376, 329, 204, 308, 328, 342, 329, + 343, 375, 199, 208, 208, 342, 208, 343, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, 208, 209, + 208, 208, 208, 208, 208, 208, 210, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 210, 210, 208, 208, 208, 211, 212, 211, + 213, 211, 346, 374, 343, 198, 336, 337, 336, 338, + 336, 345, 346, 345, 347, 345, 368, 204, 308, 373, + + 204, 308, 204, 308, 295, 295, 299, 299, 340, 354, + 357, 364, 340, 354, 357, 364, 372, 371, 199, 314, + 370, 314, 367, 366, 365, 282, 337, 363, 314, 314, + 314, 314, 314, 314, 314, 314, 314, 314, 314, 314, + 314, 314, 314, 314, 314, 314, 362, 361, 315, 319, + 319, 320, 319, 321, 319, 319, 319, 319, 319, 319, + 319, 319, 319, 319, 319, 319, 319, 319, 319, 319, + 319, 319, 322, 322, 322, 322, 322, 322, 322, 322, + 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + 319, 319, 319, 330, 330, 360, 330, 331, 330, 330, + + 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, + 330, 330, 330, 330, 330, 330, 331, 331, 331, 331, + 331, 331, 331, 331, 331, 331, 331, 331, 331, 331, + 331, 331, 331, 331, 330, 330, 330, 353, 359, 358, + 238, 204, 308, 356, 355, 353, 353, 353, 353, 353, + 353, 369, 205, 351, 349, 348, 288, 286, 339, 369, + 369, 369, 369, 369, 369, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 75, + 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 78, 78, 78, 78, 78, 78, 78, + + 78, 78, 78, 78, 78, 78, 78, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 93, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 104, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 109, 109, 109, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 113, 113, 113, + 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, + 113, 128, 128, 128, 128, 128, 128, 128, 128, 128, + + 128, 128, 128, 128, 128, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 134, 134, 134, 134, 138, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 155, 155, 155, + 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, + 155, 161, 161, 161, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 167, 167, 167, 167, 167, + 167, 167, 167, 167, 167, 167, 167, 167, 167, 177, + 177, 335, 334, 177, 177, 181, 181, 181, 181, 181, + + 181, 181, 181, 181, 181, 181, 181, 181, 181, 184, + 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, + 184, 184, 194, 194, 333, 332, 194, 194, 203, 203, + 270, 326, 203, 203, 203, 203, 203, 203, 325, 203, + 203, 203, 205, 205, 246, 323, 205, 205, 205, 205, + 205, 205, 205, 205, 205, 205, 217, 217, 217, 217, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, + 220, 236, 318, 317, 220, 220, 220, 220, 220, 220, + 220, 220, 220, 220, 221, 221, 221, 221, 221, 221, + 221, 221, 221, 221, 221, 221, 221, 221, 224, 224, + + 212, 311, 224, 224, 224, 224, 224, 224, 224, 307, + 303, 224, 226, 226, 196, 293, 226, 226, 230, 230, + 179, 230, 230, 230, 230, 230, 230, 230, 230, 230, + 230, 230, 234, 234, 174, 286, 234, 234, 238, 238, + 281, 280, 238, 238, 238, 238, 238, 238, 238, 238, + 238, 238, 241, 241, 281, 280, 241, 241, 241, 241, + 241, 241, 241, 241, 241, 241, 243, 243, 278, 277, + 243, 243, 243, 243, 243, 243, 276, 243, 243, 243, + 244, 244, 273, 272, 270, 269, 244, 244, 244, 244, + 244, 246, 246, 264, 262, 246, 246, 246, 246, 246, + + 246, 246, 246, 246, 246, 250, 250, 261, 259, 250, + 250, 253, 253, 253, 253, 253, 253, 253, 253, 253, + 253, 253, 253, 253, 253, 257, 257, 258, 256, 257, + 257, 252, 257, 257, 257, 257, 257, 257, 257, 260, + 260, 251, 249, 260, 260, 245, 260, 260, 260, 260, + 260, 260, 260, 263, 263, 242, 237, 236, 263, 263, + 263, 263, 235, 263, 263, 263, 263, 265, 265, 233, + 265, 265, 265, 265, 265, 265, 265, 265, 265, 265, + 265, 266, 266, 232, 229, 266, 266, 266, 266, 266, + 266, 266, 228, 227, 266, 271, 271, 225, 214, 271, + + 271, 274, 204, 202, 200, 274, 274, 274, 274, 274, + 274, 274, 274, 274, 274, 282, 282, 193, 179, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 291, + 291, 176, 291, 291, 291, 291, 291, 291, 291, 291, + 291, 291, 291, 300, 300, 300, 300, 300, 300, 300, + 300, 300, 300, 300, 300, 300, 300, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 313, 313, 174, 390, 313, 313, 313, 313, 313, + 313, 313, 313, 313, 316, 316, 97, 316, 316, 316, + 316, 316, 316, 316, 316, 316, 316, 316, 327, 327, + + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 341, 341, 344, 344, 97, 87, + 344, 344, 350, 350, 390, 390, 350, 350, 322, 322, + 322, 322, 322, 322, 322, 322, 322, 322, 322, 322, + 322, 322, 331, 331, 390, 331, 331, 331, 331, 331, + 331, 331, 331, 331, 331, 331, 45, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390 + } ; + +static const short int yy_chk[1612] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 5, 3, 5, 379, 3, 3, + + 68, 6, 3, 6, 3, 378, 3, 3, 7, 8, + 7, 8, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 27, 68, 27, 17, 377, 17, 3, 3, + 4, 4, 4, 4, 4, 4, 17, 4, 4, 17, + 4, 4, 376, 19, 4, 19, 4, 19, 4, 4, + 20, 4, 20, 28, 20, 28, 55, 31, 55, 31, + 75, 19, 75, 77, 375, 77, 17, 31, 20, 374, + 4, 4, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 18, 21, 18, 29, 22, 29, 373, 32, 23, 32, + 23, 18, 190, 21, 18, 190, 22, 32, 372, 21, + 23, 21, 22, 33, 22, 33, 23, 23, 24, 34, + 24, 34, 121, 33, 47, 47, 47, 47, 47, 34, + + 24, 18, 121, 78, 29, 78, 24, 24, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 30, 35, 30, 35, 36, 35, 36, 73, + 36, 52, 52, 52, 52, 52, 64, 64, 440, 64, + 64, 35, 440, 80, 36, 80, 89, 128, 89, 128, + 89, 130, 148, 130, 148, 156, 148, 156, 443, 156, + 148, 73, 443, 30, 37, 37, 37, 37, 37, 37, + + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 37, 37, 37, 37, 39, 39, + 39, 39, 39, 108, 111, 115, 162, 166, 162, 168, + 162, 168, 172, 168, 173, 173, 173, 173, 173, 176, + 176, 176, 176, 176, 168, 371, 108, 111, 115, 181, + 166, 181, 39, 370, 183, 172, 183, 39, 40, 40, + 40, 40, 40, 178, 178, 178, 178, 178, 184, 185, + 184, 185, 184, 185, 187, 192, 187, 192, 187, 189, + + 189, 189, 189, 189, 199, 204, 199, 203, 203, 217, + 204, 217, 40, 219, 221, 219, 221, 40, 43, 43, + 43, 43, 43, 223, 246, 223, 43, 231, 204, 231, + 253, 231, 253, 255, 246, 255, 367, 43, 44, 44, + 44, 44, 44, 259, 365, 259, 44, 267, 275, 267, + 275, 267, 275, 44, 293, 267, 293, 44, 56, 279, + 300, 279, 300, 279, 281, 281, 281, 281, 281, 302, + 304, 302, 304, 56, 285, 56, 285, 56, 285, 306, + 56, 306, 56, 56, 56, 56, 56, 56, 56, 285, + 56, 60, 60, 60, 60, 60, 321, 363, 321, 60, + + 287, 287, 287, 287, 287, 290, 292, 290, 292, 290, + 292, 294, 322, 294, 322, 294, 296, 296, 296, 296, + 296, 309, 327, 362, 327, 309, 309, 329, 341, 329, + 341, 361, 60, 69, 69, 343, 69, 343, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 70, 70, 70, + 70, 70, 347, 360, 347, 70, 336, 336, 336, 336, + 336, 345, 345, 345, 345, 345, 352, 368, 368, 359, + + 352, 352, 369, 369, 436, 436, 437, 437, 445, 449, + 451, 453, 445, 449, 451, 453, 358, 356, 70, 210, + 355, 210, 351, 349, 348, 339, 338, 335, 210, 210, + 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, + 210, 210, 210, 210, 210, 210, 334, 333, 210, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, + 229, 229, 229, 270, 270, 332, 270, 270, 270, 270, + + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 270, 310, 326, 325, + 323, 310, 310, 318, 317, 310, 310, 310, 310, 310, + 310, 353, 311, 307, 298, 297, 289, 286, 283, 353, + 353, 353, 353, 353, 353, 391, 391, 391, 391, 391, + 391, 391, 391, 391, 391, 391, 391, 391, 391, 392, + 392, 392, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392, 393, 393, 393, 393, 393, 393, 393, + + 393, 393, 393, 393, 393, 393, 393, 394, 394, 394, + 394, 394, 394, 394, 394, 394, 394, 394, 394, 394, + 394, 395, 395, 395, 395, 395, 395, 395, 395, 395, + 395, 395, 395, 395, 395, 396, 396, 396, 396, 396, + 396, 396, 396, 396, 396, 396, 396, 396, 396, 397, + 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, + 397, 397, 397, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 399, 399, 399, + 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, + 399, 400, 400, 400, 400, 400, 400, 400, 400, 400, + + 400, 400, 400, 400, 400, 401, 401, 401, 401, 401, + 401, 401, 401, 401, 401, 401, 401, 401, 401, 402, + 402, 402, 402, 402, 402, 402, 402, 402, 402, 402, + 402, 402, 402, 403, 403, 403, 403, 403, 403, 403, + 403, 403, 403, 403, 403, 403, 403, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 405, 405, 405, 405, 405, 405, 405, 405, 405, + 405, 405, 405, 405, 405, 406, 406, 406, 406, 406, + 406, 406, 406, 406, 406, 406, 406, 406, 406, 407, + 407, 278, 277, 407, 407, 408, 408, 408, 408, 408, + + 408, 408, 408, 408, 408, 408, 408, 408, 408, 409, + 409, 409, 409, 409, 409, 409, 409, 409, 409, 409, + 409, 409, 410, 410, 273, 272, 410, 410, 411, 411, + 268, 252, 411, 411, 411, 411, 411, 411, 251, 411, + 411, 411, 412, 412, 248, 239, 412, 412, 412, 412, + 412, 412, 412, 412, 412, 412, 413, 413, 413, 413, + 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, + 414, 236, 228, 227, 414, 414, 414, 414, 414, 414, + 414, 414, 414, 414, 415, 415, 415, 415, 415, 415, + 415, 415, 415, 415, 415, 415, 415, 415, 416, 416, + + 213, 206, 416, 416, 416, 416, 416, 416, 416, 202, + 198, 416, 417, 417, 197, 186, 417, 417, 418, 418, + 180, 418, 418, 418, 418, 418, 418, 418, 418, 418, + 418, 418, 419, 419, 175, 171, 419, 419, 420, 420, + 170, 169, 420, 420, 420, 420, 420, 420, 420, 420, + 420, 420, 421, 421, 165, 164, 421, 421, 421, 421, + 421, 421, 421, 421, 421, 421, 422, 422, 160, 159, + 422, 422, 422, 422, 422, 422, 158, 422, 422, 422, + 423, 423, 154, 153, 151, 150, 423, 423, 423, 423, + 423, 424, 424, 144, 141, 424, 424, 424, 424, 424, + + 424, 424, 424, 424, 424, 425, 425, 140, 137, 425, + 425, 426, 426, 426, 426, 426, 426, 426, 426, 426, + 426, 426, 426, 426, 426, 427, 427, 136, 132, 427, + 427, 125, 427, 427, 427, 427, 427, 427, 427, 428, + 428, 124, 122, 428, 428, 119, 428, 428, 428, 428, + 428, 428, 428, 429, 429, 112, 106, 102, 429, 429, + 429, 429, 100, 429, 429, 429, 429, 430, 430, 95, + 430, 430, 430, 430, 430, 430, 430, 430, 430, 430, + 430, 431, 431, 91, 87, 431, 431, 431, 431, 431, + 431, 431, 86, 85, 431, 432, 432, 83, 72, 432, + + 432, 433, 67, 66, 62, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 434, 434, 57, 54, 434, + 434, 434, 434, 434, 434, 434, 434, 434, 434, 435, + 435, 50, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 438, 438, 438, 438, 438, 438, 438, + 438, 438, 438, 438, 438, 438, 438, 439, 439, 439, + 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, + 439, 441, 441, 49, 45, 441, 441, 441, 441, 441, + 441, 441, 441, 441, 442, 442, 16, 442, 442, 442, + 442, 442, 442, 442, 442, 442, 442, 442, 444, 444, + + 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, + 444, 444, 446, 446, 446, 446, 446, 446, 446, 446, + 446, 446, 446, 446, 446, 446, 447, 447, 15, 10, + 447, 447, 448, 448, 0, 0, 448, 448, 450, 450, + 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, + 450, 450, 452, 452, 0, 452, 452, 452, 452, 452, + 452, 452, 452, 452, 452, 452, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + + 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, + 390 + } ; + +static yy_state_type yy_last_accepting_state; +static YY_CHAR *yy_last_accepting_cpos; + +static yy_state_type yy_state_buf[YY_BUF_SIZE + 2], *yy_state_ptr; +static YY_CHAR *yy_full_match; +static int yy_lp; +static int yy_looking_for_trail_begin = 0; +static int yy_full_lp; +static int *yy_full_state; +#define YY_TRAILING_MASK 0x2000 +#define YY_TRAILING_HEAD_MASK 0x4000 +#define REJECT \ +{ \ +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ \ +yy_cp = yy_full_match; /* restore poss. backed-over text */ \ +yy_lp = yy_full_lp; /* restore orig. accepting pos. */ \ +yy_state_ptr = yy_full_state; /* restore orig. state */ \ +yy_current_state = *yy_state_ptr; /* restore curr. state */ \ +++yy_lp; \ +goto find_rule; \ +} +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yyunput ( YY_CHAR c, YY_CHAR *buf_ptr ); +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +void yy_load_buffer_state ( void ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); + +#define yy_new_buffer yy_create_buffer + +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +YY_DECL + { + yy_state_type yy_current_state; + YY_CHAR *yy_cp, *yy_bp; + int yy_act; + + + static int bracelevel, didadef; + int i, indented_code, checking_used, new_xlation; + int doing_codeblock = false; + Char nmdef[MAXLINE], myesc(); + + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; + if ( yy_bp[-1] == '\n' ) + ++yy_current_state; + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[(int)*yy_cp]; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 391 ) + yy_c = yy_meta[(int)yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + *yy_state_ptr++ = yy_current_state; + ++yy_cp; + } + while ( yy_current_state != 390 ); + +yy_find_action: + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; +find_rule: /* we branch to this label when backtracking */ + for ( ; ; ) /* until we find what rule we matched */ + { + if ( yy_lp && yy_lp < yy_accept[yy_current_state + 1] ) + { + yy_act = yy_acclist[yy_lp]; + if ( yy_act & YY_TRAILING_HEAD_MASK || + yy_looking_for_trail_begin ) + { + if ( yy_act == yy_looking_for_trail_begin ) + { + yy_looking_for_trail_begin = 0; + yy_act &= ~YY_TRAILING_HEAD_MASK; + break; + } + } + else if ( yy_act & YY_TRAILING_MASK ) + { + yy_looking_for_trail_begin = yy_act & ~YY_TRAILING_MASK; + yy_looking_for_trail_begin |= YY_TRAILING_HEAD_MASK; + } + else + { + yy_full_match = yy_cp; + yy_full_state = yy_state_ptr; + yy_full_lp = yy_lp; + break; + } + ++yy_lp; + goto find_rule; + } + --yy_cp; + yy_current_state = *--yy_state_ptr; + yy_lp = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + + + switch ( yy_act ) + { +case 1: +# line 82 "scan.l" +indented_code = true; BEGIN(CODEBLOCK); + YY_BREAK +case 2: +# line 83 "scan.l" +++linenum; /* treat as a comment */ + YY_BREAK +case 3: +# line 84 "scan.l" +ECHO; BEGIN(C_COMMENT); + YY_BREAK +case 4: +# line 85 "scan.l" +return ( SCDECL ); + YY_BREAK +case 5: +# line 86 "scan.l" +return ( XSCDECL ); + YY_BREAK +case 6: +# line 87 "scan.l" +{ + ++linenum; + line_directive_out( stdout ); + indented_code = false; + BEGIN(CODEBLOCK); + } + YY_BREAK +case 7: +# line 94 "scan.l" +return ( WHITESPACE ); + YY_BREAK +case 8: +# line 96 "scan.l" +{ + sectnum = 2; + line_directive_out( stdout ); + BEGIN(SECT2PROLOG); + return ( SECTEND ); + } + YY_BREAK +case 9: +# line 103 "scan.l" +{ + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_USED; BEGIN(USED_LIST); + } + YY_BREAK +case 10: +# line 107 "scan.l" +{ + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + } + YY_BREAK +case 11: +# line 114 "scan.l" +{ +#ifdef NOTDEF + fprintf( stderr, + "old-style lex command at line %d ignored:\n\t%s", + linenum, yytext ); +#endif + ++linenum; + } + YY_BREAK +case 12: +# line 123 "scan.l" +/* ignore old lex directive */ + YY_BREAK +case 13: +# line 125 "scan.l" +{ + ++linenum; + xlation = + (int *) malloc( sizeof( int ) * (unsigned) csize ); + + if ( ! xlation ) + flexfatal( + "dynamic memory failure building %t table" ); + + for ( i = 0; i < csize; ++i ) + xlation[i] = 0; + + num_xlations = 0; + + BEGIN(XLATION); + } + YY_BREAK +case 14: +# line 142 "scan.l" +synerr( "unrecognized '%' directive" ); + YY_BREAK +case 15: +# line 144 "scan.l" +{ + (void) strcpy( nmstr, (char *) yytext ); + didadef = false; + BEGIN(PICKUPDEF); + } + YY_BREAK +case 16: +# line 150 "scan.l" +RETURNNAME; + YY_BREAK +case 17: +# line 151 "scan.l" +++linenum; /* allows blank lines in section 1 */ + YY_BREAK +case 18: +# line 152 "scan.l" +++linenum; return ( '\n' ); + YY_BREAK +case 19: +# line 153 "scan.l" +synerr( "illegal character" ); BEGIN(RECOVER); + YY_BREAK +case 20: +# line 156 "scan.l" +ECHO; BEGIN(INITIAL); + YY_BREAK +case 21: +# line 157 "scan.l" +++linenum; ECHO; BEGIN(INITIAL); + YY_BREAK +case 22: +# line 158 "scan.l" +ECHO; + YY_BREAK +case 23: +# line 159 "scan.l" +ECHO; + YY_BREAK +case 24: +# line 160 "scan.l" +++linenum; ECHO; + YY_BREAK +case 25: +# line 163 "scan.l" +++linenum; BEGIN(INITIAL); + YY_BREAK +case 26: +# line 164 "scan.l" +ECHO; CHECK_REJECT(yytext); + YY_BREAK +case 27: +# line 165 "scan.l" +ECHO; CHECK_YYMORE(yytext); + YY_BREAK +case 28: +# line 166 "scan.l" +ECHO; + YY_BREAK +case 29: +# line 167 "scan.l" +{ + ++linenum; + ECHO; + if ( indented_code ) + BEGIN(INITIAL); + } + YY_BREAK +case 30: +# line 175 "scan.l" +/* separates name and definition */ + YY_BREAK +case 31: +# line 177 "scan.l" +{ + (void) strcpy( (char *) nmdef, (char *) yytext ); + + for ( i = strlen( (char *) nmdef ) - 1; + i >= 0 && + (nmdef[i] == ' ' || nmdef[i] == '\t'); + --i ) + ; + + nmdef[i + 1] = '\0'; + + ndinstal( nmstr, nmdef ); + didadef = true; + } + YY_BREAK +case 32: +# line 192 "scan.l" +{ + if ( ! didadef ) + synerr( "incomplete name definition" ); + BEGIN(INITIAL); + ++linenum; + } + YY_BREAK +case 33: +# line 199 "scan.l" +++linenum; BEGIN(INITIAL); RETURNNAME; + YY_BREAK +case 34: +# line 202 "scan.l" +++linenum; BEGIN(INITIAL); + YY_BREAK +case 35: +# line 203 "scan.l" + + YY_BREAK +case 36: +# line 204 "scan.l" +{ + if ( all_upper( yytext ) ) + reject_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } + YY_BREAK +case 37: +# line 210 "scan.l" +{ + if ( all_lower( yytext ) ) + yymore_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } + YY_BREAK +case 38: +# line 216 "scan.l" +synerr( "unrecognized %used/%unused construct" ); + YY_BREAK +case 39: +# line 219 "scan.l" +++linenum; BEGIN(INITIAL); + YY_BREAK +case 40: +# line 220 "scan.l" +++num_xlations; new_xlation = true; + YY_BREAK +case 41: +# line 221 "scan.l" +synerr( "bad row in translation table" ); + YY_BREAK +case 42: +# line 222 "scan.l" +/* ignore whitespace */ + YY_BREAK +case 43: +# line 224 "scan.l" +{ + xlation[myesc( yytext )] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } + YY_BREAK +case 44: +# line 229 "scan.l" +{ + xlation[yytext[0]] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } + YY_BREAK +case 45: +# line 235 "scan.l" +++linenum; + YY_BREAK +case 46: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 238 "scan.l" +{ + ++linenum; + ACTION_ECHO; + MARK_END_OF_PROLOG; + BEGIN(SECT2); + } + YY_BREAK +case 47: +# line 245 "scan.l" +++linenum; ACTION_ECHO; + YY_BREAK +case YY_STATE_EOF(SECT2PROLOG): +# line 247 "scan.l" +MARK_END_OF_PROLOG; yyterminate(); + YY_BREAK +case 49: +# line 249 "scan.l" +++linenum; /* allow blank lines in section 2 */ + YY_BREAK +case 50: +# line 251 "scan.l" +{ + indented_code = (yytext[0] != '%'); + doing_codeblock = true; + bracelevel = 1; + + if ( indented_code ) + ACTION_ECHO; + + BEGIN(CODEBLOCK_2); + } + YY_BREAK +case 51: +# line 262 "scan.l" +BEGIN(SC); return ( '<' ); + YY_BREAK +case 52: +# line 263 "scan.l" +return ( '^' ); + YY_BREAK +case 53: +# line 264 "scan.l" +BEGIN(QUOTE); return ( '"' ); + YY_BREAK +case 54: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 265 "scan.l" +BEGIN(NUM); return ( '{' ); + YY_BREAK +case 55: +# line 266 "scan.l" +BEGIN(BRACEERROR); + YY_BREAK +case 56: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 267 "scan.l" +return ( '$' ); + YY_BREAK +case 57: +# line 269 "scan.l" +{ + bracelevel = 1; + BEGIN(PERCENT_BRACE_ACTION); + return ( '\n' ); + } + YY_BREAK +case 58: +# line 274 "scan.l" +continued_action = true; ++linenum; return ( '\n' ); + YY_BREAK +case 59: +# line 276 "scan.l" +{ + /* this rule is separate from the one below because + * otherwise we get variable trailing context, so + * we can't build the scanner using -{f,F} + */ + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + YY_BREAK +case 60: +# line 287 "scan.l" +{ + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + YY_BREAK +case 61: +# line 294 "scan.l" +++linenum; return ( '\n' ); + YY_BREAK +case 62: +# line 296 "scan.l" +return ( EOF_OP ); + YY_BREAK +case 63: +# line 298 "scan.l" +{ + sectnum = 3; + BEGIN(SECT3); + return ( EOF ); /* to stop the parser */ + } + YY_BREAK +case 64: +# line 304 "scan.l" +{ + int cclval; + + (void) strcpy( nmstr, (char *) yytext ); + + /* check to see if we've already encountered this ccl */ + if ( (cclval = ccllookup( (Char *) nmstr )) ) + { + yylval = cclval; + ++cclreuse; + return ( PREVCCL ); + } + else + { + /* we fudge a bit. We know that this ccl will + * soon be numbered as lastccl + 1 by cclinit + */ + cclinstal( (Char *) nmstr, lastccl + 1 ); + + /* push back everything but the leading bracket + * so the ccl can be rescanned + */ + PUT_BACK_STRING((Char *) nmstr, 1); + + BEGIN(FIRSTCCL); + return ( '[' ); + } + } + YY_BREAK +case 65: +# line 333 "scan.l" +{ + Char *nmdefptr; + Char *ndlookup(); + + (void) strcpy( nmstr, (char *) yytext ); + nmstr[yyleng - 1] = '\0'; /* chop trailing brace */ + + /* lookup from "nmstr + 1" to chop leading brace */ + if ( ! (nmdefptr = ndlookup( nmstr + 1 )) ) + synerr( "undefined {name}" ); + + else + { /* push back name surrounded by ()'s */ + unput(')'); + PUT_BACK_STRING(nmdefptr, 0); + unput('('); + } + } + YY_BREAK +case 66: +# line 352 "scan.l" +return ( yytext[0] ); + YY_BREAK +case 67: +# line 353 "scan.l" +RETURNCHAR; + YY_BREAK +case 68: +# line 354 "scan.l" +++linenum; return ( '\n' ); + YY_BREAK +case 69: +# line 357 "scan.l" +return ( ',' ); + YY_BREAK +case 70: +# line 358 "scan.l" +BEGIN(SECT2); return ( '>' ); + YY_BREAK +case 71: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 359 "scan.l" +BEGIN(CARETISBOL); return ( '>' ); + YY_BREAK +case 72: +# line 360 "scan.l" +RETURNNAME; + YY_BREAK +case 73: +# line 361 "scan.l" +synerr( "bad start condition name" ); + YY_BREAK +case 74: +# line 363 "scan.l" +BEGIN(SECT2); return ( '^' ); + YY_BREAK +case 75: +# line 366 "scan.l" +RETURNCHAR; + YY_BREAK +case 76: +# line 367 "scan.l" +BEGIN(SECT2); return ( '"' ); + YY_BREAK +case 77: +# line 369 "scan.l" +{ + synerr( "missing quote" ); + BEGIN(SECT2); + ++linenum; + return ( '"' ); + } + YY_BREAK +case 78: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 377 "scan.l" +BEGIN(CCL); return ( '^' ); + YY_BREAK +case 79: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 378 "scan.l" +return ( '^' ); + YY_BREAK +case 80: +# line 379 "scan.l" +BEGIN(CCL); yylval = '-'; return ( CHAR ); + YY_BREAK +case 81: +# line 380 "scan.l" +BEGIN(CCL); RETURNCHAR; + YY_BREAK +case 82: +*yy_cp = yy_hold_char; /* undo effects of setting up yytext */ +yy_c_buf_p = yy_cp = yy_bp + 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +# line 382 "scan.l" +return ( '-' ); + YY_BREAK +case 83: +# line 383 "scan.l" +RETURNCHAR; + YY_BREAK +case 84: +# line 384 "scan.l" +BEGIN(SECT2); return ( ']' ); + YY_BREAK +case 85: +# line 387 "scan.l" +{ + yylval = myctoi( yytext ); + return ( NUMBER ); + } + YY_BREAK +case 86: +# line 392 "scan.l" +return ( ',' ); + YY_BREAK +case 87: +# line 393 "scan.l" +BEGIN(SECT2); return ( '}' ); + YY_BREAK +case 88: +# line 395 "scan.l" +{ + synerr( "bad character inside {}'s" ); + BEGIN(SECT2); + return ( '}' ); + } + YY_BREAK +case 89: +# line 401 "scan.l" +{ + synerr( "missing }" ); + BEGIN(SECT2); + ++linenum; + return ( '}' ); + } + YY_BREAK +case 90: +# line 409 "scan.l" +synerr( "bad name in {}'s" ); BEGIN(SECT2); + YY_BREAK +case 91: +# line 410 "scan.l" +synerr( "missing }" ); ++linenum; BEGIN(SECT2); + YY_BREAK +case 92: +# line 413 "scan.l" +bracelevel = 0; + YY_BREAK +case 93: +# line 414 "scan.l" +{ + ACTION_ECHO; + CHECK_REJECT(yytext); + } + YY_BREAK +case 94: +# line 418 "scan.l" +{ + ACTION_ECHO; + CHECK_YYMORE(yytext); + } + YY_BREAK +case 95: +# line 422 "scan.l" +ACTION_ECHO; + YY_BREAK +case 96: +# line 423 "scan.l" +{ + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 || + (doing_codeblock && indented_code) ) + { + if ( ! doing_codeblock ) + fputs( "\tYY_BREAK\n", temp_action_file ); + + doing_codeblock = false; + BEGIN(SECT2); + } + } + YY_BREAK + /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */ +case 97: +# line 439 "scan.l" +ACTION_ECHO; ++bracelevel; + YY_BREAK +case 98: +# line 440 "scan.l" +ACTION_ECHO; --bracelevel; + YY_BREAK +case 99: +# line 441 "scan.l" +ACTION_ECHO; + YY_BREAK +case 100: +# line 442 "scan.l" +ACTION_ECHO; + YY_BREAK +case 101: +# line 443 "scan.l" +ACTION_ECHO; BEGIN(ACTION_COMMENT); + YY_BREAK +case 102: +# line 444 "scan.l" +ACTION_ECHO; /* character constant */ + YY_BREAK +case 103: +# line 445 "scan.l" +ACTION_ECHO; BEGIN(ACTION_STRING); + YY_BREAK +case 104: +# line 446 "scan.l" +{ + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 ) + { + fputs( "\tYY_BREAK\n", temp_action_file ); + BEGIN(SECT2); + } + } + YY_BREAK +case 105: +# line 455 "scan.l" +ACTION_ECHO; + YY_BREAK +case 106: +# line 457 "scan.l" +ACTION_ECHO; BEGIN(ACTION); + YY_BREAK +case 107: +# line 458 "scan.l" +ACTION_ECHO; + YY_BREAK +case 108: +# line 459 "scan.l" +ACTION_ECHO; + YY_BREAK +case 109: +# line 460 "scan.l" +++linenum; ACTION_ECHO; + YY_BREAK +case 110: +# line 461 "scan.l" +ACTION_ECHO; + YY_BREAK +case 111: +# line 463 "scan.l" +ACTION_ECHO; + YY_BREAK +case 112: +# line 464 "scan.l" +ACTION_ECHO; + YY_BREAK +case 113: +# line 465 "scan.l" +++linenum; ACTION_ECHO; + YY_BREAK +case 114: +# line 466 "scan.l" +ACTION_ECHO; BEGIN(ACTION); + YY_BREAK +case 115: +# line 467 "scan.l" +ACTION_ECHO; + YY_BREAK +case YY_STATE_EOF(ACTION): +case YY_STATE_EOF(ACTION_COMMENT): +case YY_STATE_EOF(ACTION_STRING): +# line 469 "scan.l" +{ + synerr( "EOF encountered inside an action" ); + yyterminate(); + } + YY_BREAK +case 117: +# line 475 "scan.l" +{ + yylval = myesc( yytext ); + return ( CHAR ); + } + YY_BREAK +case 118: +# line 480 "scan.l" +{ + yylval = myesc( yytext ); + BEGIN(CCL); + return ( CHAR ); + } + YY_BREAK +case 119: +# line 487 "scan.l" +ECHO; + YY_BREAK +case 120: +# line 488 "scan.l" +ECHO; + YY_BREAK + case YY_STATE_EOF(INITIAL): + case YY_STATE_EOF(SECT2): + case YY_STATE_EOF(SECT3): + case YY_STATE_EOF(CODEBLOCK): + case YY_STATE_EOF(PICKUPDEF): + case YY_STATE_EOF(SC): + case YY_STATE_EOF(CARETISBOL): + case YY_STATE_EOF(NUM): + case YY_STATE_EOF(QUOTE): + case YY_STATE_EOF(FIRSTCCL): + case YY_STATE_EOF(CCL): + case YY_STATE_EOF(RECOVER): + case YY_STATE_EOF(BRACEERROR): + case YY_STATE_EOF(C_COMMENT): + case YY_STATE_EOF(PERCENT_BRACE_ACTION): + case YY_STATE_EOF(USED_LIST): + case YY_STATE_EOF(CODEBLOCK_2): + case YY_STATE_EOF(XLATION): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + + { + YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state() + + { + yy_state_type yy_current_state; + YY_CHAR *yy_cp; + + YY_CHAR *yy_bp = yytext; + + yy_current_state = yy_start; + if ( yy_bp[-1] == '\n' ) + ++yy_current_state; + yy_state_ptr = yy_state_buf; + *yy_state_ptr++ = yy_current_state; + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[(int)*yy_cp] : 1); + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 391 ) + yy_c = yy_meta[(int)yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + *yy_state_ptr++ = yy_current_state; + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) + { + int yy_is_jam; + + YY_CHAR yy_c = 1; + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 391 ) + yy_c = yy_meta[(int)yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + *yy_state_ptr++ = yy_current_state; + yy_is_jam = (yy_current_state == 390); + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +static void yyunput( YY_CHAR c, YY_CHAR *yy_bp ) + { + YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef __cplusplus +static int yyinput() +#else +static int input(void) +#endif + + { + int c; + YY_CHAR *yy_cp = yy_c_buf_p; + + *yy_cp = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = yytext + YY_MORE_ADJ; + return ( EOF ); + } + + YY_NEW_FILE; + +#ifdef __cplusplus + return ( yyinput() ); +#else + return ( input() ); +#endif + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( "unexpected last match in input()" ); +#endif + } + } + } + + c = *yy_c_buf_p; + yy_hold_char = *++yy_c_buf_p; + + return ( c ); + } + + +void yyrestart( FILE *input_file ) + { + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +void yy_load_buffer_state( void ) + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +void yy_delete_buffer( YY_BUFFER_STATE b ) + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } +# line 488 "scan.l" + + + +int yywrap() + + { + if ( --num_input_files > 0 ) + { + set_input_file( *++input_files ); + return ( 0 ); + } + + else + return ( 1 ); + } + + +/* set_input_file - open the given file (if NULL, stdin) for scanning */ + +void set_input_file( file ) +char *file; + + { + if ( file ) + { + infilename = file; + yyin = fopen( infilename, "r" ); + + if ( yyin == NULL ) + lerrsf( "can't open %s", file ); + } + + else + { + yyin = stdin; + infilename = ""; + } + } diff --git a/src/toolsComm/flex/scan.l.DISTRIB b/src/toolsComm/flex/scan.l.DISTRIB new file mode 100644 index 000000000..a6075aa79 --- /dev/null +++ b/src/toolsComm/flex/scan.l.DISTRIB @@ -0,0 +1,525 @@ + +/* scan.l - scanner for flex input */ + +%{ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#undef yywrap + +#define ACTION_ECHO fprintf( temp_action_file, "%s", yytext ) +#define MARK_END_OF_PROLOG fprintf( temp_action_file, "%%%% end of prolog\n" ); + +#undef YY_DECL +#define YY_DECL \ + int flexscan() + +#define RETURNCHAR \ + yylval = yytext[0]; \ + return ( CHAR ); + +#define RETURNNAME \ + (void) strcpy( nmstr, (char *) yytext ); \ + return ( NAME ); + +#define PUT_BACK_STRING(str, start) \ + for ( i = strlen( (char *) (str) ) - 1; i >= start; --i ) \ + unput((str)[i]) + +#define CHECK_REJECT(str) \ + if ( all_upper( str ) ) \ + reject = true; + +#define CHECK_YYMORE(str) \ + if ( all_lower( str ) ) \ + yymore_used = true; +%} + +%x SECT2 SECT2PROLOG SECT3 CODEBLOCK PICKUPDEF SC CARETISBOL NUM QUOTE +%x FIRSTCCL CCL ACTION RECOVER BRACEERROR C_COMMENT ACTION_COMMENT +%x ACTION_STRING PERCENT_BRACE_ACTION USED_LIST CODEBLOCK_2 XLATION + +WS [ \t\f]+ +OPTWS [ \t\f]* +NOT_WS [^ \t\f\r\n] + +NAME [a-z_][a-z_0-9-]* +NOT_NAME [^a-z_\r\n]+ + +SCNAME {NAME} + +ESCSEQ \\([^\r\n]|[0-9]{1,3}|x[0-9a-f]{1,2}) + +%% + static int bracelevel, didadef; + int i, indented_code, checking_used, new_xlation; + int doing_codeblock = false; + Char nmdef[MAXLINE], myesc(); + +^{WS} indented_code = true; BEGIN(CODEBLOCK); +^#.*\r?\n ++linenum; /* treat as a comment */ +^"/*" ECHO; BEGIN(C_COMMENT); +^"%s"{NAME}? return ( SCDECL ); +^"%x"{NAME}? return ( XSCDECL ); +^"%{".*\r?\n { + ++linenum; + line_directive_out( stdout ); + indented_code = false; + BEGIN(CODEBLOCK); + } + +{WS} return ( WHITESPACE ); + +^"%%".* { + sectnum = 2; + line_directive_out( stdout ); + BEGIN(SECT2PROLOG); + return ( SECTEND ); + } + +^"%used" { + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_USED; BEGIN(USED_LIST); + } +^"%unused" { + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + pinpoint_message( "warning - %%used/%%unused have been deprecated" ); + checking_used = REALLY_NOT_USED; BEGIN(USED_LIST); + } + + +^"%"[aeknopt]" ".*\r?\n { +#ifdef NOTDEF + fprintf( stderr, + "old-style lex command at line %d ignored:\n\t%s", + linenum, yytext ); +#endif + ++linenum; + } + +^"%"[cr]{OPTWS} /* ignore old lex directive */ + +%t{OPTWS}\r?\n { + ++linenum; + xlation = + (int *) malloc( sizeof( int ) * (unsigned) csize ); + + if ( ! xlation ) + flexfatal( + "dynamic memory failure building %t table" ); + + for ( i = 0; i < csize; ++i ) + xlation[i] = 0; + + num_xlations = 0; + + BEGIN(XLATION); + } + +^"%"[^sxanpekotcru{}]{OPTWS} synerr( "unrecognized '%' directive" ); + +^{NAME} { + (void) strcpy( nmstr, (char *) yytext ); + didadef = false; + BEGIN(PICKUPDEF); + } + +{SCNAME} RETURNNAME; +^{OPTWS}\r?\n ++linenum; /* allows blank lines in section 1 */ +{OPTWS}\r?\n ++linenum; return ( '\n' ); +. synerr( "illegal character" ); BEGIN(RECOVER); + + +"*/" ECHO; BEGIN(INITIAL); +"*/".*\r?\n ++linenum; ECHO; BEGIN(INITIAL); +[^*\r\n]+ ECHO; +"*" ECHO; +\r?\n ++linenum; ECHO; + + +^"%}".*\r?\n ++linenum; BEGIN(INITIAL); +"reject" ECHO; CHECK_REJECT(yytext); +"yymore" ECHO; CHECK_YYMORE(yytext); +{NAME}|{NOT_NAME}|. ECHO; +\r?\n { + ++linenum; + ECHO; + if ( indented_code ) + BEGIN(INITIAL); + } + + +{WS} /* separates name and definition */ + +{NOT_WS}.* { + (void) strcpy( (char *) nmdef, (char *) yytext ); + + for ( i = strlen( (char *) nmdef ) - 1; + i >= 0 && + (nmdef[i] == ' ' || nmdef[i] == '\t'); + --i ) + ; + + nmdef[i + 1] = '\0'; + + ndinstal( nmstr, nmdef ); + didadef = true; + } + +\r?\n { + if ( ! didadef ) + synerr( "incomplete name definition" ); + BEGIN(INITIAL); + ++linenum; + } + +.*\r?\n ++linenum; BEGIN(INITIAL); RETURNNAME; + + +\r?\n ++linenum; BEGIN(INITIAL); +{WS} +"reject" { + if ( all_upper( yytext ) ) + reject_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } +"yymore" { + if ( all_lower( yytext ) ) + yymore_really_used = checking_used; + else + synerr( "unrecognized %used/%unused construct" ); + } +{NOT_WS}+ synerr( "unrecognized %used/%unused construct" ); + + +"%t"{OPTWS}\r?\n ++linenum; BEGIN(INITIAL); +^{OPTWS}[0-9]+ ++num_xlations; new_xlation = true; +^. synerr( "bad row in translation table" ); +{WS} /* ignore whitespace */ + +{ESCSEQ} { + xlation[myesc( yytext )] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } +. { + xlation[yytext[0]] = + (new_xlation ? num_xlations : -num_xlations); + new_xlation = false; + } + +\r?\n ++linenum; + + +.*\r?\n/{NOT_WS} { + ++linenum; + ACTION_ECHO; + MARK_END_OF_PROLOG; + BEGIN(SECT2); + } + +.*\r?\n ++linenum; ACTION_ECHO; + +<> MARK_END_OF_PROLOG; yyterminate(); + +^{OPTWS}\r?\n ++linenum; /* allow blank lines in section 2 */ + +^({WS}|"%{") { + indented_code = (yytext[0] != '%'); + doing_codeblock = true; + bracelevel = 1; + + if ( indented_code ) + ACTION_ECHO; + + BEGIN(CODEBLOCK_2); + } + +"<" BEGIN(SC); return ( '<' ); +^"^" return ( '^' ); +\" BEGIN(QUOTE); return ( '"' ); +"{"/[0-9] BEGIN(NUM); return ( '{' ); +"{"[^0-9\r\n][^}\r\n]* BEGIN(BRACEERROR); +"$"/[ \t\r\n] return ( '$' ); + +{WS}"%{" { + bracelevel = 1; + BEGIN(PERCENT_BRACE_ACTION); + return ( '\n' ); + } +{WS}"|".*\r?\n continued_action = true; ++linenum; return ( '\n' ); + +{WS} { + /* this rule is separate from the one below because + * otherwise we get variable trailing context, so + * we can't build the scanner using -{f,F} + */ + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + +{OPTWS}/\r?\n { + bracelevel = 0; + continued_action = false; + BEGIN(ACTION); + return ( '\n' ); + } + +^{OPTWS}\r?\n ++linenum; return ( '\n' ); + +"<>" return ( EOF_OP ); + +^"%%".* { + sectnum = 3; + BEGIN(SECT3); + return ( EOF ); /* to stop the parser */ + } + +"["([^\\\]\r\n]|{ESCSEQ})+"]" { + int cclval; + + (void) strcpy( nmstr, (char *) yytext ); + + /* check to see if we've already encountered this ccl */ + if ( (cclval = ccllookup( (Char *) nmstr )) ) + { + yylval = cclval; + ++cclreuse; + return ( PREVCCL ); + } + else + { + /* we fudge a bit. We know that this ccl will + * soon be numbered as lastccl + 1 by cclinit + */ + cclinstal( (Char *) nmstr, lastccl + 1 ); + + /* push back everything but the leading bracket + * so the ccl can be rescanned + */ + PUT_BACK_STRING((Char *) nmstr, 1); + + BEGIN(FIRSTCCL); + return ( '[' ); + } + } + +"{"{NAME}"}" { + register Char *nmdefptr; + Char *ndlookup(); + + (void) strcpy( nmstr, (char *) yytext ); + nmstr[yyleng - 1] = '\0'; /* chop trailing brace */ + + /* lookup from "nmstr + 1" to chop leading brace */ + if ( ! (nmdefptr = ndlookup( nmstr + 1 )) ) + synerr( "undefined {name}" ); + + else + { /* push back name surrounded by ()'s */ + unput(')'); + PUT_BACK_STRING(nmdefptr, 0); + unput('('); + } + } + +[/|*+?.()] return ( yytext[0] ); +. RETURNCHAR; +\r?\n ++linenum; return ( '\n' ); + + +"," return ( ',' ); +">" BEGIN(SECT2); return ( '>' ); +">"/"^" BEGIN(CARETISBOL); return ( '>' ); +{SCNAME} RETURNNAME; +. synerr( "bad start condition name" ); + +"^" BEGIN(SECT2); return ( '^' ); + + +[^"\r\n] RETURNCHAR; +\" BEGIN(SECT2); return ( '"' ); + +\r?\n { + synerr( "missing quote" ); + BEGIN(SECT2); + ++linenum; + return ( '"' ); + } + + +"^"/[^-\r\n] BEGIN(CCL); return ( '^' ); +"^"/- return ( '^' ); +- BEGIN(CCL); yylval = '-'; return ( CHAR ); +. BEGIN(CCL); RETURNCHAR; + +-/[^\]\r\n] return ( '-' ); +[^\]\r\n] RETURNCHAR; +"]" BEGIN(SECT2); return ( ']' ); + + +[0-9]+ { + yylval = myctoi( yytext ); + return ( NUMBER ); + } + +"," return ( ',' ); +"}" BEGIN(SECT2); return ( '}' ); + +. { + synerr( "bad character inside {}'s" ); + BEGIN(SECT2); + return ( '}' ); + } + +\r?\n { + synerr( "missing }" ); + BEGIN(SECT2); + ++linenum; + return ( '}' ); + } + + +"}" synerr( "bad name in {}'s" ); BEGIN(SECT2); +\r?\n synerr( "missing }" ); ++linenum; BEGIN(SECT2); + + +{OPTWS}"%}".* bracelevel = 0; +"reject" { + ACTION_ECHO; + CHECK_REJECT(yytext); + } +"yymore" { + ACTION_ECHO; + CHECK_YYMORE(yytext); + } +{NAME}|{NOT_NAME}|. ACTION_ECHO; +\r?\n { + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 || + (doing_codeblock && indented_code) ) + { + if ( ! doing_codeblock ) + fputs( "\tYY_BREAK\n", temp_action_file ); + + doing_codeblock = false; + BEGIN(SECT2); + } + } + + + /* Reject and YYmore() are checked for above, in PERCENT_BRACE_ACTION */ +"{" ACTION_ECHO; ++bracelevel; +"}" ACTION_ECHO; --bracelevel; +[^a-z_{}"'/\r\n]+ ACTION_ECHO; +{NAME} ACTION_ECHO; +"/*" ACTION_ECHO; BEGIN(ACTION_COMMENT); +"'"([^'\\\r\n]|\\.)*"'" ACTION_ECHO; /* character constant */ +\" ACTION_ECHO; BEGIN(ACTION_STRING); +\r?\n { + ++linenum; + ACTION_ECHO; + if ( bracelevel == 0 ) + { + fputs( "\tYY_BREAK\n", temp_action_file ); + BEGIN(SECT2); + } + } +. ACTION_ECHO; + +"*/" ACTION_ECHO; BEGIN(ACTION); +[^*\r\n]+ ACTION_ECHO; +"*" ACTION_ECHO; +\r?\n ++linenum; ACTION_ECHO; +. ACTION_ECHO; + +[^"\\\r\n]+ ACTION_ECHO; +\\. ACTION_ECHO; +\r?\n ++linenum; ACTION_ECHO; +\" ACTION_ECHO; BEGIN(ACTION); +. ACTION_ECHO; + +<> { + synerr( "EOF encountered inside an action" ); + yyterminate(); + } + + +{ESCSEQ} { + yylval = myesc( yytext ); + return ( CHAR ); + } + +{ESCSEQ} { + yylval = myesc( yytext ); + BEGIN(CCL); + return ( CHAR ); + } + + +.*(\r?\n?) ECHO; +%% + + +int yywrap() + + { + if ( --num_input_files > 0 ) + { + set_input_file( *++input_files ); + return ( 0 ); + } + + else + return ( 1 ); + } + + +/* set_input_file - open the given file (if NULL, stdin) for scanning */ + +void set_input_file( file ) +char *file; + + { + if ( file ) + { + infilename = file; + yyin = fopen( infilename, "r" ); + + if ( yyin == NULL ) + lerrsf( "can't open %s", file ); + } + + else + { + yyin = stdin; + infilename = ""; + } + } diff --git a/src/toolsComm/flex/sym.c b/src/toolsComm/flex/sym.c new file mode 100644 index 000000000..2e6803bd1 --- /dev/null +++ b/src/toolsComm/flex/sym.c @@ -0,0 +1,295 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* sym - symbol table routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + + +/* declare functions that have forward references */ + +int hashfunct (char[], int); + + +struct hash_entry *ndtbl[NAME_TABLE_HASH_SIZE]; +struct hash_entry *sctbl[START_COND_HASH_SIZE]; +struct hash_entry *ccltab[CCL_HASH_SIZE]; + +struct hash_entry *findsym(char *sym, struct hash_entry **table, int table_size); + + +/* addsym - add symbol and definitions to symbol table + * + * synopsis + * char sym[], *str_def; + * int int_def; + * hash_table table; + * int table_size; + * 0 / -1 = addsym( sym, def, int_def, table, table_size ); + * + * -1 is returned if the symbol already exists, and the change not made. + */ + +int addsym(char *sym, char *str_def, int int_def, struct hash_entry **table, int table_size) +{ + int hash_val = hashfunct( sym, table_size ); + struct hash_entry *sym_entry = table[hash_val]; + struct hash_entry *new_entry; + struct hash_entry *successor; + + while ( sym_entry ) + { + if ( ! strcmp( sym, sym_entry->name ) ) + { /* entry already exists */ + return ( -1 ); + } + + sym_entry = sym_entry->next; + } + + /* create new entry */ + new_entry = (struct hash_entry *) malloc( sizeof( struct hash_entry ) ); + + if ( new_entry == NULL ) + flexfatal( "symbol table memory allocation failed" ); + + if ( (successor = table[hash_val]) ) + { + new_entry->next = successor; + successor->prev = new_entry; + } + else + new_entry->next = NULL; + + new_entry->prev = NULL; + new_entry->name = sym; + new_entry->str_val = str_def; + new_entry->int_val = int_def; + + table[hash_val] = new_entry; + + return ( 0 ); + } + + +/* cclinstal - save the text of a character class + * + * synopsis + * Char ccltxt[]; + * int cclnum; + * cclinstal( ccltxt, cclnum ); + */ + +void cclinstal(Char *ccltxt, int cclnum) +{ + /* we don't bother checking the return status because we are not called + * unless the symbol is new + */ + Char *copy_unsigned_string(); + + (void) addsym( (char *) copy_unsigned_string( ccltxt ), (char *) 0, cclnum, + ccltab, CCL_HASH_SIZE ); + } + + +/* ccllookup - lookup the number associated with character class text + * + * synopsis + * Char ccltxt[]; + * int ccllookup, cclval; + * cclval/0 = ccllookup( ccltxt ); + */ + +int ccllookup(Char *ccltxt) +{ + return ( findsym( (char *) ccltxt, ccltab, CCL_HASH_SIZE )->int_val ); + } + + +/* findsym - find symbol in symbol table + * + * synopsis + * char sym[]; + * hash_table table; + * int table_size; + * struct hash_entry *sym_entry, *findsym(); + * sym_entry = findsym( sym, table, table_size ); + */ + +struct hash_entry *findsym(char *sym, struct hash_entry **table, int table_size) +{ + struct hash_entry *sym_entry = table[hashfunct( sym, table_size )]; + static struct hash_entry empty_entry = + { + (struct hash_entry *) 0, (struct hash_entry *) 0, NULL, NULL, 0, + } ; + + while ( sym_entry ) + { + if ( ! strcmp( sym, sym_entry->name ) ) + return ( sym_entry ); + sym_entry = sym_entry->next; + } + + return ( &empty_entry ); + } + + +/* hashfunct - compute the hash value for "str" and hash size "hash_size" + * + * synopsis + * char str[]; + * int hash_size, hash_val; + * hash_val = hashfunct( str, hash_size ); + */ + +int hashfunct(char *str, int hash_size) +{ + int hashval; + int locstr; + + hashval = 0; + locstr = 0; + + while ( str[locstr] ) + hashval = ((hashval << 1) + str[locstr++]) % hash_size; + + return ( hashval ); + } + + +/* ndinstal - install a name definition + * + * synopsis + * char nd[]; + * Char def[]; + * ndinstal( nd, def ); + */ + +void ndinstal(char *nd, Char *def) +{ + char *copy_string(); + Char *copy_unsigned_string(); + + if ( addsym( copy_string( nd ), (char *) copy_unsigned_string( def ), 0, + ndtbl, NAME_TABLE_HASH_SIZE ) ) + synerr( "name defined twice" ); + } + + +/* ndlookup - lookup a name definition + * + * synopsis + * char nd[], *def; + * char *ndlookup(); + * def/NULL = ndlookup( nd ); + */ + +Char *ndlookup(char *nd) +{ + return ( (Char *) findsym( nd, ndtbl, NAME_TABLE_HASH_SIZE )->str_val ); + } + + +/* scinstal - make a start condition + * + * synopsis + * char str[]; + * int xcluflg; + * scinstal( str, xcluflg ); + * + * NOTE + * the start condition is Exclusive if xcluflg is true + */ + +void scinstal(char *str, int xcluflg) +{ + char *copy_string(); + + /* bit of a hack. We know how the default start-condition is + * declared, and don't put out a define for it, because it + * would come out as "#define 0 1" + */ + /* actually, this is no longer the case. The default start-condition + * is now called "INITIAL". But we keep the following for the sake + * of future robustness. + */ + + if ( strcmp( str, "0" ) ) + printf( "#define %s %d\n", str, lastsc ); + + if ( ++lastsc >= current_max_scs ) + { + current_max_scs += MAX_SCS_INCREMENT; + + ++num_reallocs; + + scset = reallocate_integer_array( scset, current_max_scs ); + scbol = reallocate_integer_array( scbol, current_max_scs ); + scxclu = reallocate_integer_array( scxclu, current_max_scs ); + sceof = reallocate_integer_array( sceof, current_max_scs ); + scname = reallocate_char_ptr_array( scname, current_max_scs ); + actvsc = reallocate_integer_array( actvsc, current_max_scs ); + } + + scname[lastsc] = copy_string( str ); + + if ( addsym( scname[lastsc], (char *) 0, lastsc, + sctbl, START_COND_HASH_SIZE ) ) + format_pinpoint_message( "start condition %s declared twice", str ); + + scset[lastsc] = mkstate( SYM_EPSILON ); + scbol[lastsc] = mkstate( SYM_EPSILON ); + scxclu[lastsc] = xcluflg; + sceof[lastsc] = false; + } + + +/* sclookup - lookup the number associated with a start condition + * + * synopsis + * char str[], scnum; + * int sclookup; + * scnum/0 = sclookup( str ); + */ + +int sclookup(char *str) +{ + return ( findsym( str, sctbl, START_COND_HASH_SIZE )->int_val ); + } diff --git a/src/toolsComm/flex/tblcmp.c b/src/toolsComm/flex/tblcmp.c new file mode 100644 index 000000000..fa50462b9 --- /dev/null +++ b/src/toolsComm/flex/tblcmp.c @@ -0,0 +1,909 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* tblcmp - table compression routines */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char rcsid[] = + "@(#) Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd (LBL)"; +#endif + +#include "flexdef.h" + + +/* declarations for functions that have forward references */ + +void mkentry (int*, int, int, int, int); +void mkprot (int[], int, int); +void mktemplate (int[], int, int); +void mv2front (int); +int tbldiff (int[], int, int[]); + + +/* bldtbl - build table entries for dfa state + * + * synopsis + * int state[numecs], statenum, totaltrans, comstate, comfreq; + * bldtbl( state, statenum, totaltrans, comstate, comfreq ); + * + * State is the statenum'th dfa state. It is indexed by equivalence class and + * gives the number of the state to enter for a given equivalence class. + * totaltrans is the total number of transitions out of the state. Comstate + * is that state which is the destination of the most transitions out of State. + * Comfreq is how many transitions there are out of State to Comstate. + * + * A note on terminology: + * "protos" are transition tables which have a high probability of + * either being redundant (a state processed later will have an identical + * transition table) or nearly redundant (a state processed later will have + * many of the same out-transitions). A "most recently used" queue of + * protos is kept around with the hope that most states will find a proto + * which is similar enough to be usable, and therefore compacting the + * output tables. + * "templates" are a special type of proto. If a transition table is + * homogeneous or nearly homogeneous (all transitions go to the same + * destination) then the odds are good that future states will also go + * to the same destination state on basically the same character set. + * These homogeneous states are so common when dealing with large rule + * sets that they merit special attention. If the transition table were + * simply made into a proto, then (typically) each subsequent, similar + * state will differ from the proto for two out-transitions. One of these + * out-transitions will be that character on which the proto does not go + * to the common destination, and one will be that character on which the + * state does not go to the common destination. Templates, on the other + * hand, go to the common state on EVERY transition character, and therefore + * cost only one difference. + */ + +void bldtbl(int *state, int statenum, int totaltrans, int comstate, int comfreq) +{ + int extptr, extrct[2][CSIZE + 1]; + int mindiff, minprot, i, d; + int checkcom; + + /* If extptr is 0 then the first array of extrct holds the result of the + * "best difference" to date, which is those transitions which occur in + * "state" but not in the proto which, to date, has the fewest differences + * between itself and "state". If extptr is 1 then the second array of + * extrct hold the best difference. The two arrays are toggled + * between so that the best difference to date can be kept around and + * also a difference just created by checking against a candidate "best" + * proto. + */ + + extptr = 0; + + /* if the state has too few out-transitions, don't bother trying to + * compact its tables + */ + + if ( (totaltrans * 100) < (numecs * PROTO_SIZE_PERCENTAGE) ) + mkentry( state, numecs, statenum, JAMSTATE, totaltrans ); + + else + { + /* checkcom is true if we should only check "state" against + * protos which have the same "comstate" value + */ + + checkcom = comfreq * 100 > totaltrans * CHECK_COM_PERCENTAGE; + + minprot = firstprot; + mindiff = totaltrans; + + if ( checkcom ) + { + /* find first proto which has the same "comstate" */ + for ( i = firstprot; i != NIL; i = protnext[i] ) + if ( protcomst[i] == comstate ) + { + minprot = i; + mindiff = tbldiff( state, minprot, extrct[extptr] ); + break; + } + } + + else + { + /* since we've decided that the most common destination out + * of "state" does not occur with a high enough frequency, + * we set the "comstate" to zero, assuring that if this state + * is entered into the proto list, it will not be considered + * a template. + */ + comstate = 0; + + if ( firstprot != NIL ) + { + minprot = firstprot; + mindiff = tbldiff( state, minprot, extrct[extptr] ); + } + } + + /* we now have the first interesting proto in "minprot". If + * it matches within the tolerances set for the first proto, + * we don't want to bother scanning the rest of the proto list + * to see if we have any other reasonable matches. + */ + + if ( mindiff * 100 > totaltrans * FIRST_MATCH_DIFF_PERCENTAGE ) + { /* not a good enough match. Scan the rest of the protos */ + for ( i = minprot; i != NIL; i = protnext[i] ) + { + d = tbldiff( state, i, extrct[1 - extptr] ); + if ( d < mindiff ) + { + extptr = 1 - extptr; + mindiff = d; + minprot = i; + } + } + } + + /* check if the proto we've decided on as our best bet is close + * enough to the state we want to match to be usable + */ + + if ( mindiff * 100 > totaltrans * ACCEPTABLE_DIFF_PERCENTAGE ) + { + /* no good. If the state is homogeneous enough, we make a + * template out of it. Otherwise, we make a proto. + */ + + if ( comfreq * 100 >= totaltrans * TEMPLATE_SAME_PERCENTAGE ) + mktemplate( state, statenum, comstate ); + + else + { + mkprot( state, statenum, comstate ); + mkentry( state, numecs, statenum, JAMSTATE, totaltrans ); + } + } + + else + { /* use the proto */ + mkentry( extrct[extptr], numecs, statenum, + prottbl[minprot], mindiff ); + + /* if this state was sufficiently different from the proto + * we built it from, make it, too, a proto + */ + + if ( mindiff * 100 >= totaltrans * NEW_PROTO_DIFF_PERCENTAGE ) + mkprot( state, statenum, comstate ); + + /* since mkprot added a new proto to the proto queue, it's possible + * that "minprot" is no longer on the proto queue (if it happened + * to have been the last entry, it would have been bumped off). + * If it's not there, then the new proto took its physical place + * (though logically the new proto is at the beginning of the + * queue), so in that case the following call will do nothing. + */ + + mv2front( minprot ); + } + } + } + + +/* cmptmps - compress template table entries + * + * synopsis + * cmptmps(); + * + * template tables are compressed by using the 'template equivalence + * classes', which are collections of transition character equivalence + * classes which always appear together in templates - really meta-equivalence + * classes. until this point, the tables for templates have been stored + * up at the top end of the nxt array; they will now be compressed and have + * table entries made for them. + */ + +void cmptmps(void) +{ + int tmpstorage[CSIZE + 1]; + int *tmp = tmpstorage, i, j; + int totaltrans, trans; + + peakpairs = numtemps * numecs + tblend; + + if ( usemecs ) + { + /* create equivalence classes base on data gathered on template + * transitions + */ + + nummecs = cre8ecs( tecfwd, tecbck, numecs ); + } + + else + nummecs = numecs; + + if ( lastdfa + numtemps + 1 >= current_max_dfas ) + increase_max_dfas(); + + /* loop through each template */ + + for ( i = 1; i <= numtemps; ++i ) + { + totaltrans = 0; /* number of non-jam transitions out of this template */ + + for ( j = 1; j <= numecs; ++j ) + { + trans = tnxt[numecs * i + j]; + + if ( usemecs ) + { + /* the absolute value of tecbck is the meta-equivalence class + * of a given equivalence class, as set up by cre8ecs + */ + if ( tecbck[j] > 0 ) + { + tmp[tecbck[j]] = trans; + + if ( trans > 0 ) + ++totaltrans; + } + } + + else + { + tmp[j] = trans; + + if ( trans > 0 ) + ++totaltrans; + } + } + + /* it is assumed (in a rather subtle way) in the skeleton that + * if we're using meta-equivalence classes, the def[] entry for + * all templates is the jam template, i.e., templates never default + * to other non-jam table entries (e.g., another template) + */ + + /* leave room for the jam-state after the last real state */ + mkentry( tmp, nummecs, lastdfa + i + 1, JAMSTATE, totaltrans ); + } + } + + + +/* expand_nxt_chk - expand the next check arrays */ + +void expand_nxt_chk(void) +{ + int old_max = current_max_xpairs; + + current_max_xpairs += MAX_XPAIRS_INCREMENT; + + ++num_reallocs; + + nxt = reallocate_integer_array( nxt, current_max_xpairs ); + chk = reallocate_integer_array( chk, current_max_xpairs ); + + memset( (char *) (chk + old_max), 0, + MAX_XPAIRS_INCREMENT * sizeof( int ) / sizeof( char ) ); + } + + +/* find_table_space - finds a space in the table for a state to be placed + * + * synopsis + * int *state, numtrans, block_start; + * int find_table_space(); + * + * block_start = find_table_space( state, numtrans ); + * + * State is the state to be added to the full speed transition table. + * Numtrans is the number of out-transitions for the state. + * + * find_table_space() returns the position of the start of the first block (in + * chk) able to accommodate the state + * + * In determining if a state will or will not fit, find_table_space() must take + * into account the fact that an end-of-buffer state will be added at [0], + * and an action number will be added in [-1]. + */ + +int find_table_space(int *state, int numtrans) +{ + /* firstfree is the position of the first possible occurrence of two + * consecutive unused records in the chk and nxt arrays + */ + int i; + int *state_ptr, *chk_ptr; + int *ptr_to_last_entry_in_state; + + /* if there are too many out-transitions, put the state at the end of + * nxt and chk + */ + if ( numtrans > MAX_XTIONS_FULL_INTERIOR_FIT ) + { + /* if table is empty, return the first available spot in chk/nxt, + * which should be 1 + */ + if ( tblend < 2 ) + return ( 1 ); + + i = tblend - numecs; /* start searching for table space near the + * end of chk/nxt arrays + */ + } + + else + i = firstfree; /* start searching for table space from the + * beginning (skipping only the elements + * which will definitely not hold the new + * state) + */ + + while ( 1 ) /* loops until a space is found */ + { + if ( i + numecs > current_max_xpairs ) + expand_nxt_chk(); + + /* loops until space for end-of-buffer and action number are found */ + while ( 1 ) + { + if ( chk[i - 1] == 0 ) /* check for action number space */ + { + if ( chk[i] == 0 ) /* check for end-of-buffer space */ + break; + + else + i += 2; /* since i != 0, there is no use checking to + * see if (++i) - 1 == 0, because that's the + * same as i == 0, so we skip a space + */ + } + + else + ++i; + + if ( i + numecs > current_max_xpairs ) + expand_nxt_chk(); + } + + /* if we started search from the beginning, store the new firstfree for + * the next call of find_table_space() + */ + if ( numtrans <= MAX_XTIONS_FULL_INTERIOR_FIT ) + firstfree = i + 1; + + /* check to see if all elements in chk (and therefore nxt) that are + * needed for the new state have not yet been taken + */ + + state_ptr = &state[1]; + ptr_to_last_entry_in_state = &chk[i + numecs + 1]; + + for ( chk_ptr = &chk[i + 1]; chk_ptr != ptr_to_last_entry_in_state; + ++chk_ptr ) + if ( *(state_ptr++) != 0 && *chk_ptr != 0 ) + break; + + if ( chk_ptr == ptr_to_last_entry_in_state ) + return ( i ); + + else + ++i; + } + } + + +/* inittbl - initialize transition tables + * + * synopsis + * inittbl(); + * + * Initializes "firstfree" to be one beyond the end of the table. Initializes + * all "chk" entries to be zero. Note that templates are built in their + * own tbase/tdef tables. They are shifted down to be contiguous + * with the non-template entries during table generation. + */ +void inittbl(void) +{ + int i; + + memset( (char *) chk, 0, + current_max_xpairs * sizeof( int ) / sizeof( char ) ); + + tblend = 0; + firstfree = tblend + 1; + numtemps = 0; + + if ( usemecs ) + { + /* set up doubly-linked meta-equivalence classes + * these are sets of equivalence classes which all have identical + * transitions out of TEMPLATES + */ + + tecbck[1] = NIL; + + for ( i = 2; i <= numecs; ++i ) + { + tecbck[i] = i - 1; + tecfwd[i - 1] = i; + } + + tecfwd[numecs] = NIL; + } + } + + +/* mkdeftbl - make the default, "jam" table entries + * + * synopsis + * mkdeftbl(); + */ + +void mkdeftbl(void) +{ + int i; + + jamstate = lastdfa + 1; + + ++tblend; /* room for transition on end-of-buffer character */ + + if ( tblend + numecs > current_max_xpairs ) + expand_nxt_chk(); + + /* add in default end-of-buffer transition */ + nxt[tblend] = end_of_buffer_state; + chk[tblend] = jamstate; + + for ( i = 1; i <= numecs; ++i ) + { + nxt[tblend + i] = 0; + chk[tblend + i] = jamstate; + } + + jambase = tblend; + + base[jamstate] = jambase; + def[jamstate] = 0; + + tblend += numecs; + ++numtemps; + } + + +/* mkentry - create base/def and nxt/chk entries for transition array + * + * synopsis + * int state[numchars + 1], numchars, statenum, deflink, totaltrans; + * mkentry( state, numchars, statenum, deflink, totaltrans ); + * + * "state" is a transition array "numchars" characters in size, "statenum" + * is the offset to be used into the base/def tables, and "deflink" is the + * entry to put in the "def" table entry. If "deflink" is equal to + * "JAMSTATE", then no attempt will be made to fit zero entries of "state" + * (i.e., jam entries) into the table. It is assumed that by linking to + * "JAMSTATE" they will be taken care of. In any case, entries in "state" + * marking transitions to "SAME_TRANS" are treated as though they will be + * taken care of by whereever "deflink" points. "totaltrans" is the total + * number of transitions out of the state. If it is below a certain threshold, + * the tables are searched for an interior spot that will accommodate the + * state array. + */ + +void mkentry(int *state, int numchars, int statenum, int deflink, int totaltrans) +{ + int minec, maxec, i, baseaddr; + int tblbase, tbllast; + + if ( totaltrans == 0 ) + { /* there are no out-transitions */ + if ( deflink == JAMSTATE ) + base[statenum] = JAMSTATE; + else + base[statenum] = 0; + + def[statenum] = deflink; + return; + } + + for ( minec = 1; minec <= numchars; ++minec ) + { + if ( state[minec] != SAME_TRANS ) + if ( state[minec] != 0 || deflink != JAMSTATE ) + break; + } + + if ( totaltrans == 1 ) + { + /* there's only one out-transition. Save it for later to fill + * in holes in the tables. + */ + stack1( statenum, minec, state[minec], deflink ); + return; + } + + for ( maxec = numchars; maxec > 0; --maxec ) + { + if ( state[maxec] != SAME_TRANS ) + if ( state[maxec] != 0 || deflink != JAMSTATE ) + break; + } + + /* Whether we try to fit the state table in the middle of the table + * entries we have already generated, or if we just take the state + * table at the end of the nxt/chk tables, we must make sure that we + * have a valid base address (i.e., non-negative). Note that not only are + * negative base addresses dangerous at run-time (because indexing the + * next array with one and a low-valued character might generate an + * array-out-of-bounds error message), but at compile-time negative + * base addresses denote TEMPLATES. + */ + + /* find the first transition of state that we need to worry about. */ + if ( totaltrans * 100 <= numchars * INTERIOR_FIT_PERCENTAGE ) + { /* attempt to squeeze it into the middle of the tabls */ + baseaddr = firstfree; + + while ( baseaddr < minec ) + { + /* using baseaddr would result in a negative base address below + * find the next free slot + */ + for ( ++baseaddr; chk[baseaddr] != 0; ++baseaddr ) + ; + } + + if ( baseaddr + maxec - minec >= current_max_xpairs ) + expand_nxt_chk(); + + for ( i = minec; i <= maxec; ++i ) + if ( state[i] != SAME_TRANS ) + if ( state[i] != 0 || deflink != JAMSTATE ) + if ( chk[baseaddr + i - minec] != 0 ) + { /* baseaddr unsuitable - find another */ + for ( ++baseaddr; + baseaddr < current_max_xpairs && + chk[baseaddr] != 0; + ++baseaddr ) + ; + + if ( baseaddr + maxec - minec >= current_max_xpairs ) + expand_nxt_chk(); + + /* reset the loop counter so we'll start all + * over again next time it's incremented + */ + + i = minec - 1; + } + } + + else + { + /* ensure that the base address we eventually generate is + * non-negative + */ + baseaddr = max( tblend + 1, minec ); + } + + tblbase = baseaddr - minec; + tbllast = tblbase + maxec; + + if ( tbllast >= current_max_xpairs ) + expand_nxt_chk(); + + base[statenum] = tblbase; + def[statenum] = deflink; + + for ( i = minec; i <= maxec; ++i ) + if ( state[i] != SAME_TRANS ) + if ( state[i] != 0 || deflink != JAMSTATE ) + { + nxt[tblbase + i] = state[i]; + chk[tblbase + i] = statenum; + } + + if ( baseaddr == firstfree ) + /* find next free slot in tables */ + for ( ++firstfree; chk[firstfree] != 0; ++firstfree ) + ; + + tblend = max( tblend, tbllast ); + } + + +/* mk1tbl - create table entries for a state (or state fragment) which + * has only one out-transition + * + * synopsis + * int state, sym, onenxt, onedef; + * mk1tbl( state, sym, onenxt, onedef ); + */ + +void mk1tbl(int state, int sym, int onenxt, int onedef) +{ + if ( firstfree < sym ) + firstfree = sym; + + while ( chk[firstfree] != 0 ) + if ( ++firstfree >= current_max_xpairs ) + expand_nxt_chk(); + + base[state] = firstfree - sym; + def[state] = onedef; + chk[firstfree] = state; + nxt[firstfree] = onenxt; + + if ( firstfree > tblend ) + { + tblend = firstfree++; + + if ( firstfree >= current_max_xpairs ) + expand_nxt_chk(); + } + } + + +/* mkprot - create new proto entry + * + * synopsis + * int state[], statenum, comstate; + * mkprot( state, statenum, comstate ); + */ + +void mkprot(int *state, int statenum, int comstate) +{ + int i, slot, tblbase; + + if ( ++numprots >= MSP || numecs * numprots >= PROT_SAVE_SIZE ) + { + /* gotta make room for the new proto by dropping last entry in + * the queue + */ + slot = lastprot; + lastprot = protprev[lastprot]; + protnext[lastprot] = NIL; + } + + else + slot = numprots; + + protnext[slot] = firstprot; + + if ( firstprot != NIL ) + protprev[firstprot] = slot; + + firstprot = slot; + prottbl[slot] = statenum; + protcomst[slot] = comstate; + + /* copy state into save area so it can be compared with rapidly */ + tblbase = numecs * (slot - 1); + + for ( i = 1; i <= numecs; ++i ) + protsave[tblbase + i] = state[i]; + } + + +/* mktemplate - create a template entry based on a state, and connect the state + * to it + * + * synopsis + * int state[], statenum, comstate, totaltrans; + * mktemplate( state, statenum, comstate, totaltrans ); + */ + +void mktemplate(int *state, int statenum, int comstate) +{ + int i, numdiff, tmpbase, tmp[CSIZE + 1]; + Char transset[CSIZE + 1]; + int tsptr; + + ++numtemps; + + tsptr = 0; + + /* calculate where we will temporarily store the transition table + * of the template in the tnxt[] array. The final transition table + * gets created by cmptmps() + */ + + tmpbase = numtemps * numecs; + + if ( tmpbase + numecs >= current_max_template_xpairs ) + { + current_max_template_xpairs += MAX_TEMPLATE_XPAIRS_INCREMENT; + + ++num_reallocs; + + tnxt = reallocate_integer_array( tnxt, current_max_template_xpairs ); + } + + for ( i = 1; i <= numecs; ++i ) + if ( state[i] == 0 ) + tnxt[tmpbase + i] = 0; + else + { + transset[tsptr++] = i; + tnxt[tmpbase + i] = comstate; + } + + if ( usemecs ) + mkeccl( transset, tsptr, tecfwd, tecbck, numecs, 0 ); + + mkprot( tnxt + tmpbase, -numtemps, comstate ); + + /* we rely on the fact that mkprot adds things to the beginning + * of the proto queue + */ + + numdiff = tbldiff( state, firstprot, tmp ); + mkentry( tmp, numecs, statenum, -numtemps, numdiff ); + } + + +/* mv2front - move proto queue element to front of queue + * + * synopsis + * int qelm; + * mv2front( qelm ); + */ + +void mv2front(int qelm) +{ + if ( firstprot != qelm ) + { + if ( qelm == lastprot ) + lastprot = protprev[lastprot]; + + protnext[protprev[qelm]] = protnext[qelm]; + + if ( protnext[qelm] != NIL ) + protprev[protnext[qelm]] = protprev[qelm]; + + protprev[qelm] = NIL; + protnext[qelm] = firstprot; + protprev[firstprot] = qelm; + firstprot = qelm; + } + } + + +/* place_state - place a state into full speed transition table + * + * synopsis + * int *state, statenum, transnum; + * place_state( state, statenum, transnum ); + * + * State is the statenum'th state. It is indexed by equivalence class and + * gives the number of the state to enter for a given equivalence class. + * Transnum is the number of out-transitions for the state. + */ + +void place_state(int *state, int statenum, int transnum) +{ + int i; + int *state_ptr; + int position = find_table_space( state, transnum ); + + /* base is the table of start positions */ + base[statenum] = position; + + /* put in action number marker; this non-zero number makes sure that + * find_table_space() knows that this position in chk/nxt is taken + * and should not be used for another accepting number in another state + */ + chk[position - 1] = 1; + + /* put in end-of-buffer marker; this is for the same purposes as above */ + chk[position] = 1; + + /* place the state into chk and nxt */ + state_ptr = &state[1]; + + for ( i = 1; i <= numecs; ++i, ++state_ptr ) + if ( *state_ptr != 0 ) + { + chk[position + i] = i; + nxt[position + i] = *state_ptr; + } + + if ( position + numecs > tblend ) + tblend = position + numecs; + } + + +/* stack1 - save states with only one out-transition to be processed later + * + * synopsis + * int statenum, sym, nextstate, deflink; + * stack1( statenum, sym, nextstate, deflink ); + * + * if there's room for another state one the "one-transition" stack, the + * state is pushed onto it, to be processed later by mk1tbl. If there's + * no room, we process the sucker right now. + */ + +void stack1(int statenum, int sym, int nextstate, int deflink) +{ + if ( onesp >= ONE_STACK_SIZE - 1 ) + mk1tbl( statenum, sym, nextstate, deflink ); + + else + { + ++onesp; + onestate[onesp] = statenum; + onesym[onesp] = sym; + onenext[onesp] = nextstate; + onedef[onesp] = deflink; + } + } + + +/* tbldiff - compute differences between two state tables + * + * synopsis + * int state[], pr, ext[]; + * int tbldiff, numdifferences; + * numdifferences = tbldiff( state, pr, ext ) + * + * "state" is the state array which is to be extracted from the pr'th + * proto. "pr" is both the number of the proto we are extracting from + * and an index into the save area where we can find the proto's complete + * state table. Each entry in "state" which differs from the corresponding + * entry of "pr" will appear in "ext". + * Entries which are the same in both "state" and "pr" will be marked + * as transitions to "SAME_TRANS" in "ext". The total number of differences + * between "state" and "pr" is returned as function value. Note that this + * number is "numecs" minus the number of "SAME_TRANS" entries in "ext". + */ + +int tbldiff(int *state, int pr, int *ext) +{ + int i, *sp = state, *ep = ext, *protp; + int numdiff = 0; + + protp = &protsave[numecs * (pr - 1)]; + + for ( i = numecs; i > 0; --i ) + { + if ( *++protp == *++sp ) + *++ep = SAME_TRANS; + else + { + *++ep = *sp; + ++numdiff; + } + } + + return ( numdiff ); + } diff --git a/src/toolsComm/flex/yylex.c b/src/toolsComm/flex/yylex.c new file mode 100644 index 000000000..272b8f7b5 --- /dev/null +++ b/src/toolsComm/flex/yylex.c @@ -0,0 +1,222 @@ +/*************************************************************************\ +* 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 is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* yylex - scanner front-end for flex */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Vern Paxson. + * + * The United States Government has rights in this work pursuant + * to contract no. DE-AC03-76SF00098 between the United States + * Department of Energy and the University of California. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + */ + +#include +#include "flexdef.h" +#include "parse.h" + +/* ANSI C does not guarantee that isascii() is defined */ +#ifndef isascii +#define isascii(c) ((c) <= 0177) +#endif + + +/* yylex - scan for a regular expression token + * + * synopsis + * + * token = yylex(); + * + * token - return token found + */ + +int yylex(void) +{ + int toktype; + static int beglin = false; + + if ( eofseen ) + toktype = EOF; + else + toktype = flexscan(); + + if ( toktype == EOF || toktype == 0 ) + { + eofseen = 1; + + if ( sectnum == 1 ) + { + synerr( "premature EOF" ); + sectnum = 2; + toktype = SECTEND; + } + + else if ( sectnum == 2 ) + { + sectnum = 3; + toktype = 0; + } + + else + toktype = 0; + } + + if ( trace ) + { + if ( beglin ) + { + fprintf( stderr, "%d\t", num_rules + 1 ); + beglin = 0; + } + + switch ( toktype ) + { + case '<': + case '>': + case '^': + case '$': + case '"': + case '[': + case ']': + case '{': + case '}': + case '|': + case '(': + case ')': + case '-': + case '/': + case '\\': + case '?': + case '.': + case '*': + case '+': + case ',': + (void) putc( toktype, stderr ); + break; + + case '\n': + (void) putc( '\n', stderr ); + + if ( sectnum == 2 ) + beglin = 1; + + break; + + case SCDECL: + fputs( "%s", stderr ); + break; + + case XSCDECL: + fputs( "%x", stderr ); + break; + + case WHITESPACE: + (void) putc( ' ', stderr ); + break; + + case SECTEND: + fputs( "%%\n", stderr ); + + /* we set beglin to be true so we'll start + * writing out numbers as we echo rules. flexscan() has + * already assigned sectnum + */ + + if ( sectnum == 2 ) + beglin = 1; + + break; + + case NAME: + fprintf( stderr, "'%s'", nmstr ); + break; + + case CHAR: + switch ( yylval ) + { + case '<': + case '>': + case '^': + case '$': + case '"': + case '[': + case ']': + case '{': + case '}': + case '|': + case '(': + case ')': + case '-': + case '/': + case '\\': + case '?': + case '.': + case '*': + case '+': + case ',': + fprintf( stderr, "\\%c", yylval ); + break; + + default: + if ( ! isascii( yylval ) || ! isprint( yylval ) ) + fprintf( stderr, "\\%.3o", yylval ); + else + (void) putc( yylval, stderr ); + break; + } + + break; + + case NUMBER: + fprintf( stderr, "%d", yylval ); + break; + + case PREVCCL: + fprintf( stderr, "[%d]", yylval ); + break; + + case EOF_OP: + fprintf( stderr, "<>" ); + break; + + case 0: + fprintf( stderr, "End Marker" ); + break; + + default: + fprintf( stderr, "*Something Weird* - tok: %d val: %d\n", + toktype, yylval ); + break; + } + } + + return ( toktype ); +} + diff --git a/src/util/Makefile b/src/util/Makefile new file mode 100644 index 000000000..e0cd22c77 --- /dev/null +++ b/src/util/Makefile @@ -0,0 +1,38 @@ +#************************************************************************* +# 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 = ca Com + +# +# Added winmm user32 for the non-dll build +# +PROD_HOST_DEFAULT = ca_test iocLogServer +PROD_HOST_WIN32 = ca_test iocLogServer +PROD_SYS_LIBS_WIN32 = ws2_32 advapi32 user32 + +iocLogServer_SYS_LIBS_solaris = socket + +ca_test_SRCS = ca_test_main.c ca_test.c +iocLogServer_SRCS = iocLogServer.c + +OBJS_vxWorks = ca_test + +SCRIPTS_solaris := S99logServer S99caRepeater +SCRIPTS_Linux := S99logServer S99caRepeater + +include $(TOP)/configure/RULES + +S99%: ../rc2.% + sed -e s%:INSTALL_BIN:%`cd $(INSTALL_BIN); pwd`% $< >$@ + +# EOF Makefile.Host for base/src/util diff --git a/src/util/ca_test.c b/src/util/ca_test.c new file mode 100644 index 000000000..e03d25eb8 --- /dev/null +++ b/src/util/ca_test.c @@ -0,0 +1,326 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* @(#)ca_test.c Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: Jeff Hill + * Date: 07-01-91 + * + * make options + * -DvxWorks makes a version for VxWorks + */ + +/* + * ANSI + */ +#include +#include +#include + +#include "cadef.h" +#include "epicsTime.h" + +int ca_test(char *pname, char *pvalue); +static int cagft(char *pname); +static void printit(struct event_handler_args args); +static int capft(char *pname, char *pvalue); +static void verify_value(chid chan_id, chtype type); + +static unsigned long outstanding; + + +/* + * ca_test + * + * find channel, write a value if supplied, and + * read back the current value + * + */ +int ca_test( +char *pname, +char *pvalue +) +{ + int status; + if(pvalue){ + status = capft(pname,pvalue); + } + else{ + status = cagft(pname); + } + ca_task_exit(); + return status; +} + + + +/* + * cagft() + * + * ca get field test + * + * test ca get over the range of CA data types + */ +static int cagft(char *pname) +{ + const unsigned maxTries = 1000ul; + unsigned ntries = 0u; + chid chan_id; + int status; + int i; + + /* + * convert name to chan id + */ + status = ca_search(pname, &chan_id); + SEVCHK(status,NULL); + status = ca_pend_io(5.0); + if(status != ECA_NORMAL){ + SEVCHK(ca_clear_channel(chan_id),NULL); + printf("Not Found %s\n", pname); + return -1; + } + + printf("name:\t%s\n", + ca_name(chan_id)); + printf("native type:\t%s\n", + dbr_type_to_text(ca_field_type(chan_id))); + printf("native count:\t%lu\n", + ca_element_count(chan_id)); + + + /* + * fetch as each type + */ + for(i=0; i<=LAST_BUFFER_TYPE; i++){ + if(ca_field_type(chan_id)==DBR_STRING) { + if( (i!=DBR_STRING) + && (i!=DBR_STS_STRING) + && (i!=DBR_TIME_STRING) + && (i!=DBR_GR_STRING) + && (i!=DBR_CTRL_STRING)) { + continue; + } + } + /* ignore write only types */ + if ( + i == DBR_PUT_ACKT || + i == DBR_PUT_ACKS ) { + continue; + } + + status = ca_array_get_callback( + i, + ca_element_count(chan_id), + chan_id, + printit, + NULL); + SEVCHK(status, NULL); + + outstanding++; + } + + /* + * wait for the operation to complete + * before returning + */ + while ( ntries < maxTries ) { + unsigned long oldOut; + + oldOut = outstanding; + ca_pend_event ( 0.05 ); + + if ( ! outstanding ) { + SEVCHK ( ca_clear_channel ( chan_id ), NULL ); + printf ( "\n\n" ); + return 0; + } + + if ( outstanding == oldOut ) { + ntries++; + } + } + + SEVCHK ( ca_clear_channel ( chan_id ), NULL ); + return -1; +} + + +/* + * PRINTIT() + */ +static void printit ( struct event_handler_args args ) +{ + if ( args.status == ECA_NORMAL ) { + ca_dump_dbr ( args.type, args.count, args.dbr ); + } + else { + printf ( "%s\t%s\n", dbr_text[args.type], ca_message(args.status) ); + } + + outstanding--; +} + +/* + * capft + * + * test ca_put() over a range of data types + * + */ +static int capft( +char *pname, +char *pvalue +) +{ + dbr_short_t shortvalue; + dbr_long_t longvalue; + dbr_float_t floatvalue; + dbr_char_t charvalue; + dbr_double_t doublevalue; + unsigned long ntries = 10ul; + int status; + chid chan_id; + + if (((*pname < ' ') || (*pname > 'z')) + || ((*pvalue < ' ') || (*pvalue > 'z'))){ + printf("\nusage \"pv name\",\"value\"\n"); + return -1; + } + + /* + * convert name to chan id + */ + status = ca_search(pname, &chan_id); + SEVCHK(status,NULL); + status = ca_pend_io(5.0); + if(status != ECA_NORMAL){ + SEVCHK(ca_clear_channel(chan_id),NULL); + printf("Not Found %s\n", pname); + return -1; + } + + printf("name:\t%s\n", ca_name(chan_id)); + printf("native type:\t%d\n", ca_field_type(chan_id)); + printf("native count:\t%lu\n", ca_element_count(chan_id)); + + /* + * string value ca_put + */ + status = ca_put( + DBR_STRING, + chan_id, + pvalue); + SEVCHK(status, NULL); + verify_value(chan_id, DBR_STRING); + + if(ca_field_type(chan_id)==0)goto skip_rest; + + if(sscanf(pvalue,"%hd",&shortvalue)==1) { + /* + * short integer ca_put + */ + status = ca_put( + DBR_SHORT, + chan_id, + &shortvalue); + SEVCHK(status, NULL); + verify_value(chan_id, DBR_SHORT); + status = ca_put( + DBR_ENUM, + chan_id, + &shortvalue); + SEVCHK(status, NULL); + verify_value(chan_id, DBR_ENUM); + charvalue=(dbr_char_t)shortvalue; + status = ca_put( + DBR_CHAR, + chan_id, + &charvalue); + SEVCHK(status, NULL); + verify_value(chan_id, DBR_CHAR); + } + if(sscanf(pvalue,"%d",&longvalue)==1) { + /* + * long integer ca_put + */ + status = ca_put( + DBR_LONG, + chan_id, + &longvalue); + SEVCHK(status, NULL); + verify_value(chan_id, DBR_LONG); + } + if(epicsScanFloat(pvalue, &floatvalue)==1) { + /* + * single precision float ca_put + */ + status = ca_put( + DBR_FLOAT, + chan_id, + &floatvalue); + SEVCHK(status, NULL); + verify_value(chan_id, DBR_FLOAT); + } + if(epicsScanDouble(pvalue, &doublevalue)==1) { + /* + * double precision float ca_put + */ + status = ca_put( + DBR_DOUBLE, + chan_id, + &doublevalue); + SEVCHK(status, NULL); + verify_value(chan_id, DBR_DOUBLE); + } + +skip_rest: + + /* + * wait for the operation to complete + * (outstabnding decrements to zero) + */ + while(ntries){ + ca_pend_event(1.0); + + if(!outstanding){ + SEVCHK(ca_clear_channel(chan_id),NULL); + printf("\n\n"); + return 0; + } + + ntries--; + } + + SEVCHK(ca_clear_channel(chan_id),NULL); + return -1; +} + + +/* + * VERIFY_VALUE + * + * initiate print out the values in a database access interface structure + */ +static void verify_value(chid chan_id, chtype type) +{ + int status; + + /* + * issue a get which calls back `printit' + * upon completion + */ + status = ca_array_get_callback( + type, + ca_element_count(chan_id), + chan_id, + printit, + NULL); + SEVCHK(status, NULL); + + outstanding++; +} diff --git a/src/util/ca_test.h b/src/util/ca_test.h new file mode 100644 index 000000000..82116107b --- /dev/null +++ b/src/util/ca_test.h @@ -0,0 +1,23 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* @(#)ca_test.k Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: Jeff Hill + * Date: 21JAN2000 + */ + +#ifdef __cplusplus +extern "C" { +#endif + +int ca_test(char *pname, char *pvalue); + +#ifdef __cplusplus +} +#endif diff --git a/src/util/ca_test_main.c b/src/util/ca_test_main.c new file mode 100644 index 000000000..fd608a42b --- /dev/null +++ b/src/util/ca_test_main.c @@ -0,0 +1,55 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* @(#)ca_test_main.c Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: Jeff Hill + * Date: 21JAN2000 + */ +#include +#include + +#include "ca_test.h" +#include "dbDefs.h" + +int main(int argc, char **argv) +{ + + /* + * print error and return if arguments are invalid + */ + if(argc < 2 || argc > 3){ + printf("usage: %s [optional value to be written]\n", argv[0]); + printf("the following arguments were received\n"); + while(argc>0) { + printf("%s\n",argv[0]); + argv++; argc--; + } + return -1; + } + + + /* + * check for supplied value + */ + if(argc == 2){ + return ca_test(argv[1], NULL); + } + else if(argc == 3){ + char *pt; + + /* strip leading and trailing quotes*/ + if(argv[2][1]=='"') argv[2]++; + if( (pt=strchr(argv[2],'"')) ) *pt = 0; + return ca_test(argv[1], argv[2]); + } + else{ + return -1; + } + +} diff --git a/src/util/iocLogServer.c b/src/util/iocLogServer.c new file mode 100644 index 000000000..dd2a14679 --- /dev/null +++ b/src/util/iocLogServer.c @@ -0,0 +1,1004 @@ +/*************************************************************************\ +* 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. +\*************************************************************************/ +/* iocLogServer.c */ +/* base/src/util Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd */ + +/* + * archive logMsg() from several IOC's to a common rotating file + * + * + * Author: Jeffrey O. Hill + * Date: 080791 + */ + +#include +#include +#include +#include +#include +#include + +#ifdef UNIX +#include +#include +#endif + +#include "dbDefs.h" +#include "epicsAssert.h" +#include "fdmgr.h" +#include "envDefs.h" +#include "osiSock.h" +#include "epicsStdio.h" + +static unsigned short ioc_log_port; +static long ioc_log_file_limit; +static char ioc_log_file_name[256]; +static char ioc_log_file_command[256]; + + +struct iocLogClient { + int insock; + struct ioc_log_server *pserver; + size_t nChar; + char recvbuf[1024]; + char name[32]; + char ascii_time[32]; +}; + +struct ioc_log_server { + char outfile[256]; + long filePos; + FILE *poutfile; + void *pfdctx; + SOCKET sock; + long max_file_size; +}; + +#define IOCLS_ERROR (-1) +#define IOCLS_OK 0 + +static void acceptNewClient (void *pParam); +static void readFromClient(void *pParam); +static void logTime (struct iocLogClient *pclient); +static int getConfig(void); +static int openLogFile(struct ioc_log_server *pserver); +static void handleLogFileError(void); +static void envFailureNotify(const ENV_PARAM *pparam); +static void freeLogClient(struct iocLogClient *pclient); +static void writeMessagesToLog (struct iocLogClient *pclient); + +#ifdef UNIX +static int setupSIGHUP(struct ioc_log_server *); +static void sighupHandler(int); +static void serviceSighupRequest(void *pParam); +static int getDirectory(void); +static int sighupPipe[2]; +#endif + + + +/* + * + * main() + * + */ +int main(void) +{ + struct sockaddr_in serverAddr; /* server's address */ + struct timeval timeout; + int status; + struct ioc_log_server *pserver; + + osiSockIoctl_t optval; + + status = getConfig(); + if(status<0){ + fprintf(stderr, "iocLogServer: EPICS environment underspecified\n"); + fprintf(stderr, "iocLogServer: failed to initialize\n"); + return IOCLS_ERROR; + } + + pserver = (struct ioc_log_server *) + calloc(1, sizeof *pserver); + if(!pserver){ + fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); + return IOCLS_ERROR; + } + + pserver->pfdctx = (void *) fdmgr_init(); + if(!pserver->pfdctx){ + fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); + return IOCLS_ERROR; + } + + /* + * Open the socket. Use ARPA Internet address format and stream + * sockets. Format described in . + */ + pserver->sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0); + if (pserver->sock==INVALID_SOCKET) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: sock create err: %s\n", sockErrBuf); + return IOCLS_ERROR; + } + + epicsSocketEnableAddressReuseDuringTimeWaitState ( pserver->sock ); + + /* Zero the sock_addr structure */ + memset((void *)&serverAddr, 0, sizeof serverAddr); + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(ioc_log_port); + + /* get server's Internet address */ + status = bind ( pserver->sock, + (struct sockaddr *)&serverAddr, + sizeof (serverAddr) ); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: bind err: %s\n", sockErrBuf ); + fprintf (stderr, + "iocLogServer: a server is already installed on port %u?\n", + (unsigned)ioc_log_port); + return IOCLS_ERROR; + } + + /* listen and accept new connections */ + status = listen(pserver->sock, 10); + if (status<0) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: listen err %s\n", sockErrBuf); + return IOCLS_ERROR; + } + + /* + * Set non blocking IO + * to prevent dead locks + */ + optval = TRUE; + status = socket_ioctl( + pserver->sock, + FIONBIO, + &optval); + if(status<0){ + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf); + return IOCLS_ERROR; + } + +# ifdef UNIX + status = setupSIGHUP(pserver); + if (status<0) { + return IOCLS_ERROR; + } +# endif + + status = openLogFile(pserver); + if (status<0) { + fprintf(stderr, + "File access problems to `%s' because `%s'\n", + ioc_log_file_name, + strerror(errno)); + return IOCLS_ERROR; + } + + status = fdmgr_add_callback( + pserver->pfdctx, + pserver->sock, + fdi_read, + acceptNewClient, + pserver); + if(status<0){ + fprintf(stderr, + "iocLogServer: failed to add read callback\n"); + return IOCLS_ERROR; + } + + + while(TRUE){ + timeout.tv_sec = 60; /* 1 min */ + timeout.tv_usec = 0; + fdmgr_pend_event(pserver->pfdctx, &timeout); + fflush(pserver->poutfile); + } +} + +/* + * seekLatestLine (struct ioc_log_server *pserver) + */ +static int seekLatestLine (struct ioc_log_server *pserver) +{ + static const time_t invalidTime = (time_t) -1; + time_t theLatestTime = invalidTime; + long latestFilePos = -1; + int status; + + /* + * start at the beginning + */ + rewind (pserver->poutfile); + + while (1) { + struct tm theDate; + int convertStatus; + char month[16]; + + /* + * find the line in the file with the latest date + * + * this assumes ctime() produces dates of the form: + * DayName MonthName dayNum 24hourHourNum:minNum:secNum yearNum + */ + convertStatus = fscanf ( + pserver->poutfile, " %*s %*s %15s %d %d:%d:%d %d %*[^\n] ", + month, &theDate.tm_mday, &theDate.tm_hour, + &theDate.tm_min, &theDate.tm_sec, &theDate.tm_year); + if (convertStatus==6) { + static const char *pMonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + static const unsigned nMonths = sizeof(pMonths)/sizeof(pMonths[0]); + time_t lineTime = (time_t) -1; + unsigned iMonth; + + for (iMonth=0; iMonthtm_epoch_year) { + theDate.tm_year -= tm_epoch_year; + theDate.tm_isdst = -1; /* dont know */ + lineTime = mktime (&theDate); + if ( lineTime != invalidTime ) { + if (theLatestTime == invalidTime || + difftime(lineTime, theLatestTime)>=0) { + latestFilePos = ftell (pserver->poutfile); + theLatestTime = lineTime; + } + } + else { + char date[128]; + size_t nChar; + nChar = strftime (date, sizeof(date), "%a %b %d %H:%M:%S %Y\n", &theDate); + if (nChar>0) { + fprintf (stderr, "iocLogServer: strange date in log file: %s\n", date); + } + else { + fprintf (stderr, "iocLogServer: strange date in log file\n"); + } + } + } + else { + fprintf (stderr, "iocLogServer: strange year in log file: %d\n", theDate.tm_year); + } + } + else { + fprintf (stderr, "iocLogServer: strange month in log file: %s\n", month); + } + } + else { + char c = fgetc (pserver->poutfile); + + /* + * bypass the line if it does not match the expected format + */ + while ( c!=EOF && c!='\n' ) { + c = fgetc (pserver->poutfile); + } + + if (c==EOF) { + break; + } + } + } + + /* + * move to the proper location in the file + */ + if (latestFilePos != -1) { + status = fseek (pserver->poutfile, latestFilePos, SEEK_SET); + if (status!=IOCLS_OK) { + fclose (pserver->poutfile); + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + } + else { + status = fseek (pserver->poutfile, 0L, SEEK_END); + if (status!=IOCLS_OK) { + fclose (pserver->poutfile); + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + } + + pserver->filePos = ftell (pserver->poutfile); + + if (theLatestTime==invalidTime) { + if (pserver->filePos!=0) { + fprintf (stderr, "iocLogServer: **** Warning ****\n"); + fprintf (stderr, "iocLogServer: no recognizable dates in \"%s\"\n", + ioc_log_file_name); + fprintf (stderr, "iocLogServer: logging at end of file\n"); + } + } + + return IOCLS_OK; +} + + +/* + * openLogFile() + * + */ +static int openLogFile (struct ioc_log_server *pserver) +{ + enum TF_RETURN ret; + + if (ioc_log_file_limit==0u) { + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + + if (pserver->poutfile && pserver->poutfile != stderr){ + fclose (pserver->poutfile); + pserver->poutfile = NULL; + } + + pserver->poutfile = fopen(ioc_log_file_name, "r+"); + if (pserver->poutfile) { + fclose (pserver->poutfile); + pserver->poutfile = NULL; + ret = truncateFile (ioc_log_file_name, ioc_log_file_limit); + if (ret==TF_ERROR) { + return IOCLS_ERROR; + } + pserver->poutfile = fopen(ioc_log_file_name, "r+"); + } + else { + pserver->poutfile = fopen(ioc_log_file_name, "w"); + } + + if (!pserver->poutfile) { + pserver->poutfile = stderr; + return IOCLS_ERROR; + } + strcpy (pserver->outfile, ioc_log_file_name); + pserver->max_file_size = ioc_log_file_limit; + + return seekLatestLine (pserver); +} + + +/* + * handleLogFileError() + * + */ +static void handleLogFileError(void) +{ + fprintf(stderr, + "iocLogServer: log file access problem (errno=%s)\n", + strerror(errno)); + exit(IOCLS_ERROR); +} + + + +/* + * acceptNewClient() + * + */ +static void acceptNewClient ( void *pParam ) +{ + struct ioc_log_server *pserver = (struct ioc_log_server *) pParam; + struct iocLogClient *pclient; + osiSocklen_t addrSize; + struct sockaddr_in addr; + int status; + osiSockIoctl_t optval; + + pclient = ( struct iocLogClient * ) malloc ( sizeof ( *pclient ) ); + if ( ! pclient ) { + return; + } + + addrSize = sizeof ( addr ); + pclient->insock = epicsSocketAccept ( pserver->sock, (struct sockaddr *)&addr, &addrSize ); + if ( pclient->insock==INVALID_SOCKET || addrSize < sizeof (addr) ) { + static unsigned acceptErrCount; + static int lastErrno; + int thisErrno; + + free ( pclient ); + if ( SOCKERRNO == SOCK_EWOULDBLOCK || SOCKERRNO == SOCK_EINTR ) { + return; + } + + thisErrno = SOCKERRNO; + if ( acceptErrCount % 1000 || lastErrno != thisErrno ) { + fprintf ( stderr, "Accept Error %d\n", SOCKERRNO ); + } + acceptErrCount++; + lastErrno = thisErrno; + + return; + } + + /* + * Set non blocking IO + * to prevent dead locks + */ + optval = TRUE; + status = socket_ioctl( + pclient->insock, + FIONBIO, + &optval); + if(status<0){ + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, "%s:%d ioctl FBIO client er %s\n", + __FILE__, __LINE__, sockErrBuf); + epicsSocketDestroy ( pclient->insock ); + free(pclient); + return; + } + + pclient->pserver = pserver; + pclient->nChar = 0u; + + ipAddrToA (&addr, pclient->name, sizeof(pclient->name)); + + logTime(pclient); + +#if 0 + status = fprintf( + pclient->pserver->poutfile, + "%s %s ----- Client Connect -----\n", + pclient->name, + pclient->ascii_time); + if(status<0){ + handleLogFileError(); + } +#endif + + /* + * turn on KEEPALIVE so if the client crashes + * this task will find out and exit + */ + { + long true = 1; + + status = setsockopt( + pclient->insock, + SOL_SOCKET, + SO_KEEPALIVE, + (char *)&true, + sizeof(true) ); + if(status<0){ + fprintf(stderr, "Keepalive option set failed\n"); + } + } + + status = shutdown(pclient->insock, SHUT_WR); + if(status<0){ + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf (stderr, "%s:%d shutdown err %s\n", __FILE__, __LINE__, + sockErrBuf); + epicsSocketDestroy ( pclient->insock ); + free(pclient); + + return; + } + + status = fdmgr_add_callback( + pserver->pfdctx, + pclient->insock, + fdi_read, + readFromClient, + pclient); + if (status<0) { + epicsSocketDestroy ( pclient->insock ); + free(pclient); + fprintf(stderr, "%s:%d client fdmgr_add_callback() failed\n", + __FILE__, __LINE__); + return; + } +} + + + +/* + * readFromClient() + * + */ +#define NITEMS 1 + +static void readFromClient(void *pParam) +{ + struct iocLogClient *pclient = (struct iocLogClient *)pParam; + int recvLength; + int size; + + logTime(pclient); + + size = (int) (sizeof(pclient->recvbuf) - pclient->nChar); + recvLength = recv(pclient->insock, + &pclient->recvbuf[pclient->nChar], + size, + 0); + if (recvLength <= 0) { + if (recvLength<0) { + int errnoCpy = SOCKERRNO; + if (errnoCpy==SOCK_EWOULDBLOCK || errnoCpy==SOCK_EINTR) { + return; + } + if (errnoCpy != SOCK_ECONNRESET && + errnoCpy != SOCK_ECONNABORTED && + errnoCpy != SOCK_EPIPE && + errnoCpy != SOCK_ETIMEDOUT + ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf(stderr, + "%s:%d socket=%d size=%d read error=%s\n", + __FILE__, __LINE__, pclient->insock, + size, sockErrBuf); + } + } + /* + * disconnect + */ + freeLogClient (pclient); + return; + } + + pclient->nChar += (size_t) recvLength; + + writeMessagesToLog (pclient); +} + +/* + * writeMessagesToLog() + */ +static void writeMessagesToLog (struct iocLogClient *pclient) +{ + int status; + size_t lineIndex = 0; + + while (TRUE) { + size_t nchar; + size_t nTotChar; + size_t crIndex; + int ntci; + + if ( lineIndex >= pclient->nChar ) { + pclient->nChar = 0u; + break; + } + + /* + * find the first carrage return and create + * an entry in the log for the message associated + * with it. If a carrage return does not exist and + * the buffer isnt full then move the partial message + * to the front of the buffer and wait for a carrage + * return to arrive. If the buffer is full and there + * is no carrage return then force the message out and + * insert an artificial carrage return. + */ + nchar = pclient->nChar - lineIndex; + for ( crIndex = lineIndex; crIndex < pclient->nChar; crIndex++ ) { + if ( pclient->recvbuf[crIndex] == '\n' ) { + break; + } + } + if ( crIndex < pclient->nChar ) { + nchar = crIndex - lineIndex; + } + else { + nchar = pclient->nChar - lineIndex; + if ( nchar < sizeof ( pclient->recvbuf ) ) { + if ( lineIndex != 0 ) { + pclient->nChar = nchar; + memmove ( pclient->recvbuf, + & pclient->recvbuf[lineIndex], nchar); + } + break; + } + } + + /* + * reset the file pointer if we hit the end of the file + */ + nTotChar = strlen(pclient->name) + + strlen(pclient->ascii_time) + nchar + 3u; + assert (nTotChar <= INT_MAX); + ntci = (int) nTotChar; + if ( pclient->pserver->filePos+ntci >= pclient->pserver->max_file_size ) { + if ( pclient->pserver->max_file_size >= pclient->pserver->filePos ) { + unsigned nPadChar; + /* + * this gets rid of leftover junk at the end of the file + */ + nPadChar = pclient->pserver->max_file_size - pclient->pserver->filePos; + while (nPadChar--) { + status = putc ( ' ', pclient->pserver->poutfile ); + if ( status == EOF ) { + handleLogFileError(); + } + } + } + +# ifdef DEBUG + fprintf ( stderr, + "ioc log server: resetting the file pointer\n" ); +# endif + fflush ( pclient->pserver->poutfile ); + rewind ( pclient->pserver->poutfile ); + pclient->pserver->filePos = ftell ( pclient->pserver->poutfile ); + } + + /* + * NOTE: !! change format string here then must + * change nTotChar calc above !! + */ + assert (ncharpserver->poutfile, + "%s %s %.*s\n", + pclient->name, + pclient->ascii_time, + (int) nchar, + &pclient->recvbuf[lineIndex]); + if (status<0) { + handleLogFileError(); + } + else { + if (status != ntci) { + fprintf(stderr, "iocLogServer: didnt calculate number of characters correctly?\n"); + } + pclient->pserver->filePos += status; + } + lineIndex += nchar+1u; + } +} + + +/* + * freeLogClient () + */ +static void freeLogClient(struct iocLogClient *pclient) +{ + int status; + +# ifdef DEBUG + if(length == 0){ + fprintf(stderr, "iocLogServer: nil message disconnect\n"); + } +# endif + + /* + * flush any left overs + */ + if (pclient->nChar) { + /* + * this forces a flush + */ + if (pclient->nCharrecvbuf)) { + pclient->recvbuf[pclient->nChar] = '\n'; + } + writeMessagesToLog (pclient); + } + + status = fdmgr_clear_callback( + pclient->pserver->pfdctx, + pclient->insock, + fdi_read); + if (status!=IOCLS_OK) { + fprintf(stderr, "%s:%d fdmgr_clear_callback() failed\n", + __FILE__, __LINE__); + } + + epicsSocketDestroy ( pclient->insock ); + + free (pclient); + + return; +} + + +/* + * + * logTime() + * + */ +static void logTime(struct iocLogClient *pclient) +{ + time_t sec; + char *pcr; + char *pTimeString; + + sec = time (NULL); + pTimeString = ctime (&sec); + strncpy (pclient->ascii_time, + pTimeString, + sizeof (pclient->ascii_time) ); + pclient->ascii_time[sizeof(pclient->ascii_time)-1] = '\0'; + pcr = strchr(pclient->ascii_time, '\n'); + if (pcr) { + *pcr = '\0'; + } +} + + +/* + * + * getConfig() + * Get Server Configuration + * + * + */ +static int getConfig(void) +{ + int status; + char *pstring; + long param; + + status = envGetLongConfigParam( + &EPICS_IOC_LOG_PORT, + ¶m); + if(status>=0){ + ioc_log_port = (unsigned short) param; + } + else { + ioc_log_port = 7004U; + } + + status = envGetLongConfigParam( + &EPICS_IOC_LOG_FILE_LIMIT, + &ioc_log_file_limit); + if(status>=0){ + if (ioc_log_file_limit<=0) { + envFailureNotify (&EPICS_IOC_LOG_FILE_LIMIT); + return IOCLS_ERROR; + } + } + else { + ioc_log_file_limit = 10000; + } + + pstring = envGetConfigParam( + &EPICS_IOC_LOG_FILE_NAME, + sizeof ioc_log_file_name, + ioc_log_file_name); + if(pstring == NULL){ + envFailureNotify(&EPICS_IOC_LOG_FILE_NAME); + return IOCLS_ERROR; + } + + /* + * its ok to not specify the IOC_LOG_FILE_COMMAND + */ + pstring = envGetConfigParam( + &EPICS_IOC_LOG_FILE_COMMAND, + sizeof ioc_log_file_command, + ioc_log_file_command); + return IOCLS_OK; +} + + + +/* + * + * failureNotify() + * + * + */ +static void envFailureNotify(const ENV_PARAM *pparam) +{ + fprintf(stderr, + "iocLogServer: EPICS environment variable `%s' undefined\n", + pparam->name); +} + + + +#ifdef UNIX +static int setupSIGHUP(struct ioc_log_server *pserver) +{ + int status; + struct sigaction sigact; + + status = getDirectory(); + if (status<0){ + fprintf(stderr, "iocLogServer: failed to determine log file " + "directory\n"); + return IOCLS_ERROR; + } + + /* + * Set up SIGHUP handler. SIGHUP will cause the log file to be + * closed and re-opened, possibly with a different name. + */ + sigact.sa_handler = sighupHandler; + sigemptyset (&sigact.sa_mask); + sigact.sa_flags = 0; + if (sigaction(SIGHUP, &sigact, NULL)){ + fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); + return IOCLS_ERROR; + } + + status = pipe(sighupPipe); + if(status<0){ + fprintf(stderr, + "iocLogServer: failed to create pipe because `%s'\n", + strerror(errno)); + return IOCLS_ERROR; + } + + status = fdmgr_add_callback( + pserver->pfdctx, + sighupPipe[0], + fdi_read, + serviceSighupRequest, + pserver); + if(status<0){ + fprintf(stderr, + "iocLogServer: failed to add SIGHUP callback\n"); + return IOCLS_ERROR; + } + return IOCLS_OK; +} + +/* + * + * sighupHandler() + * + * + */ +static void sighupHandler(int signo) +{ + (void) write(sighupPipe[1], "SIGHUP\n", 7); +} + + + +/* + * serviceSighupRequest() + * + */ +static void serviceSighupRequest(void *pParam) +{ + struct ioc_log_server *pserver = (struct ioc_log_server *)pParam; + char buff[256]; + int status; + + /* + * Read and discard message from pipe. + */ + (void) read(sighupPipe[0], buff, sizeof buff); + + /* + * Determine new log file name. + */ + status = getDirectory(); + if (status<0){ + fprintf(stderr, "iocLogServer: failed to determine new log " + "file name\n"); + return; + } + + /* + * If it's changed, open the new file. + */ + if (strcmp(ioc_log_file_name, pserver->outfile) == 0) { + fprintf(stderr, + "iocLogServer: log file name unchanged; not re-opened\n"); + } + else { + status = openLogFile(pserver); + if(status<0){ + fprintf(stderr, + "File access problems to `%s' because `%s'\n", + ioc_log_file_name, + strerror(errno)); + strcpy(ioc_log_file_name, pserver->outfile); + status = openLogFile(pserver); + if(status<0){ + fprintf(stderr, + "File access problems to `%s' because `%s'\n", + ioc_log_file_name, + strerror(errno)); + return; + } + else { + fprintf(stderr, + "iocLogServer: re-opened old log file %s\n", + ioc_log_file_name); + } + } + else { + fprintf(stderr, + "iocLogServer: opened new log file %s\n", + ioc_log_file_name); + } + } +} + + + +/* + * + * getDirectory() + * + * + */ +static int getDirectory(void) +{ + FILE *pipe; + char dir[256]; + int i; + + if (ioc_log_file_command[0] != '\0') { + + /* + * Use popen() to execute command and grab output. + */ + pipe = popen(ioc_log_file_command, "r"); + if (pipe == NULL) { + fprintf(stderr, + "Problem executing `%s' because `%s'\n", + ioc_log_file_command, + strerror(errno)); + return IOCLS_ERROR; + } + if (fgets(dir, sizeof(dir), pipe) == NULL) { + fprintf(stderr, + "Problem reading o/p from `%s' because `%s'\n", + ioc_log_file_command, + strerror(errno)); + return IOCLS_ERROR; + } + (void) pclose(pipe); + + /* + * Terminate output at first newline and discard trailing + * slash character if present.. + */ + for (i=0; dir[i] != '\n' && dir[i] != '\0'; i++) + ; + dir[i] = '\0'; + + i = strlen(dir); + if (i > 1 && dir[i-1] == '/') dir[i-1] = '\0'; + + /* + * Use output as directory part of file name. + */ + if (dir[0] != '\0') { + char *name = ioc_log_file_name; + char *slash = strrchr(ioc_log_file_name, '/'); + char temp[256]; + + if (slash != NULL) name = slash + 1; + strcpy(temp,name); + sprintf(ioc_log_file_name,"%s/%s",dir,temp); + } + } + return IOCLS_OK; +} +#endif diff --git a/src/util/rc2.caRepeater b/src/util/rc2.caRepeater new file mode 100644 index 000000000..4db37b49a --- /dev/null +++ b/src/util/rc2.caRepeater @@ -0,0 +1,28 @@ +#!/bin/sh +# +# System-V init script for the EPICS CA Repeater. +# + +INSTALL_BIN=:INSTALL_BIN: + +# To change the default values for the EPICS environment parameters, +# uncomment and modify the relevant lines below. These are the only +# EPICS environment variables that the CA Repeater makes use of. + +# EPICS_CA_REPEATER_PORT="5065" export EPICS_CA_REPEATER_PORT + +if [ $1 = "start" ]; then + if [ -x $INSTALL_BIN/caRepeater ]; then + echo "Starting EPICS CA Repeater " + $INSTALL_BIN/caRepeater & + fi +else + if [ $1 = "stop" ]; then + pid=`ps -e | sed -ne '/caRepeat/s/^ *\([1-9][0-9]*\).*$/\1/p'` + if [ "${pid}" != "" ]; then + echo "Stopping EPICS CA Repeater " + kill ${pid} + fi + fi +fi + diff --git a/src/util/rc2.logServer b/src/util/rc2.logServer new file mode 100644 index 000000000..407227d91 --- /dev/null +++ b/src/util/rc2.logServer @@ -0,0 +1,29 @@ +#!/bin/sh +# +# System-V init script for the EPICS IOC Log Server. +# + +INSTALL_BIN=:INSTALL_BIN: + +# To change the default values for the EPICS Environment parameters, +# uncomment and modify the relevant lines below. + +# EPICS_IOC_LOG_PORT="6500" export EPICS_IOC_LOG_PORT +# EPICS_IOC_LOG_FILE_NAME="/path/to/iocLog" export EPICS_IOC_LOG_FILE_NAME +# EPICS_IOC_LOG_FILE_LIMIT="1000000" export EPICS_IOC_LOG_FILE_LIMIT + +if [ $1 = "start" ]; then + if [ -x $INSTALL_BIN/iocLogServer ]; then + echo "Starting EPICS Log Server " + $INSTALL_BIN/iocLogServer & + fi +else + if [ $1 = "stop" ]; then + pid=`ps -e | sed -ne '/iocLogSe/s/^ *\([1-9][0-9]*\).*$/\1/p'` + if [ "${pid}" != "" ]; then + echo "Stopping EPICS Log Server " + kill ${pid} + fi + fi +fi + diff --git a/src/vxWorks/Makefile b/src/vxWorks/Makefile new file mode 100644 index 000000000..e0c390613 --- /dev/null +++ b/src/vxWorks/Makefile @@ -0,0 +1,33 @@ +#************************************************************************* +# 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 + +iocCore_OBJS += $(INSTALL_BIN)/ComLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/caLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/dbStaticIocLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/registryIocLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/dbIocLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/asIocLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/miscIocLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/dbtoolsIocLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/rsrvIocLibrary.o +iocCore_OBJS += $(INSTALL_BIN)/vxComLibrary.o +iocCore_SRCS += registerRecordDeviceDriver.c + +ifeq ($(strip $(COMPAT_313)),YES) +OBJLIB_vxWorks = iocCore +OBJLIB_OBJS += $(iocCore_OBJS) +OBJLIB_SRCS += registerRecordDeviceDriver.c +endif + +include $(TOP)/configure/RULES + diff --git a/src/vxWorks/registerRecordDeviceDriver.c b/src/vxWorks/registerRecordDeviceDriver.c new file mode 100644 index 000000000..0fdd79c17 --- /dev/null +++ b/src/vxWorks/registerRecordDeviceDriver.c @@ -0,0 +1,118 @@ +/*************************************************************************\ +* 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 +#include +#include +#include +#include + +#include "dbStaticLib.h" +#include "recSup.h" +#include "devSup.h" +#include "drvSup.h" +#include "cantProceed.h" +#include "epicsFindSymbol.h" +#define epicsExportSharedSymbols +#include "registry.h" +#include "registryRecordType.h" +#include "registryDeviceSupport.h" +#include "registryDriverSupport.h" + +static void *locateAddrName(char *name) +{ + char pname[100]; + void *addr; + + addr = epicsFindSymbol(name); + if(addr) return(addr); + strcpy(pname,"p"); + strcat(pname,name); + addr = epicsFindSymbol(pname); + if(addr) return((*(void **)addr)); + return(0); +} + +int registerRecordDeviceDriver(struct dbBase *pdbbase) +{ + dbRecordType *pdbRecordType; + recordTypeLocation rtl; + recordTypeLocation *precordTypeLocation; + char name[100]; + drvSup *pdrvSup; + + if(!pdbbase) { + printf("pdbbase not specified\n"); + return(0); + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + computeSizeOffset sizeOffset; + if(registryRecordTypeFind(pdbRecordType->name)) continue; + strcpy(name,pdbRecordType->name); + strcat(name,"RSET"); + rtl.prset = (rset *)locateAddrName(name); + if(!rtl.prset) { + printf("RSET %s not found\n",name); + continue; + } + strcpy(name,pdbRecordType->name); + strcat(name,"RecordSizeOffset"); + rtl.sizeOffset = (computeSizeOffset)locateAddrName(name); + if(!rtl.sizeOffset) { + printf("SizeOfset %s not found\n",name); + continue; + } + precordTypeLocation = callocMustSucceed(1,sizeof(recordTypeLocation), + "registerRecordDeviceDriver"); + precordTypeLocation->prset = rtl.prset; + precordTypeLocation->sizeOffset = rtl.sizeOffset; + if(!registryRecordTypeAdd(pdbRecordType->name,precordTypeLocation)) { + errlogPrintf("registryRecordTypeAdd failed for %s\n", + pdbRecordType->name); + continue; + } + sizeOffset = precordTypeLocation->sizeOffset; + sizeOffset(pdbRecordType); + } + for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); + pdbRecordType; + pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { + devSup *pdevSup; + for(pdevSup = (devSup *)ellFirst(&pdbRecordType->devList); + pdevSup; + pdevSup = (devSup *)ellNext(&pdevSup->node)) { + dset *pdset = (dset *)locateAddrName(pdevSup->name); + if(!pdset) { + printf("DSET %s not found\n",pdevSup->name); + continue; + } + if(!registryDeviceSupportAdd(pdevSup->name,pdset)) { + errlogPrintf("registryRecordTypeAdd failed for %s\n", + pdevSup->name); + } + } + } + for(pdrvSup = (drvSup *)ellFirst(&pdbbase->drvList); + pdrvSup; + pdrvSup = (drvSup *)ellNext(&pdrvSup->node)) { + drvet *pdrvet = (drvet *)locateAddrName(pdrvSup->name); + if(!pdrvet) { + printf("drvet %s not found\n",pdrvSup->name); + continue; + } + if(!registryDriverSupportAdd(pdrvSup->name,pdrvet)) { + errlogPrintf("registryRecordTypeAdd failed for %s\n", + pdrvSup->name); + } + } + return(0); +} + diff --git a/startup/EpicsHostArch b/startup/EpicsHostArch new file mode 100755 index 000000000..ae25ea7cd --- /dev/null +++ b/startup/EpicsHostArch @@ -0,0 +1,82 @@ +#!/bin/sh +#************************************************************************* +# 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. +#************************************************************************* +# +# EpicsHostArch - returns the Epics host architecture suitable +# for assigning to the EPICS_HOST_ARCH variable + +if [ "x${1}" != "x" ] +then + suffix="-"${1} +else + suffix="" +fi + +sysname=`uname` + +case $sysname in + Linux ) + os=linux + cpu=`uname -m` + case $cpu in i386 | i486 | i586 | i686 ) + cpu=x86 + ;; + esac + if [ ${cpu} = "x86_64" ]; then + cpu=x86_64 + fi + echo ${os}-${cpu}${suffix} + ;; + Darwin ) + os=darwin + cpu=`uname -m` + case "$cpu" in + "Power Macintosh") cpu=ppc ;; + "i386") cpu=x86 ;; + esac + echo ${os}-${cpu}${suffix} + ;; + SunOS ) + version=`uname -r | sed '1s/^\([0-9]*\).*$/\1/'` + if [ ${version} -ge 5 ]; then + os=solaris + else + os=sun4 + fi + cpu=`uname -m` + case $cpu in + sun4*) + cpu=sparc + ;; + i86pc) + cpu=x86 + ;; + esac + echo ${os}-${cpu}${suffix} + ;; + * ) + sysname=`uname -o` + case $sysname in + Cygwin ) + os=cygwin + cpu=`uname -m` + case $cpu in i386 | i486 | i586 | i686 ) + cpu=x86 + ;; + esac + echo ${os}-${cpu}${suffix} + ;; + * ) + echo unsupported + ;; + esac + ;; +esac + diff --git a/startup/EpicsHostArch.pl b/startup/EpicsHostArch.pl new file mode 100755 index 000000000..9e24882cb --- /dev/null +++ b/startup/EpicsHostArch.pl @@ -0,0 +1,42 @@ +eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- + if $running_under_some_shell; # EpicsHostArch.pl +#************************************************************************* +# 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 is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# Revision-Id: anj@aps.anl.gov-20101014190140-s9gxgtnoyhbck6id +# Returns the Epics host architecture suitable +# for assigning to the EPICS_HOST_ARCH variable + +use Config; +use POSIX; + +$suffix=""; +$suffix="-".$ARGV[0] if ($ARGV[0] ne ""); + +$EpicsHostArch = GetEpicsHostArch(); +print "$EpicsHostArch$suffix"; + +sub GetEpicsHostArch { # no args + $arch=$Config{'archname'}; + if ($arch =~ /sun4-solaris/) { return "solaris-sparc"; + } elsif ($arch =~ m/i86pc-solaris/) { return "solaris-x86"; + } elsif ($arch =~ m/i[3-6]86-linux/) { return "linux-x86"; + } elsif ($arch =~ m/x86_64-linux/) { return "linux-x86_64"; + } elsif ($arch =~ m/MSWin32-x86/) { return "win32-x86"; + } elsif ($arch =~ m/cygwin/) { return "cygwin-x86"; + } elsif ($arch =~ m/darwin/) { + my($kernel, $hostname, $release, $version, $cpu) = POSIX::uname(); + if ($cpu =~ m/Power Macintosh/) { return "darwin-ppc"; } + elsif ($cpu =~ m/i386/) { return "darwin-x86"; } + else { return "unsupported"; } + } else { return "unsupported"; } +} + +#EOF EpicsHostArch.pl + diff --git a/startup/Site.cshrc b/startup/Site.cshrc new file mode 100755 index 000000000..23453cb91 --- /dev/null +++ b/startup/Site.cshrc @@ -0,0 +1,118 @@ +#!/bin/csh -f +#************************************************************************* +# 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. +#************************************************************************* +# Site-specific EPICS environment settings +# +# sites should modify these definitions + +# Location of epics base +if ( ! $?EPICS_BASE ) then + set EPICS_BASE=/usr/local/epics/base +endif + +# Location of epics extensions +if ( ! $?EPICS_EXTENSIONS ) then + setenv EPICS_EXTENSIONS /usr/local/epics/extensions +endif + +# Postscript printer definition needed by some extensions (eg medm, dp, dm, ...) +if ( ! $?PSPRINTTER ) then + setenv PSPRINTER lp +endif + +# Needed only by medm extension +#setenv EPICS_DISPLAY_PATH +# Needed only by medm extension +setenv BROWSER firefox + +# Needed only by orbitscreen extension +if ( ! $?ORBITSCREENHOME ) then + setenv ORBITSCREENHOME $EPICS_EXTENSIONS/src/orbitscreen +endif + +# Needed only by adt extension +if ( ! $?ADTHOME ) then + setenv ADTHOME /usr/local/oag/apps/src/appconfig/adt + echo $ADTHOME +endif + +# Needed only by ar extension (archiver) +setenv EPICS_AR_PORT 7002 + +# Needed for java extensions +if ( $?CLASSPATH ) then + setenv CLASSPATH "${CLASSPATH}:${EPICS_EXTENSIONS}/javalib" +else + setenv CLASSPATH "${EPICS_EXTENSIONS}/javalib" +endif + +# Allow private versions of extensions without a bin subdir +if ( $?EPICS_EXTENSIONS_PVT ) then + set path = ( $path $EPICS_EXTENSIONS_PVT) +endif + +################################################################## + +# Start of set R3.14 environment variables + +setenv EPICS_HOST_ARCH `$EPICS_BASE/startup/EpicsHostArch.pl` + +# Allow private versions of base +if ( $?EPICS_BASE_PVT ) then + if ( -e $EPICS_BASE_PVT/bin/$EPICS_HOST_ARCH ) then + set path = ( $path $EPICS_BASE_PVT/bin/$EPICS_HOST_ARCH) + endif +endif + +# Allow private versions of extensions +if ( $?EPICS_EXTENSIONS_PVT ) then + if ( -e $EPICS_EXTENSIONS_PVT/bin/$EPICS_HOST_ARCH ) then + set path = ( $path $EPICS_EXTENSIONS_PVT/bin/$EPICS_HOST_ARCH) + endif +endif +set path = ( $path $EPICS_EXTENSIONS/bin/$EPICS_HOST_ARCH ) + +# End of set R3.14 environment variables +################################################################## + + +## Start of set pre R3.14 environment variables +# +## Time service: +## EPICS_TS_MIN_WEST the local time difference from GMT. +#setenv EPICS_TS_MIN_WEST 360 +# +#if ( -e /usr/local/etc/setup/HostArch.pl ) then +# setenv HOST_ARCH `/usr/local/etc/setup/HostArch.pl` +#else +# setenv HOST_ARCH `/usr/local/epics/startup/HostArch.pl` +#endif +# +## Allow private versions of extensions +#if ( $?EPICS_EXTENSIONS_PVT ) then +# if ( -e $EPICS_EXTENSIONS_PVT/bin/$HOST_ARCH ) then +# set path = ( $path $EPICS_EXTENSIONS_PVT/bin/$HOST_ARCH) +# endif +# # Needed if shared extension libraries are built +# if ( -e $EPICS_EXTENSIONS_PVT/lib/$HOST_ARCH ) then +# if ( $?LD_LIBRARY_PATH ) then +# setenv LD_LIBRARY_PATH "${LD_LIBRARY_PATH}:${EPICS_EXTENSIONS_PVT}/lib/${HOST_ARCH}" +# else +# setenv LD_LIBRARY_PATH "${EPICS_EXTENSIONS_PVT}/lib/${HOST_ARCH}" +# endif +# endif +#endif +# +#set path = ( $path $EPICS_EXTENSIONS/bin/$HOST_ARCH ) +## Needed if shared extension libraries are built +#setenv LD_LIBRARY_PATH "${LD_LIBRARY_PATH}:${EPICS_EXTENSIONS}/lib/${HOST_ARCH}" + +# End of set pre R3.14 environment variables +################################################################## diff --git a/startup/Site.profile b/startup/Site.profile new file mode 100755 index 000000000..5cb9b0273 --- /dev/null +++ b/startup/Site.profile @@ -0,0 +1,118 @@ +#!/bin/sh +#************************************************************************* +# 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. +#************************************************************************* +# Site-specific EPICS environment settings +# +# sites should modify these definitions + +# Location of epics base +if [ -z "${MY_EPICS_BASE}" ] ; then + MY_EPICS_BASE=/usr/local/epics/base +fi + +# Location of epics extensions (medm, msi, etc.) +if [ -z "${EPICS_EXTENSIONS}" ] ; then + EPICS_EXTENSIONS=/usr/local/epics/extensions +fi + +# Postscript printer definition needed by some extensions (eg medm, dp, dm, ...) +if [ -z "${PSPRINTER}" ] ; then + export PSPRINTER=lp +fi + +#Needed only by the idl and ezcaIDL extensions. +#export EPICS_EXTENSIONS + +# Needed only by medm extension +#export EPICS_DISPLAY_PATH=/path/to/adl/files +export BROWSER=firefox + +# Needed only by orbitscreen extension +#if [ -z "${ORBITSCREENHOME}" ] ; then +# export "ORBITSCREENHOME=${EPICS_EXTENSIONS/src/orbitscreen}" +#fi + +# Needed only by adt extension +#if [ -z "${ADTHOME}" ] ; then +# ADTHOME= +# export ADTHOME +#fi + +# Needed only by ar extension (archiver) +#EPICS_AR_PORT=7002 +#export EPICS_AR_PORT + +# Needed for java extensions +if [ -z "${CLASSPATH}" ] ; then + CLASSPATH="${EPICS_EXTENSIONS}/javalib" +else + CLASSPATH="${CLASSPATH}:${EPICS_EXTENSIONS}/javalib" +fi +export CLASSPATH + +# Allow private versions of extensions without a bin subdir +if [ -n "${EPICS_EXTENSIONS_PVT}" ] ; then + PATH="${PATH}:${EPICS_EXTENSIONS_PVT}" +fi + +#--------------------------------------------------------------- +# Start of set R3.14 environment variables +# +EPICS_HOST_ARCH=`"${MY_EPICS_BASE}"/startup/EpicsHostArch.pl` +export EPICS_HOST_ARCH + +# Allow private versions of base +if [ -n "${EPICS_BASE_PVT}" ] ; then + if [ -d "${EPICS_BASE_PVT}/bin/${EPICS_HOST_ARCH}" ]; then + PATH="${PATH}:${EPICS_BASE_PVT}/bin/${EPICS_HOST_ARCH}" + fi +fi + +# Allow private versions of extensions +if [ -n "${EPICS_EXTENSIONS_PVT}" ] ; then + if [ -d "${EPICS_EXTENSIONS_PVT}/bin/${EPICS_HOST_ARCH}" ]; then + PATH="${PATH}:${EPICS_EXTENSIONS_PVT}/bin/${EPICS_HOST_ARCH}" + fi +fi +PATH="${PATH}:${EPICS_EXTENSIONS}/bin/${EPICS_HOST_ARCH}" + +# End of set R3.14 environment variables + +#--------------------------------------------------------------- +# +## Start of set pre R3.14 environment variables +# +## Time service: +## EPICS_TS_MIN_WEST the local time difference from GMT. +#EPICS_TS_MIN_WEST=360 +#export EPICS_TS_MIN_WEST +# +#HOST_ARCH=`"${MY_EPICS_BASE}"/startup/HostArch` +#export HOST_ARCH +# +## Allow private versions of base +#if [ -n "${EPICS_BASE_PVT}" ] ; then +# if [ -d "${EPICS_BASE_PVT}/bin/${HOST_ARCH}" ]; then +# PATH="${PATH}:${EPICS_BASE_PVT}/bin/${HOST_ARCH}" +# fi +#fi +# +## Allow private versions of extensions +#if [ -n "${EPICS_EXTENSIONS_PVT}" ] ; then +# if [ -d "${EPICS_EXTENSIONS_PVT}/bin/${HOST_ARCH}" ]; then +# PATH="${PATH}:${EPICS_EXTENSIONS_PVT}/bin/${HOST_ARCH}" +# fi +#fi +# +#PATH="${PATH}:${EPICS_EXTENSIONS}/lib/${HOST_ARCH}" +# +# End of set pre R3.14 environment variables + +#--------------------------------------------------------------- diff --git a/startup/cygwin.bat b/startup/cygwin.bat new file mode 100755 index 000000000..30a39ecd4 --- /dev/null +++ b/startup/cygwin.bat @@ -0,0 +1,89 @@ +@ECHO OFF +REM ************************************************************************* +REM Copyright (c) 2002 The University of Chicago, as Operator of Argonne +REM National Laboratory. +REM Copyright (c) 2002 The Regents of the University of California, as +REM Operator of Los Alamos National Laboratory. +REM EPICS BASE Versions 3.13.7 +REM and higher are distributed subject to a Software License Agreement found +REM in file LICENSE that is included with this distribution. +REM ************************************************************************* +REM Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +REM +REM Site-specific EPICS environment settings +REM +REM sites should modify these definitions + + +REM =================================================== +REM ====== REQUIRED ENVIRONMENT VARIABLES FOLLOW ====== +REM +REM --------------- WINDOWS --------------------------- +REM ----- WIN95 ----- +REM set PATH=C:\WINDOWS;C:\WINDOWS\COMMAND +REM ----- WINNT ----- +REM set PATH=C:\WINNT;C:\WINNT\SYSTEM32 +REM ----- WINXP ----- +set PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\system32\Wbem + +REM --------------- GNU tools ------------------------- +REM -- cygwin contains GNU make, perl, gcc, g++, vim, ... +REM -- Can be preceeded or replaced with paths to GNU make and perl +REM -- need grep from here NOT from cvs directory +REM -- some tools may need a tmp directory +set PATH=%PATH%;c:\cygwin\bin + +REM --------------- EPICS ----------------------------- +REM -- R3.14 requirements +set EPICS_HOST_ARCH=cygwin-x86 +set PATH=%PATH%;G:\epics\base\bin\%EPICS_HOST_ARCH% +set PATH=%PATH%;G:\epics\extensions\bin\%EPICS_HOST_ARCH% + +REM =================================================== +REM ====== OPTIONAL ENVIRONMENT VARIABLES FOLLOW ====== + +REM ---------------- EPICS tools ---------------------- +REM -- HOST_ARCH needed for Makefile.Host builds +set HOST_ARCH=cygwin32 + +REM --------------- GNU make flags -------------------- +REM set MAKEFLAGS=-w + +REM --------------- EPICS Channel Access -------------- +REM -- Uncomment and modify the following lines +REM -- to override the base/configure/CONFIG_ENV defaults +REM set EPICS_CA_ADDR_LIST=n.n.n.n n.n.n.n +REM set EPICS_CA_AUTO_ADDR_LIST=YES + +REM --------------- cygwin vim ------------------------ +REM -- HOME needed by vim to find _vimrc file. +REM set HOME=/home/%USERNAME% +REM -- VIM needed by vim to find help files. +REM set VIM=/usr/share/vim/vim61 + +REM --------------- remote cvs (use cygwin cvs) ------- +REM -- HOME needed by cvs for .cvsrc file (set in vim above) +REM set CVSROOT=:ext:%USERNAME%@venus.aps.anl.gov:/usr/local/epicsmgr/cvsroot +REM set CVS_RSH=/bin/ssh.exe + +REM --------------- JAVA ------------------------------ +REM -- Needed for java extensions +REM set PATH=%PATH%;C:\j2sdk1.4.1_01\bin +REM set CLASSPATH=G:\epics\extensions\javalib + +REM --------------- X11+Motif-------------------------- +REM -- Exceed or cygwin Xfree86 needed for Xwindows extensions +REM +REM -- Exceed ( Cygwin should preceed Exceed in path) +REM set PATH=%PATH%;C:\Exceed +REM ------ Exceed 2007 ------ +REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\12.00\Exceed\ +REM ------ Exceed 2008 ------ +REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\13.00\Exceed\ +REM -- +REM -- or +REM ----- cygwin Xfree86 ----- +REM set PATH=%PATH%;c:\cygwin\usr\X11R6\bin +REM set DISPLAY=localhost:0 + +REM =================================================== diff --git a/startup/win32.bat b/startup/win32.bat new file mode 100755 index 000000000..247e997b6 --- /dev/null +++ b/startup/win32.bat @@ -0,0 +1,100 @@ +@ECHO OFF +REM ************************************************************************* +REM Copyright (c) 2002 The University of Chicago, as Operator of Argonne +REM National Laboratory. +REM Copyright (c) 2002 The Regents of the University of California, as +REM Operator of Los Alamos National Laboratory. +REM EPICS BASE Versions 3.13.7 +REM and higher are distributed subject to a Software License Agreement found +REM in file LICENSE that is included with this distribution. +REM ************************************************************************* +REM Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd +REM +REM Site-specific EPICS environment settings +REM +REM sites should modify these definitions + + +REM =================================================== +REM ====== REQUIRED ENVIRONMENT VARIABLES FOLLOW ====== +REM +REM --------------- WINDOWS --------------------------- +REM ----- WIN95 ----- +REM set PATH=C:\WINDOWS;C:\WINDOWS\COMMAND +REM ----- WINNT ----- +REM set PATH=C:\WINNT;C:\WINNT\SYSTEM32 +REM ----- WINXP ----- +set PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\system32\Wbem + +REM --------------- GNU tools ------------------------- +REM -- cygwin contains GNU make, perl, tk/tcl, vim, ... +REM -- Can be preceeded or replaced with paths to GNU make and perl +REM -- need grep from here NOT from cvs directory +REM -- some tools may need a tmp directory +set PATH=%PATH%;c:\cygwin\bin + +REM --------------- Visual c++ ------------------------ +REM ---- Visual c++ 6.0 ------ +REM call "C:\Program files\Microsoft Visual Studio\Vc98\bin\vcvars32.bat" +REM ---- Visual Studio .NET 2003 ------ +REM call "C:\Program files\Microsoft Visual Studio .NET 2003\Vc7\bin\vcvars32.bat" +REM ---- Visual Studio 2005 ----- +REM call "C:\Program files\Microsoft Visual Studio 8\VC\bin\vcvars32.bat" +REM ---- Visual Studio 2008 ----- +call "C:\Program files\Microsoft Visual Studio 9.0\VC\bin\vcvars32.bat" + + +REM --------------- EPICS ----------------------------- +REM -- R3.14 requirements +set EPICS_HOST_ARCH=win32-x86 +set PATH=%PATH%;G:\epics\base\bin\%EPICS_HOST_ARCH% +set PATH=%PATH%;G:\epics\extensions\bin\%EPICS_HOST_ARCH% + +REM =================================================== +REM ====== OPTIONAL ENVIRONMENT VARIABLES FOLLOW ====== + +REM ---------------- EPICS tools ---------------------- +REM -- HOST_ARCH needed for Makefile.Host builds -- +set HOST_ARCH=WIN32 + +REM --------------- GNU make flags -------------------- +REM set MAKEFLAGS=-w + +REM --------------- EPICS Channel Access -------------- +REM -- Uncomment and modify the following lines +REM -- to override the base/configure/CONFIG_ENV defaults +REM set EPICS_CA_ADDR_LIST=n.n.n.n n.n.n.n +REM set EPICS_CA_AUTO_ADDR_LIST=YES + +REM --------------- cygwin vim ------------------------ +REM -- HOME needed by vim to find _vimrc file. +REM set HOME=/home/%USERNAME% +REM -- VIM needed by vim to find help files. +REM set VIM=/usr/share/vim/vim61 + +REM --------------- remote cvs (use cygwin cvs) ------- +REM -- HOME needed by cvs for .cvsrc file (set in vim above) +REM set CVSROOT=:ext:%USERNAME%@venus.aps.anl.gov:/usr/local/epicsmgr/cvsroot +REM set CVS_RSH=/bin/ssh.exe + +REM --------------- JAVA ------------------------------ +REM -- Needed for java extensions +REM set PATH=%PATH%;C:\j2sdk1.4.1_01\bin +REM set CLASSPATH=G:\epics\extensions\javalib + +REM --------------- X11+Motif-------------------------- +REM -- Exceed or cygwin Xfree86 needed for Xwindows extensions +REM +REM -- Exceed ( Cygwin should preceed Exceed in path) +REM set PATH=%PATH%;C:\Exceed +REM ------ Exceed 2007 ------ +REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\12.00\Exceed\ +REM ------ Exceed 2008 ------ +REM set PATH=%PATH%;C:\Program Files\Hummingbird\Connectivity\13.00\Exceed\ +REM -- +REM -- or +REM ----- cygwin Xfree86 ----- +REM set PATH=%PATH%;c:\cygwin\usr\X11R6\bin +REM set DISPLAY=localhost:0 + +REM ===================================================